audiod: Allow to specify usernames in --user-allow.
authorAndre Noll <maan@tuebingen.mpg.de>
Mon, 19 Jan 2015 23:25:18 +0000 (00:25 +0100)
committerAndre Noll <maan@tuebingen.mpg.de>
Tue, 31 Mar 2015 19:55:53 +0000 (19:55 +0000)
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
audiod.h
audiod_command.c
m4/gengetopt/audiod.m4

index 8f246ba..3fe654a 100644 (file)
--- a/audiod.c
+++ b/audiod.c
@@ -14,6 +14,7 @@
 #include <sys/un.h>
 #include <netdb.h>
 #include <signal.h>
+#include <pwd.h>
 
 #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);
 }
 
 /*
index c86b665..21e34e6 100644 (file)
--- 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);
index 7e68d2d..5c7d2d5 100644 (file)
@@ -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);
index 2e6f936..732f808 100644 (file)
@@ -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" -