From cb193b91d68778125739fe129d49f3c5f4abf999 Mon Sep 17 00:00:00 2001 From: Andre Noll Date: Tue, 20 Jan 2015 00:25:18 +0100 Subject: [PATCH] audiod: Allow to specify usernames in --user-allow. It's more convenient to specify usernames rather than the UIDs of the users who may execute an audiod command. This patch allows to do so. For backwards compatibility we still need to accept numerical UIDs though. On startup we first try to convert each given --user-allow argument to a number and regard this number as a UID. Only if the conversion fails, we translate the argument to a username with getpwnam(). In order to not perform this conversion on each command, we allocate a UID whitelist at startup and populate it with the UIDs derived from both types of --user allow arguments. A pointer to the whitelist is passed as an additional argument to handle_connect(). The documentation is updated to reflect this change. --- audiod.c | 31 ++++++++++++++++++++++++++++--- audiod.h | 2 +- audiod_command.c | 9 +++++---- m4/gengetopt/audiod.m4 | 25 +++++++++++++------------ 4 files changed, 47 insertions(+), 20 deletions(-) diff --git a/audiod.c b/audiod.c index 8f246ba5..3fe654a7 100644 --- a/audiod.c +++ b/audiod.c @@ -14,6 +14,7 @@ #include #include #include +#include #include "para.h" #include "error.h" @@ -148,6 +149,8 @@ static struct signal_task *signal_task; static struct status_task status_task_struct; +static uid_t *uid_whitelist; + /** * the task that calls the status command of para_server * @@ -320,7 +323,7 @@ empty: static void parse_config_or_die(void) { - int ret; + int ret, i; char *config_file; struct audiod_cmdline_parser_params params = { .override = 0, @@ -340,6 +343,7 @@ static void parse_config_or_die(void) ret = file_exists(config_file); if (conf.config_file_given && !ret) { PARA_EMERG_LOG("can not read config file %s\n", config_file); + free(config_file); goto err; } if (ret) { @@ -347,9 +351,29 @@ static void parse_config_or_die(void) daemon_set_loglevel(conf.loglevel_arg); } free(config_file); + if (conf.user_allow_given > 0) { + uid_whitelist = para_malloc(conf.user_allow_given + * sizeof(uid_t)); + for (i = 0; i < conf.user_allow_given; i++) { + int32_t val; + struct passwd *pw; + ret = para_atoi32(conf.user_allow_arg[i], &val); + if (ret >= 0) { + uid_whitelist[i] = val; + continue; + } + errno = 0; /* see getpwnam(3) */ + pw = getpwnam(conf.user_allow_arg[i]); + if (!pw) { + PARA_EMERG_LOG("invalid username: %s\n", + conf.user_allow_arg[i]); + goto err; + } + uid_whitelist[i] = pw->pw_uid; + } + } return; err: - free(config_file); exit(EXIT_FAILURE); } @@ -1029,7 +1053,7 @@ static int command_post_select(struct sched *s, void *context) for (i = 0; i < 2; i++) { if (ct->fd[i] < 0) continue; - ret = handle_connect(ct->fd[i], &s->rfds); + ret = handle_connect(ct->fd[i], &s->rfds, uid_whitelist); if (ret < 0) { PARA_ERROR_LOG("%s\n", para_strerror(-ret)); if (ret == -E_AUDIOD_TERM) { @@ -1156,6 +1180,7 @@ static void audiod_cleanup(void) close_unused_slots(); audiod_cmdline_parser_free(&conf); close_stat_clients(); + free(uid_whitelist); } /* diff --git a/audiod.h b/audiod.h index c86b6659..21e34e65 100644 --- a/audiod.h +++ b/audiod.h @@ -66,7 +66,7 @@ extern struct slot_info slot[MAX_STREAM_SLOTS]; extern struct audiod_args_info conf; extern int audiod_status; -int handle_connect(int accept_fd, fd_set *rfds); +int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist); void audiod_status_dump(bool force); char *get_time_string(int slot_num); struct btr_node *audiod_get_btr_root(void); diff --git a/audiod_command.c b/audiod_command.c index 7e68d2d2..5c7d2d53 100644 --- a/audiod_command.c +++ b/audiod_command.c @@ -436,14 +436,14 @@ static int com_version(int fd, int argc, char **argv) return ret; } -static int check_perms(uid_t uid) +static int check_perms(uid_t uid, uid_t *whitelist) { int i; if (!conf.user_allow_given) return 1; for (i = 0; i < conf.user_allow_given; i++) - if (uid == conf.user_allow_arg[i]) + if (uid == whitelist[i]) return 1; return -E_UCRED_PERM; } @@ -453,6 +453,7 @@ static int check_perms(uid_t uid) * * \param accept_fd The fd to accept connections on. * \param rfds If \a accept_fd is not set in \a rfds, do nothing. + * \param uid_whitelist Array of UIDs which are allowed to connect. * * This is called in each iteration of the select loop. If there is an incoming * connection on \a accept_fd, this function reads the command sent by the peer, @@ -465,7 +466,7 @@ static int check_perms(uid_t uid) * * \sa para_accept(), recv_cred_buffer() * */ -int handle_connect(int accept_fd, fd_set *rfds) +int handle_connect(int accept_fd, fd_set *rfds, uid_t *uid_whitelist) { int i, argc, ret, clifd; char buf[MAXLINE], **argv = NULL; @@ -480,7 +481,7 @@ int handle_connect(int accept_fd, fd_set *rfds) goto out; uid = ret; PARA_INFO_LOG("connection from user %i, buf: %s\n", ret, buf); - ret = check_perms(uid); + ret = check_perms(uid, uid_whitelist); if (ret < 0) goto out; ret = create_argv(buf, "\n", &argv); diff --git a/m4/gengetopt/audiod.m4 b/m4/gengetopt/audiod.m4 index 2e6f936f..732f8087 100644 --- a/m4/gengetopt/audiod.m4 +++ b/m4/gengetopt/audiod.m4 @@ -82,20 +82,21 @@ details=" option "user-allow" - #~~~~~~~~~~~~~~~~~~~~ -"allow this uid" -int typestr="uid" -default="-1" +"allow this user to connect to audiod" +string typestr = "username" optional multiple -details=" - Allow the user identified by \"uid\" to connect to para_audiod. - May be specified multiple times. If not specified at all, - all users are allowed to connect. - - This feature requires unix socket credentials and is currently - only supported on Linux systems. On other operating systems, - the option is silently ignored and all local users are allowed - to connect to para_audiod. +details = " + Allow the user identified by username (either a string or + a UID) to connect to para_audiod. This option may be given + multiple times. If not specified at all, all users are allowed + to connect. + + This feature is based on the ability to send unix + credentials through local sockets using ancillary data + (SCM_CREDENTIALS). Currently it only works on Linux. On + other operating systems the option is silently ignored and + all local users are allowed to connect. " option "clock-diff-count" - -- 2.39.2