From 9c87a65ce91c38b60c19ae108a51a2e8ce46a85c Mon Sep 17 00:00:00 2001 From: Lennart Poettering Date: Wed, 19 Jul 2006 17:44:19 +0000 Subject: * add new --system command line parameter to the daemon for running PulseAudio as system-wide instance * add PA_ prefixes to all global #defines * modify auth-by-creds: define a new group "pulse-access" which is used for authentication * add proper privilige dropping when running in --system mode * create runtime directory once on startup and not by each module seperately git-svn-id: file:///home/lennart/svn/public/pulseaudio/trunk@1105 fefdeb5f-60dc-0310-8127-8f9354f1896f --- src/daemon/cmdline.c | 12 ++++- src/daemon/daemon-conf.c | 22 +++----- src/daemon/daemon-conf.h | 3 +- src/daemon/daemon.conf.in | 6 +++ src/daemon/main.c | 129 ++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 151 insertions(+), 21 deletions(-) (limited to 'src/daemon') diff --git a/src/daemon/cmdline.c b/src/daemon/cmdline.c index a106dc09..ab876edf 100644 --- a/src/daemon/cmdline.c +++ b/src/daemon/cmdline.c @@ -58,7 +58,8 @@ enum { ARG_RESAMPLE_METHOD, ARG_KILL, ARG_USE_PID_FILE, - ARG_CHECK + ARG_CHECK, + ARG_SYSTEM }; /* Tabel for getopt_long() */ @@ -84,6 +85,7 @@ static struct option long_options[] = { {"kill", 0, 0, ARG_KILL}, {"use-pid-file", 2, 0, ARG_USE_PID_FILE}, {"check", 0, 0, ARG_CHECK}, + {"system", 2, 0, ARG_SYSTEM}, {NULL, 0, 0, 0} }; @@ -105,6 +107,7 @@ void pa_cmdline_help(const char *argv0) { " --check Check for a running daemon\n\n" "OPTIONS:\n" + " --system[=BOOL] Run as system-wide instance\n" " -D, --daemonize[=BOOL] Daemonize after startup\n" " --fail[=BOOL] Quit when startup fails\n" " --high-priority[=BOOL] Try to set high process priority\n" @@ -276,6 +279,13 @@ int pa_cmdline_parse(pa_daemon_conf *conf, int argc, char *const argv [], int *d goto fail; } break; + + case ARG_SYSTEM: + if ((conf->system_instance = optarg ? pa_parse_boolean(optarg) : 1) < 0) { + pa_log(__FILE__": --system expects boolean argument"); + goto fail; + } + break; default: goto fail; diff --git a/src/daemon/daemon-conf.c b/src/daemon/daemon-conf.c index d1afed7b..a5a62567 100644 --- a/src/daemon/daemon-conf.c +++ b/src/daemon/daemon-conf.c @@ -39,23 +39,15 @@ #include "daemon-conf.h" -#ifndef DEFAULT_CONFIG_DIR -# ifndef OS_IS_WIN32 -# define DEFAULT_CONFIG_DIR "/etc/pulse" -# else -# define DEFAULT_CONFIG_DIR "%PULSE_ROOT%" -# endif -#endif - #ifndef OS_IS_WIN32 # define PATH_SEP "/" #else # define PATH_SEP "\\" #endif -#define DEFAULT_SCRIPT_FILE DEFAULT_CONFIG_DIR PATH_SEP "default.pa" +#define DEFAULT_SCRIPT_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "default.pa" #define DEFAULT_SCRIPT_FILE_USER ".pulse" PATH_SEP "default.pa" -#define DEFAULT_CONFIG_FILE DEFAULT_CONFIG_DIR PATH_SEP "daemon.conf" +#define DEFAULT_CONFIG_FILE PA_DEFAULT_CONFIG_DIR PATH_SEP "daemon.conf" #define DEFAULT_CONFIG_FILE_USER ".pulse" PATH_SEP "daemon.conf" #define ENV_SCRIPT_FILE "PULSE_SCRIPT" @@ -79,7 +71,8 @@ static const pa_daemon_conf default_conf = { .log_level = PA_LOG_NOTICE, .resample_method = PA_RESAMPLER_SRC_SINC_FASTEST, .config_file = NULL, - .use_pid_file = 1 + .use_pid_file = 1, + .system_instance = 0 }; pa_daemon_conf* pa_daemon_conf_new(void) { @@ -89,9 +82,7 @@ pa_daemon_conf* pa_daemon_conf_new(void) { if ((f = pa_open_config_file(DEFAULT_SCRIPT_FILE, DEFAULT_SCRIPT_FILE_USER, ENV_SCRIPT_FILE, &c->default_script_file, "r"))) fclose(f); -#ifdef DLSEARCHPATH - c->dl_search_path = pa_xstrdup(DLSEARCHPATH); -#endif + c->dl_search_path = pa_xstrdup(PA_DLSEARCHPATH); return c; } @@ -212,6 +203,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { { "verbose", parse_log_level, NULL }, { "resample-method", parse_resample_method, NULL }, { "use-pid-file", pa_config_parse_bool, NULL }, + { "system-instance", pa_config_parse_bool, NULL }, { NULL, NULL, NULL }, }; @@ -229,6 +221,7 @@ int pa_daemon_conf_load(pa_daemon_conf *c, const char *filename) { table[11].data = c; table[12].data = c; table[13].data = &c->use_pid_file; + table[14].data = &c->system_instance; pa_xfree(c->config_file); c->config_file = NULL; @@ -295,6 +288,7 @@ char *pa_daemon_conf_dump(pa_daemon_conf *c) { pa_strbuf_printf(s, "log-level = %s\n", log_level_to_string[c->log_level]); pa_strbuf_printf(s, "resample-method = %s\n", pa_resample_method_to_string(c->resample_method)); pa_strbuf_printf(s, "use-pid-file = %i\n", c->use_pid_file); + pa_strbuf_printf(s, "system-instance = %i\n", !!c->system_instance); return pa_strbuf_tostring_free(s); } diff --git a/src/daemon/daemon-conf.h b/src/daemon/daemon-conf.h index c325495c..bfea7358 100644 --- a/src/daemon/daemon-conf.h +++ b/src/daemon/daemon-conf.h @@ -46,7 +46,8 @@ typedef struct pa_daemon_conf { module_idle_time, scache_idle_time, auto_log_target, - use_pid_file; + use_pid_file, + system_instance; char *script_commands, *dl_search_path, *default_script_file; pa_log_target_t log_target; pa_log_level_t log_level; diff --git a/src/daemon/daemon.conf.in b/src/daemon/daemon.conf.in index 30bf3ca1..6e55c0ee 100644 --- a/src/daemon/daemon.conf.in +++ b/src/daemon/daemon.conf.in @@ -46,6 +46,9 @@ ## Unload autoloaded modules after being idle for this time ; module-idle-time = 20 +## Unload autoloaded sample cache entries after being idle for this time +; scache-idle-time = 20 + ## The path were to look for dynamic shared objects (DSOs aka ## plugins). You may specify more than one path seperated by ## colons. @@ -75,3 +78,6 @@ ## process per user, you better disable this option since it ## effectively disables multiple instances. ; use-pid-file = 1 + +## Run the daemon as system-wide instance, requires root priviliges +; system-instance = 0 diff --git a/src/daemon/main.c b/src/daemon/main.c index c41c69df..4961f0ca 100644 --- a/src/daemon/main.c +++ b/src/daemon/main.c @@ -37,6 +37,9 @@ #include #include #include +#include +#include + #include #ifdef HAVE_SYS_IOCTL_H @@ -149,6 +152,104 @@ static void close_pipe(int p[2]) { p[0] = p[1] = -1; } +#define set_env(key, value) putenv(pa_sprintf_malloc("%s=%s", (key), (value))) + +static int change_user(void) { + struct passwd *pw; + struct group * gr; + int r; + + if (!(pw = getpwnam(PA_SYSTEM_USER))) { + pa_log(__FILE__": Failed to find user '%s'.", PA_SYSTEM_USER); + return -1; + } + + if (!(gr = getgrnam(PA_SYSTEM_GROUP))) { + pa_log(__FILE__": Failed to find group '%s'.", PA_SYSTEM_GROUP); + return -1; + } + + pa_log_info(__FILE__": Found user '%s' (UID %lu) and group '%s' (GID %lu).", + PA_SYSTEM_USER, (unsigned long) pw->pw_uid, + PA_SYSTEM_GROUP, (unsigned long) gr->gr_gid); + + if (pw->pw_gid != gr->gr_gid) { + pa_log(__FILE__": GID of user '%s' and of group '%s' don't match.", PA_SYSTEM_USER, PA_SYSTEM_GROUP); + return -1; + } + + if (strcmp(pw->pw_dir, PA_SYSTEM_RUNTIME_PATH) != 0) + pa_log_warn(__FILE__": Warning: home directory of user '%s' is not '%s', ignoring.", PA_SYSTEM_USER, PA_SYSTEM_RUNTIME_PATH); + + if (pa_make_secure_dir(PA_SYSTEM_RUNTIME_PATH, 0755, pw->pw_uid, gr->gr_gid) < 0) { + pa_log(__FILE__": Failed to create '%s': %s", PA_SYSTEM_RUNTIME_PATH, pa_cstrerror(errno)); + return -1; + } + + if (initgroups(PA_SYSTEM_USER, gr->gr_gid) != 0) { + pa_log(__FILE__": Failed to change group list: %s", pa_cstrerror(errno)); + return -1; + } + +#if defined(HAVE_SETRESGID) + r = setresgid(gr->gr_gid, gr->gr_gid, gr->gr_gid); +#elif defined(HAVE_SETEGID) + if ((r = setgid(gr->gr_gid)) >= 0) + r = setegid(gr->gr_gid); +#elif defined(HAVE_SETREGID) + r = setregid(gr->gr_gid, gr->gr_gid); +#else +#error "No API to drop priviliges" +#endif + + if (r < 0) { + pa_log(__FILE__": Failed to change GID: %s", pa_cstrerror(errno)); + return -1; + } + +#if defined(HAVE_SETRESUID) + r = setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid); +#elif defined(HAVE_SETEUID) + if ((r = setuid(pw->pw_uid)) >= 0) + r = seteuid(pw->pw_uid); +#elif defined(HAVE_SETREUID) + r = setreuid(pw->pw_uid, pw->pw_uid); +#else +#error "No API to drop priviliges" +#endif + + if (r < 0) { + pa_log(__FILE__": Failed to change UID: %s", pa_cstrerror(errno)); + return -1; + } + + set_env("USER", PA_SYSTEM_USER); + set_env("LOGNAME", PA_SYSTEM_GROUP); + set_env("HOME", PA_SYSTEM_RUNTIME_PATH); + + /* Relevant for pa_runtime_path() */ + set_env("PULSE_RUNTIME_PATH", PA_SYSTEM_RUNTIME_PATH); + + pa_log_info(__FILE__": Successfully dropped root privileges."); + + return 0; +} + +static int create_runtime_dir(void) { + char fn[PATH_MAX]; + + pa_runtime_path(NULL, fn, sizeof(fn)); + + if (pa_make_secure_dir(fn, 0700, getuid(), getgid()) < 0) { + pa_log(__FILE__": Failed to create '%s': %s", fn, pa_cstrerror(errno)); + return -1; + } + + /* Relevant for pa_runtime_path() later on */ + set_env("PULSE_RUNTIME_PATH", fn); + return 0; +} + int main(int argc, char *argv[]) { pa_core *c; pa_strbuf *buf = NULL; @@ -172,13 +273,14 @@ int main(int argc, char *argv[]) { setlocale(LC_ALL, ""); - pa_limit_caps(); + if (getuid() != 0) + pa_limit_caps(); #ifdef HAVE_GETUID suid_root = getuid() != 0 && geteuid() == 0; - if (suid_root && (pa_own_uid_in_group("realtime", &gid) <= 0 || gid >= 1000)) { - pa_log_warn(__FILE__": WARNING: called SUID root, but not in group 'realtime'."); + if (suid_root && (pa_own_uid_in_group(PA_REALTIME_GROUP, &gid) <= 0 || gid >= 1000)) { + pa_log_warn(__FILE__": WARNING: called SUID root, but not in group '"PA_REALTIME_GROUP"'."); pa_drop_root(); } #else @@ -220,7 +322,8 @@ int main(int argc, char *argv[]) { if (conf->high_priority && conf->cmd == PA_CMD_DAEMON) pa_raise_priority(); - pa_drop_caps(); + if (getuid() != 0) + pa_drop_caps(); if (suid_root) pa_drop_root(); @@ -278,6 +381,14 @@ int main(int argc, char *argv[]) { assert(conf->cmd == PA_CMD_DAEMON); } + if (getuid() == 0 && !conf->system_instance) { + pa_log(__FILE__": This program is not intended to be run as root (unless --system is specified)."); + goto finish; + } else if (getuid() != 0 && conf->system_instance) { + pa_log(__FILE__": Root priviliges required."); + goto finish; + } + if (conf->daemonize) { pid_t child; int tty_fd; @@ -362,6 +473,13 @@ int main(int argc, char *argv[]) { } chdir("/"); + umask(0022); + + if (conf->system_instance) { + if (change_user() < 0) + goto finish; + } else if (create_runtime_dir() < 0) + goto finish; if (conf->use_pid_file) { if (pa_pid_file_create() < 0) { @@ -379,12 +497,13 @@ int main(int argc, char *argv[]) { #ifdef SIGPIPE signal(SIGPIPE, SIG_IGN); #endif - + mainloop = pa_mainloop_new(); assert(mainloop); c = pa_core_new(pa_mainloop_get_api(mainloop)); assert(c); + c->is_system_instance = !!conf->system_instance; r = pa_signal_init(pa_mainloop_get_api(mainloop)); assert(r == 0); -- cgit