diff --git a/conf.c b/conf.c index 0fe5266..545f61d 100644 --- a/conf.c +++ b/conf.c @@ -747,8 +747,8 @@ static void usage(const char *name) info( " default: run in background if started from a TTY"); info( " -e, --stderr Log to stderr too"); info( " default: log to system logger only if started from a TTY"); - info( " --runas UID|UID:GID Use given UID, GID if started as root"); - info( " UID and GID can be numeric, or login and group names"); + info( " --runas UID|UID:GID Run as given UID, GID, which can be"); + info( " numeric, or login and group names"); info( " default: drop to user \"nobody\""); info( " -h, --help Display this help message and exit"); @@ -1500,6 +1500,7 @@ void conf(struct ctx *c, int argc, char **argv) } while (name != -1); check_root(&uid, &gid); + drop_root(uid, gid); if (c->mode == MODE_PASTA) { if (*netns && optind != argc) { diff --git a/passt.1 b/passt.1 index 61f0e4c..88cc879 100644 --- a/passt.1 +++ b/passt.1 @@ -104,9 +104,10 @@ terminal, and to both system logger and standard error otherwise. .TP .BR \-\-runas " " \fIUID\fR|\fIUID:GID\fR|\fILOGIN\fR|\fILOGIN:GROUP\fR -If started as root, change to given UID and corresponding group if UID is given, +Attempt to change to given UID and corresponding group if UID is given, or to given UID and given GID if both are given. Alternatively, login name, or -login name and group name can be passed. +login name and group name can be passed. This requires privileges (either +initial effective UID 0 or CAP_SETUID capability) to work. Default is to change to user \fInobody\fR if started as root. .TP diff --git a/util.c b/util.c index b2ccb3d..eb25c37 100644 --- a/util.c +++ b/util.c @@ -492,7 +492,13 @@ void check_root(uid_t *uid, gid_t *gid) char buf[BUFSIZ]; int fd; - if (getuid() && geteuid()) + if (!*uid) + *uid = geteuid(); + + if (!*gid) + *gid = getegid(); + + if (*uid) return; if ((fd = open("/proc/self/uid_map", O_RDONLY | O_CLOEXEC)) < 0) @@ -524,11 +530,28 @@ void check_root(uid_t *uid, gid_t *gid) *uid = *gid = 65534; #endif } +} - if (!setgroups(0, NULL) && !setgid(*gid) && !setuid(*uid)) +/** + * drop_root() - Switch to given UID and GID + * @uid: User ID to switch to + * @gid: Group ID to switch to + */ +void drop_root(uid_t uid, gid_t gid) +{ + if (setgroups(0, NULL)) { + /* If we don't start with CAP_SETGID, this will EPERM */ + if (errno != EPERM) { + err("Can't drop supplementary groups: %s", + strerror(errno)); + exit(EXIT_FAILURE); + } + } + + if (!setgid(gid) && !setuid(uid)) return; - fprintf(stderr, "Can't change user/group, exiting"); + err("Can't change user/group, exiting"); exit(EXIT_FAILURE); } diff --git a/util.h b/util.h index 58312fb..e2f686b 100644 --- a/util.h +++ b/util.h @@ -235,6 +235,7 @@ void procfs_scan_listen(struct ctx *c, uint8_t proto, int ip_version, int ns, uint8_t *map, uint8_t *exclude); void drop_caps(void); void check_root(uid_t *uid, gid_t *gid); +void drop_root(uid_t uid, gid_t gid); int ns_enter(const struct ctx *c); void write_pidfile(int fd, pid_t pid); int __daemon(int pidfile_fd, int devnull_fd);