diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2015-12-20 11:38:34 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2015-12-20 11:38:34 +0000 |
commit | 382f92a63dd063a4776a31ea29a9926e53ba543a (patch) | |
tree | d162701a97f48a1ef493a7042723df872b61bc86 /usr.bin/sndiod/sndiod.c | |
parent | 632ffaf9f58a5970fb1cc89cb89d71e652917191 (diff) |
In case of a bug in sndiod, an attacker (a local user) could run
arbitrary code as user _sndio, i.e. get a second uid.
Mitigate the risk by implementing initial privilege separation as
follows. Break sndiod in two processes: a chroot()ed "worker" process
processing input, and a non-chroot()ed "helper" process opening
devices and passing descriptors to the worker.
With help from benno, claudio, semarie and gilles.
ok benno, semarie and tb
Diffstat (limited to 'usr.bin/sndiod/sndiod.c')
-rw-r--r-- | usr.bin/sndiod/sndiod.c | 160 |
1 files changed, 107 insertions, 53 deletions
diff --git a/usr.bin/sndiod/sndiod.c b/usr.bin/sndiod/sndiod.c index ad873912375..51eb5cee031 100644 --- a/usr.bin/sndiod/sndiod.c +++ b/usr.bin/sndiod/sndiod.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sndiod.c,v 1.17 2015/11/26 12:35:37 ratchov Exp $ */ +/* $OpenBSD: sndiod.c,v 1.18 2015/12/20 11:38:33 ratchov Exp $ */ /* * Copyright (c) 2008-2012 Alexandre Ratchov <alex@caoua.org> * @@ -18,6 +18,7 @@ #include <sys/stat.h> #include <sys/types.h> #include <sys/resource.h> +#include <sys/socket.h> #include <err.h> #include <errno.h> @@ -35,6 +36,7 @@ #include "amsg.h" #include "defs.h" #include "dev.h" +#include "fdpass.h" #include "file.h" #include "listen.h" #include "midi.h" @@ -50,6 +52,13 @@ #endif /* + * privileged user name + */ +#ifndef SNDIO_PRIV_USER +#define SNDIO_PRIV_USER "_sndiop" +#endif + +/* * priority when run as root */ #ifndef SNDIO_PRIO @@ -328,6 +337,8 @@ main(int argc, char **argv) struct port *p; struct listen *l; struct passwd *pw; + int s[2]; + pid_t pid; atexit(log_flush); @@ -449,69 +460,112 @@ main(int argc, char **argv) setsig(); filelist_init(); - getbasepath(base, sizeof(base)); - snprintf(path, SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", base, unit); - listen_new_un(path); - if (tcpaddr) { -#ifdef USE_TCP - listen_new_tcp(tcpaddr, AUCAT_PORT + unit); -#else - errx(1, "-L option disabled at compilation time"); -#endif - } - if (geteuid() == 0) { - if ((pw = getpwnam(SNDIO_USER)) == NULL) - errx(1, "unknown user %s", SNDIO_USER); - if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0) - err(1, "setpriority"); - if (setgroups(1, &pw->pw_gid) || - setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || - setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) - err(1, "cannot drop privileges"); + /* start subprocesses */ + + if (socketpair(AF_UNIX, SOCK_STREAM, 0, s) < 0) { + perror("socketpair"); + return 1; } - midi_init(); - for (p = port_list; p != NULL; p = p->next) { - if (!port_init(p)) - return 1; + pid = fork(); + if (pid == -1) { + log_puts("can't fork\n"); + return 1; } - for (d = dev_list; d != NULL; d = d->next) { - if (!dev_init(d)) + if (pid == 0) { + setproctitle("helper"); + close(s[0]); + if (fdpass_new(s[1], &helper_fileops) == NULL) return 1; - } - for (l = listen_list; l != NULL; l = l->next) { - if (!listen_init(l)) + if (background) { + log_flush(); + log_level = 0; + if (daemon(0, 0) < 0) + err(1, "daemon"); + } + if (geteuid() == 0) { + if ((pw = getpwnam(SNDIO_PRIV_USER)) == NULL) + errx(1, "unknown user %s", SNDIO_PRIV_USER); + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + err(1, "cannot drop privileges"); + } + while (file_poll()) + ; /* nothing */ + } else { + close(s[1]); + if (fdpass_new(s[0], &worker_fileops) == NULL) return 1; + + getbasepath(base, sizeof(base)); + snprintf(path, + SOCKPATH_MAX, "%s/" SOCKPATH_FILE "%u", + base, unit); + listen_new_un(path); + if (tcpaddr) + listen_new_tcp(tcpaddr, AUCAT_PORT + unit); + for (l = listen_list; l != NULL; l = l->next) { + if (!listen_init(l)) + return 1; + } + + midi_init(); + for (p = port_list; p != NULL; p = p->next) { + if (!port_init(p)) + return 1; + } + for (d = dev_list; d != NULL; d = d->next) { + if (!dev_init(d)) + return 1; + } + if (background) { + log_flush(); + log_level = 0; + if (daemon(0, 0) < 0) + err(1, "daemon"); + } + if (geteuid() == 0) { + if ((pw = getpwnam(SNDIO_USER)) == NULL) + errx(1, "unknown user %s", SNDIO_USER); + if (setpriority(PRIO_PROCESS, 0, SNDIO_PRIO) < 0) + err(1, "setpriority"); + if (chroot(pw->pw_dir) != 0 || chdir("/") != 0) + err(1, "cannot chroot to %s", pw->pw_dir); + if (setgroups(1, &pw->pw_gid) || + setresgid(pw->pw_gid, pw->pw_gid, pw->pw_gid) || + setresuid(pw->pw_uid, pw->pw_uid, pw->pw_uid)) + err(1, "cannot drop privileges"); + } + for (;;) { + if (quit_flag) + break; + if (!fdpass_peer) + break; + if (!file_poll()) + break; + } + if (fdpass_peer) + fdpass_close(fdpass_peer); + while (listen_list != NULL) + listen_close(listen_list); + while (sock_list != NULL) + sock_close(sock_list); + for (d = dev_list; d != NULL; d = d->next) + dev_done(d); + for (p = port_list; p != NULL; p = p->next) + port_done(p); + while (file_poll()) + ; /* nothing */ + midi_done(); + + rmdir(base); } - if (background) { - log_flush(); - log_level = 0; - if (daemon(0, 0) < 0) - err(1, "daemon"); - } - for (;;) { - if (quit_flag) - break; - if (!file_poll()) - break; - } - while (listen_list != NULL) - listen_close(listen_list); - while (sock_list != NULL) - sock_close(sock_list); while (opt_list != NULL) opt_del(opt_list); - for (d = dev_list; d != NULL; d = d->next) - dev_done(d); - for (p = port_list; p != NULL; p = p->next) - port_done(p); - midi_done(); - while (file_poll()) - ; /* nothing */ while (dev_list) dev_del(dev_list); while (port_list) port_del(port_list); - rmdir(base); filelist_done(); unsetsig(); return 0; |