diff --git a/sniproxy.conf b/sniproxy.conf index b3b99e93..d69ebd22 100644 --- a/sniproxy.conf +++ b/sniproxy.conf @@ -3,6 +3,7 @@ # lines with only white space are ignored user nobody +group nogroup # PID file, needs to be placed in directory writable by user pidfile /var/tmp/sniproxy.pid diff --git a/src/config.c b/src/config.c index 9d8de425..5bfed7f3 100644 --- a/src/config.c +++ b/src/config.c @@ -38,6 +38,7 @@ struct LoggerBuilder { }; static int accept_username(struct Config *, char *); +static int accept_groupname(struct Config *, char *); static int accept_pidfile(struct Config *, char *); static int end_listener_stanza(struct Config *, struct Listener *); static int end_table_stanza(struct Config *, struct Table *); @@ -146,6 +147,11 @@ static struct Keyword global_grammar[] = { (int(*)(void *, char *))accept_username, NULL, NULL}, + { "groupname", + NULL, + (int(*)(void *, char *))accept_groupname, + NULL, + NULL}, { "pidfile", NULL, (int(*)(void *, char *))accept_pidfile, @@ -198,6 +204,7 @@ init_config(const char *filename, struct ev_loop *loop) { config->filename = NULL; config->user = NULL; + config->group = NULL; config->pidfile = NULL; config->access_log = NULL; config->resolver.nameservers = NULL; @@ -253,6 +260,7 @@ void free_config(struct Config *config, struct ev_loop *loop) { free(config->filename); free(config->user); + free(config->group); free(config->pidfile); free_string_vector(config->resolver.nameservers); @@ -325,6 +333,17 @@ accept_username(struct Config *config, char *username) { return 1; } +static int +accept_groupname(struct Config *config, char *groupname) { + config->group = strdup(groupname); + if (config->group == NULL) { + err("%s: strdup", __func__); + return -1; + } + + return 1; +} + static int accept_pidfile(struct Config *config, char *pidfile) { config->pidfile = strdup(pidfile); diff --git a/src/config.h b/src/config.h index 64d19c2d..6333a3da 100644 --- a/src/config.h +++ b/src/config.h @@ -33,6 +33,7 @@ struct Config { char *filename; char *user; + char *group; char *pidfile; struct ResolverConfig { char **nameservers; diff --git a/src/sniproxy.c b/src/sniproxy.c index 9e53e0a4..c833c25a 100644 --- a/src/sniproxy.c +++ b/src/sniproxy.c @@ -50,7 +50,7 @@ static void usage(); static void daemonize(void); static void write_pidfile(const char *, pid_t); static void set_limits(int); -static void drop_perms(const char* username); +static void drop_perms(const char* username, const char* groupname); static void perror_exit(const char *); static void signal_cb(struct ev_loop *, struct ev_signal *, int revents); @@ -119,7 +119,7 @@ main(int argc, char **argv) { init_listeners(&config->listeners, &config->tables, EV_DEFAULT); /* Drop permissions only when we can */ - drop_perms(config->user ? config->user : default_username); + drop_perms(config->user ? config->user : default_username, config->group); ev_signal_init(&sighup_watcher, signal_cb, SIGHUP); ev_signal_init(&sigusr1_watcher, signal_cb, SIGUSR1); @@ -210,7 +210,7 @@ set_limits(int max_nofiles) { } static void -drop_perms(const char *username) { +drop_perms(const char *username, const char *groupname) { /* check if we are already an unprivileged user */ if (getuid() != 0) return; @@ -222,11 +222,25 @@ drop_perms(const char *username) { else if (user == NULL) fatal("getpwnam(): user %s does not exist", username); + gid_t gid = user->pw_gid; + + if (groupname != NULL) { + errno = 0; + struct group *group = getgrnam(groupname); + if (errno) + fatal("getgrnam(): %s", strerror(errno)); + else if (group == NULL) + fatal("getgrnam(): group %s does not exist", groupname); + + gid = group->gr_gid; + } + /* drop any supplementary groups */ - if (setgroups(1, &user->pw_gid) < 0) + if (setgroups(1, &gid) < 0) fatal("setgroups(): %s", strerror(errno)); - if (setgid(user->pw_gid) < 0) + /* set the main gid */ + if (setgid(gid) < 0) fatal("setgid(): %s", strerror(errno)); if (setuid(user->pw_uid) < 0)