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.
#include <sys/un.h>
#include <netdb.h>
#include <signal.h>
#include <sys/un.h>
#include <netdb.h>
#include <signal.h>
#include "para.h"
#include "error.h"
#include "para.h"
#include "error.h"
static struct status_task status_task_struct;
static struct status_task status_task_struct;
+static uid_t *uid_whitelist;
+
/**
* the task that calls the status command of para_server
*
/**
* the task that calls the status command of para_server
*
static void parse_config_or_die(void)
{
static void parse_config_or_die(void)
{
char *config_file;
struct audiod_cmdline_parser_params params = {
.override = 0,
char *config_file;
struct audiod_cmdline_parser_params params = {
.override = 0,
ret = file_exists(config_file);
if (conf.config_file_given && !ret) {
PARA_EMERG_LOG("can not read config file %s\n", config_file);
ret = file_exists(config_file);
if (conf.config_file_given && !ret) {
PARA_EMERG_LOG("can not read config file %s\n", config_file);
daemon_set_loglevel(conf.loglevel_arg);
}
free(config_file);
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;
+ }
+ }
for (i = 0; i < 2; i++) {
if (ct->fd[i] < 0)
continue;
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) {
if (ret < 0) {
PARA_ERROR_LOG("%s\n", para_strerror(-ret));
if (ret == -E_AUDIOD_TERM) {
close_unused_slots();
audiod_cmdline_parser_free(&conf);
close_stat_clients();
close_unused_slots();
audiod_cmdline_parser_free(&conf);
close_stat_clients();
extern struct audiod_args_info conf;
extern int audiod_status;
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);
void audiod_status_dump(bool force);
char *get_time_string(int slot_num);
struct btr_node *audiod_get_btr_root(void);
-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++)
{
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;
}
return 1;
return -E_UCRED_PERM;
}
*
* \param accept_fd The fd to accept connections on.
* \param rfds If \a accept_fd is not set in \a rfds, do nothing.
*
* \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,
*
* 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,
*
* \sa para_accept(), recv_cred_buffer()
* */
*
* \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;
{
int i, argc, ret, clifd;
char buf[MAXLINE], **argv = NULL;
goto out;
uid = ret;
PARA_INFO_LOG("connection from user %i, buf: %s\n", ret, buf);
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);
if (ret < 0)
goto out;
ret = create_argv(buf, "\n", &argv);
option "user-allow" -
#~~~~~~~~~~~~~~~~~~~~
option "user-allow" -
#~~~~~~~~~~~~~~~~~~~~
-"allow this uid"
-int typestr="uid"
-default="-1"
+"allow this user to connect to audiod"
+string typestr = "username"
-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" -
"
option "clock-diff-count" -