From 2955b20e9d541e2b0023c83bca44acbfceca7d7a Mon Sep 17 00:00:00 2001 From: badaix Date: Sat, 1 Jul 2017 11:15:37 +0200 Subject: [PATCH] check permissions for server.json --- client/Makefile | 10 +-- client/snapClient.cpp | 11 ++- common/daemon.cpp | 170 ++++++++++++++++++++++++++++++++++++++++++ common/daemon.h | 155 ++++---------------------------------- server/Makefile | 8 +- server/config.cpp | 22 +++++- server/snapServer.cpp | 17 +++-- 7 files changed, 230 insertions(+), 163 deletions(-) create mode 100644 common/daemon.cpp diff --git a/client/Makefile b/client/Makefile index 0f52f4da..907974c1 100644 --- a/client/Makefile +++ b/client/Makefile @@ -50,13 +50,13 @@ else ifeq ($(TARGET), OPENWRT) STRIP = echo CXXFLAGS += -pthread -DNO_CPP11_STRING -DHAS_TREMOR -DHAS_ALSA -DHAS_AVAHI -DHAS_DAEMON LDFLAGS = -lasound -lvorbisidec -logg -lFLAC -lavahi-client -lavahi-common -latomic -OBJ += player/alsaPlayer.o browseZeroConf/browseAvahi.o +OBJ += ../common/daemon.o player/alsaPlayer.o browseZeroConf/browseAvahi.o else ifeq ($(TARGET), BUILDROOT) CXXFLAGS += -pthread -DNO_CPP11_STRING -DHAS_TREMOR -DHAS_ALSA -DHAS_AVAHI -DHAS_DAEMON LDFLAGS += -lasound -lvorbisidec -logg -lFLAC -lavahi-client -lavahi-common -latomic -OBJ += player/alsaPlayer.o browseZeroConf/browseAvahi.o +OBJ += ../common/daemon.o player/alsaPlayer.o browseZeroConf/browseAvahi.o else ifeq ($(TARGET), MACOS) @@ -64,7 +64,7 @@ CXX = g++ STRIP = strip CXXFLAGS += -DHAS_OGG -DHAS_COREAUDIO -DFREEBSD -DMACOS -DHAS_BONJOUR -DHAS_DAEMON -I/usr/local/include -Wno-unused-local-typedef -Wno-deprecated LDFLAGS = -logg -lvorbis -lFLAC -L/usr/local/lib -framework AudioToolbox -framework CoreFoundation -framework IOKit -OBJ += player/coreAudioPlayer.o browseZeroConf/browseBonjour.o +OBJ += ../common/daemon.o player/coreAudioPlayer.o browseZeroConf/browseBonjour.o else @@ -72,7 +72,7 @@ CXX = g++ STRIP = strip CXXFLAGS += -pthread -DHAS_OGG -DHAS_ALSA -DHAS_AVAHI -DHAS_DAEMON LDFLAGS = -lrt -lasound -logg -lvorbis -lFLAC -lavahi-client -lavahi-common -static-libgcc -static-libstdc++ -OBJ += player/alsaPlayer.o browseZeroConf/browseAvahi.o +OBJ += ../common/daemon.o player/alsaPlayer.o browseZeroConf/browseAvahi.o endif @@ -171,7 +171,7 @@ installsysv: adduser: @if ! getent passwd snapclient >/dev/null; then \ - useradd --gid audio --system --home-dir /var/lib/snapclient snapclient; \ + useradd --gid audio --system snapclient; \ fi; \ ifeq ($(TARGET), MACOS) diff --git a/client/snapClient.cpp b/client/snapClient.cpp index ea7a9ff4..bad990da 100644 --- a/client/snapClient.cpp +++ b/client/snapClient.cpp @@ -159,9 +159,10 @@ int main (int argc, char **argv) signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); +#ifdef HAS_DAEMON + std::unique_ptr daemon; if (daemonOption.isSet()) { -#ifdef HAS_DAEMON string pidFile = "/var/run/snapclient/pid"; if (instance != 1) pidFile += "." + cpt::to_string(instance); @@ -178,7 +179,8 @@ int main (int argc, char **argv) if (user_group.size() > 1) group = user_group[1]; } - daemonize(user, group, pidFile); + daemon.reset(new Daemon(user, group, pidFile)); + daemon->daemonize(); if (processPriority < -20) processPriority = -20; else if (processPriority > 19) @@ -186,8 +188,8 @@ int main (int argc, char **argv) if (processPriority != 0) setpriority(PRIO_PROCESS, 0, processPriority); logS(kLogNotice) << "daemon started" << std::endl; -#endif } +#endif PcmDevice pcmDevice = getPcmDevice(soundcard); #if defined(HAS_ALSA) @@ -241,9 +243,6 @@ int main (int argc, char **argv) } logS(kLogNotice) << "daemon terminated." << endl; -#ifdef HAS_DAEMON - daemonShutdown(); -#endif exit(exitcode); } diff --git a/common/daemon.cpp b/common/daemon.cpp new file mode 100644 index 00000000..dc2c4b91 --- /dev/null +++ b/common/daemon.cpp @@ -0,0 +1,170 @@ +/*** + This file is part of snapcast + Copyright (C) 2014-2017 Johannes Pohl + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . +***/ + +#include "daemon.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "common/snapException.h" +#include "common/strCompat.h" +#include "common/utils.h" + + +Daemon::Daemon(const std::string& user, const std::string& group, const std::string& pidfile) : + pidFilehandle_(-1), + user_(user), + group_(group), + pidfile_(pidfile) +{ + if (pidfile.empty() || pidfile.find('/') == std::string::npos) + throw SnapException("invalid pid file \"" + pidfile + "\""); +} + + +Daemon::~Daemon() +{ + if (pidFilehandle_ != -1) + close(pidFilehandle_); +} + + +void Daemon::daemonize() +{ + std::string pidfileDir(pidfile_.substr(0, pidfile_.find_last_of('/'))); + mkdirRecursive(pidfileDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); + + /// Ensure only one copy + pidFilehandle_ = open(pidfile_.c_str(), O_RDWR|O_CREAT, 0644); + if (pidFilehandle_ == -1 ) + { + /// Couldn't open lock file + throw SnapException("Could not open PID lock file \"" + pidfile_ + "\""); + } + + uid_t user_uid = (uid_t)-1; + gid_t user_gid = (gid_t)-1; + std::string user_name; +#ifdef FREEBSD + bool had_group = false; +#endif + + if (!user_.empty()) + { + struct passwd *pwd = getpwnam(user_.c_str()); + if (pwd == nullptr) + throw SnapException("no such user \"" + user_ + "\""); + user_uid = pwd->pw_uid; + user_gid = pwd->pw_gid; + user_name = strdup(user_.c_str()); + /// this is needed by libs such as arts + setenv("HOME", pwd->pw_dir, true); + } + + if (!group_.empty()) + { + struct group *grp = getgrnam(group_.c_str()); + if (grp == nullptr) + throw SnapException("no such group \"" + group_ + "\""); + user_gid = grp->gr_gid; +#ifdef FREEBSD + had_group = true; +#endif + } + + if (chown(pidfile_.c_str(), user_uid, user_gid) == -1) + { + /// Couldn't open lock file + throw SnapException("Could not chown PID lock file \"" + pidfile_ + "\""); + } + + /// set gid + if (user_gid != (gid_t)-1 && user_gid != getgid() && setgid(user_gid) == -1) + throw SnapException("Failed to set group " + cpt::to_string((int)user_gid)); + +//#if defined(FREEBSD) && !defined(MACOS) +//#ifdef FREEBSD + /// init supplementary groups + /// (must be done before we change our uid) + /// no need to set the new user's supplementary groups if we are already this user +// if (!had_group && user_uid != getuid() && initgroups(user_name, user_gid) == -1) +// throw SnapException("Failed to set supplementary groups of user \"" + user + "\""); +//#endif + /// set uid + if (user_uid != (uid_t)-1 && user_uid != getuid() && setuid(user_uid) == -1) + throw SnapException("Failed to set user " + user_); + + /// Our process ID and Session ID + pid_t pid, sid; + + /// Fork off the parent process + pid = fork(); + if (pid < 0) + exit(EXIT_FAILURE); + + /// If we got a good PID, then we can exit the parent process. + if (pid > 0) + exit(EXIT_SUCCESS); + + /// Change the file mode mask + umask(0); + + /// Open any logs here + + /// Create a new SID for the child process + sid = setsid(); + if (sid < 0) + { + /// Log the failure + exit(EXIT_FAILURE); + } + + /// Change the current working directory + if ((chdir("/")) < 0) + { + /// Log the failure + exit(EXIT_FAILURE); + } + + /// Try to lock file + if (lockf(pidFilehandle_, F_TLOCK, 0) == -1) + throw SnapException("Could not lock PID lock file \"" + pidfile_ + "\""); + + char str[10]; + /// Get and format PID + sprintf(str, "%d\n", getpid()); + + /// write pid to lockfile + if (write(pidFilehandle_, str, strlen(str)) != (int)strlen(str)) + throw SnapException("Could not write PID to lock file \"" + pidfile_ + "\""); + + /// Close out the standard file descriptors + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); +} + + + diff --git a/common/daemon.h b/common/daemon.h index 04e4b4df..43fb7167 100644 --- a/common/daemon.h +++ b/common/daemon.h @@ -16,149 +16,26 @@ along with this program. If not, see . ***/ -#ifndef DAEMONIZE_H -#define DAEMONIZE_H +#ifndef DAEMON_H +#define DAEMON_H -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "common/snapException.h" -#include "common/strCompat.h" -#include "common/utils.h" +#include -int pidFilehandle; - -void daemonize(const std::string& user, const std::string& group, const std::string& pidfile) +class Daemon { - if (pidfile.empty() || pidfile.find('/') == std::string::npos) - throw SnapException("invalid pid file \"" + pidfile + "\""); +public: + Daemon(const std::string& user, const std::string& group, const std::string& pidfile); + virtual ~Daemon(); - std::string pidfileDir(pidfile.substr(0, pidfile.find_last_of('/'))); - mkdirRecursive(pidfileDir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); - - /// Ensure only one copy - pidFilehandle = open(pidfile.c_str(), O_RDWR|O_CREAT, 0644); - if (pidFilehandle == -1 ) - { - /// Couldn't open lock file - throw SnapException("Could not open PID lock file \"" + pidfile + "\""); - } - - uid_t user_uid = (uid_t)-1; - gid_t user_gid = (gid_t)-1; - std::string user_name; -#ifdef FREEBSD - bool had_group = false; -#endif - - if (!user.empty()) - { - struct passwd *pwd = getpwnam(user.c_str()); - if (pwd == nullptr) - throw SnapException("no such user \"" + user + "\""); - user_uid = pwd->pw_uid; - user_gid = pwd->pw_gid; - user_name = strdup(user.c_str()); - /// this is needed by libs such as arts - setenv("HOME", pwd->pw_dir, true); - } - - if (!group.empty()) { - struct group *grp = getgrnam(group.c_str()); - if (grp == nullptr) - throw SnapException("no such group \"" + group + "\""); - user_gid = grp->gr_gid; -#ifdef FREEBSD - had_group = true; -#endif - } - - if (chown(pidfile.c_str(), user_uid, user_gid) == -1) - { - /// Couldn't open lock file - throw SnapException("Could not chown PID lock file \"" + pidfile + "\""); - } - - /// set gid - if (user_gid != (gid_t)-1 && user_gid != getgid() && setgid(user_gid) == -1) - throw SnapException("Failed to set group " + cpt::to_string((int)user_gid)); - -//#if defined(FREEBSD) && !defined(MACOS) -//#ifdef FREEBSD - /// init supplementary groups - /// (must be done before we change our uid) - /// no need to set the new user's supplementary groups if we are already this user -// if (!had_group && user_uid != getuid() && initgroups(user_name, user_gid) == -1) -// throw SnapException("Failed to set supplementary groups of user \"" + user + "\""); -//#endif - /// set uid - if (user_uid != (uid_t)-1 && user_uid != getuid() && setuid(user_uid) == -1) - throw SnapException("Failed to set user " + user); - - /// Our process ID and Session ID - pid_t pid, sid; - - /// Fork off the parent process - pid = fork(); - if (pid < 0) - exit(EXIT_FAILURE); - - /// If we got a good PID, then we can exit the parent process. - if (pid > 0) - exit(EXIT_SUCCESS); - - /// Change the file mode mask - umask(0); - - /// Open any logs here - - /// Create a new SID for the child process - sid = setsid(); - if (sid < 0) - { - /// Log the failure - exit(EXIT_FAILURE); - } - - /// Change the current working directory - if ((chdir("/")) < 0) - { - /// Log the failure - exit(EXIT_FAILURE); - } - - /// Try to lock file - if (lockf(pidFilehandle, F_TLOCK, 0) == -1) - throw SnapException("Could not lock PID lock file \"" + pidfile + "\""); - - char str[10]; - /// Get and format PID - sprintf(str, "%d\n", getpid()); - - /// write pid to lockfile - if (write(pidFilehandle, str, strlen(str)) != (int)strlen(str)) - throw SnapException("Could not write PID to lock file \"" + pidfile + "\""); - - /// Close out the standard file descriptors - close(STDIN_FILENO); - close(STDOUT_FILENO); - close(STDERR_FILENO); -} - - -void daemonShutdown() -{ - close(pidFilehandle); -} - - -#endif + void daemonize(); + +private: + int pidFilehandle_; + std::string user_; + std::string group_; + std::string pidfile_; +}; +#endif // DAEMON_H diff --git a/server/Makefile b/server/Makefile index b53150a7..f10ab5b3 100644 --- a/server/Makefile +++ b/server/Makefile @@ -50,7 +50,7 @@ else ifeq ($(TARGET), OPENWRT) STRIP = echo CXXFLAGS += -DNO_CPP11_STRING -DHAS_AVAHI -DHAS_DAEMON -pthread LDFLAGS += -lavahi-client -lavahi-common -latomic -OBJ += publishZeroConf/publishAvahi.o +OBJ += ../common/daemon.o publishZeroConf/publishAvahi.o else ifeq ($(TARGET), FREEBSD) @@ -58,7 +58,7 @@ CXX = g++ STRIP = echo CXXFLAGS += -DNO_CPP11_STRING -DFREEBSD -DHAS_AVAHI -DHAS_DAEMON -pthread LDFLAGS += -lrt -lavahi-client -lavahi-common -static-libgcc -static-libstdc++ -OBJ += publishZeroConf/publishAvahi.o +OBJ += ../common/daemon.o publishZeroConf/publishAvahi.o else ifeq ($(TARGET), MACOS) @@ -66,7 +66,7 @@ CXX = g++ STRIP = strip CXXFLAGS += -DFREEBSD -DMACOS -DHAS_BONJOUR -DHAS_DAEMON -Wno-deprecated -I/usr/local/include LDFLAGS += -L/usr/local/lib -framework CoreFoundation -framework IOKit -OBJ += publishZeroConf/publishBonjour.o +OBJ += ../common/daemon.o publishZeroConf/publishBonjour.o else @@ -74,7 +74,7 @@ CXX = g++ STRIP = strip CXXFLAGS += -DHAS_AVAHI -DHAS_DAEMON -pthread LDFLAGS = -lrt -lvorbis -lvorbisenc -logg -lFLAC -lavahi-client -lavahi-common -static-libgcc -static-libstdc++ -OBJ += publishZeroConf/publishAvahi.o +OBJ += ../common/daemon.o publishZeroConf/publishAvahi.o endif diff --git a/server/config.cpp b/server/config.cpp index aa1686f6..e169c618 100644 --- a/server/config.cpp +++ b/server/config.cpp @@ -19,6 +19,7 @@ #include "config.h" #include #include +#include #include #include #include "common/snapException.h" @@ -34,13 +35,30 @@ Config::Config() if (getenv("HOME") == NULL) dir = "/var/lib/snapserver/"; else - dir = getenv("HOME") + string("/.config/snapserver/"); + dir = getenv("HOME"); + + if (!dir.empty() && (dir.back() != '/')) + dir += "/"; + + if (dir.find("/var/lib/snapserver") == string::npos) + dir += ".config/snapserver/"; + int status = mkdirRecursive(dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH); if ((status != 0) && (errno != EEXIST)) throw SnapException("failed to create settings directory: \"" + dir + "\": " + cpt::to_string(errno)); filename_ = dir + "server.json"; - logO << "Settings file: " << filename_ << "\n"; + logS(kLogNotice) << "Settings file: \"" << filename_ << "\"\n"; + + int fd; + if ((fd = open(filename_.c_str(), O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH)) == -1) + { + if (errno == EACCES) + throw std::runtime_error("failed to open file \"" + filename_ + "\", permission denied"); + else + throw std::runtime_error("failed to open file \"" + filename_ + "\""); + } + close(fd); try { diff --git a/server/snapServer.cpp b/server/snapServer.cpp index 050dd60e..30e5b601 100644 --- a/server/snapServer.cpp +++ b/server/snapServer.cpp @@ -51,6 +51,7 @@ int main(int argc, char* argv[]) #ifdef MACOS #pragma message "Warning: the macOS support is experimental and might not be maintained" #endif + int exitcode = EXIT_SUCCESS; try { @@ -140,16 +141,16 @@ int main(int argc, char* argv[]) return 1; } - Config::instance(); std::clog.rdbuf(new Log("snapserver", LOG_DAEMON)); signal(SIGHUP, signal_handler); signal(SIGTERM, signal_handler); signal(SIGINT, signal_handler); +#ifdef HAS_DAEMON + std::unique_ptr daemon; if (daemonOption.isSet()) { -#ifdef HAS_DAEMON string user = ""; string group = ""; @@ -163,7 +164,9 @@ int main(int argc, char* argv[]) if (user_group.size() > 1) group = user_group[1]; } - daemonize(user, group, "/var/run/snapserver/pid"); + + daemon.reset(new Daemon(user, group, "/var/run/snapserver/pid")); + daemon->daemonize(); if (processPriority < -20) processPriority = -20; else if (processPriority > 19) @@ -171,8 +174,11 @@ int main(int argc, char* argv[]) if (processPriority != 0) setpriority(PRIO_PROCESS, 0, processPriority); logS(kLogNotice) << "daemon started" << std::endl; -#endif } +#endif + + Config::instance(); + #if defined(HAS_AVAHI) || defined(HAS_BONJOUR) PublishZeroConf publishZeroConfg("Snapcast"); publishZeroConfg.publish({mDNSService("_snapcast._tcp", settings.port), mDNSService("_snapcast-jsonrpc._tcp", settings.controlPort)}); @@ -206,9 +212,6 @@ int main(int argc, char* argv[]) } logS(kLogNotice) << "daemon terminated." << endl; -#ifdef HAS_DAEMON - daemonShutdown(); -#endif exit(exitcode); }