diff options
author | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2010-04-22 17:43:31 +0000 |
---|---|---|
committer | Alexandre Ratchov <ratchov@cvs.openbsd.org> | 2010-04-22 17:43:31 +0000 |
commit | f5dd670f418fc5b9117869578ba44867a6a648e2 (patch) | |
tree | bebbc00ffdc0855fe060b4ae82f0e77f1998deba /usr.bin/aucat | |
parent | e153255799cfad37e6558a74e851726a9175cc77 (diff) |
Allow multiple users to share the same aucat server. If aucat is
run by root, it binds a shared address to the socket, cranks the
process priority and drops privileges. sio_open(3) will try to
connect to the private socket first (if any), then to the shared
socket. Only one user may have connections to aucat at a given
time.
based on discussions with henning, pyr and others
ok jacek, deraadt
Diffstat (limited to 'usr.bin/aucat')
-rw-r--r-- | usr.bin/aucat/aucat.1 | 14 | ||||
-rw-r--r-- | usr.bin/aucat/aucat.c | 55 | ||||
-rw-r--r-- | usr.bin/aucat/midicat.1 | 14 | ||||
-rw-r--r-- | usr.bin/aucat/sock.c | 39 |
4 files changed, 110 insertions, 12 deletions
diff --git a/usr.bin/aucat/aucat.1 b/usr.bin/aucat/aucat.1 index 8fe76090f04..dfb35e0467e 100644 --- a/usr.bin/aucat/aucat.1 +++ b/usr.bin/aucat/aucat.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: aucat.1,v 1.68 2010/04/21 06:13:07 ratchov Exp $ +.\" $OpenBSD: aucat.1,v 1.69 2010/04/22 17:43:30 ratchov Exp $ .\" .\" Copyright (c) 2006 Alexandre Ratchov <alex@caoua.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: April 21 2010 $ +.Dd $Mdocdate: April 22 2010 $ .Dt AUCAT 1 .Os .Sh NAME @@ -387,6 +387,16 @@ can be used in server mode to overcome hardware limitations and allow applications to run on fixed sample rate devices or on devices supporting only unusual encodings. +.Pp +The +.Nm +audio server may be started by the super-user, +in which case any user will be able to connect to it. +For privacy reasons, only one user may have connections to it +at a given time. +.Pp +Alternatively, each user may run his instance +of the server. It is generally not desirable to have multiple instances of .Nm diff --git a/usr.bin/aucat/aucat.c b/usr.bin/aucat/aucat.c index d713057cad6..89504bf5601 100644 --- a/usr.bin/aucat/aucat.c +++ b/usr.bin/aucat/aucat.c @@ -1,4 +1,4 @@ -/* $OpenBSD: aucat.c,v 1.85 2010/04/21 06:15:02 ratchov Exp $ */ +/* $OpenBSD: aucat.c,v 1.86 2010/04/22 17:43:30 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -18,11 +18,13 @@ #include <sys/queue.h> #include <sys/stat.h> #include <sys/types.h> +#include <sys/resource.h> #include <err.h> #include <errno.h> #include <fcntl.h> #include <limits.h> +#include <pwd.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> @@ -43,6 +45,16 @@ #include "dbg.h" #endif +/* + * unprivileged user name + */ +#define SNDIO_USER "_sndio" + +/* + * priority when run as root + */ +#define SNDIO_PRIO (-20) + #define PROG_AUCAT "aucat" #define PROG_MIDICAT "midicat" @@ -303,20 +315,47 @@ getbasepath(char *base, size_t size) { uid_t uid; struct stat sb; + mode_t mask; uid = geteuid(); - snprintf(base, PATH_MAX, "/tmp/aucat-%u", uid); - if (mkdir(base, 0700) < 0) { + if (uid == 0) { + mask = 022; + snprintf(base, PATH_MAX, "/tmp/aucat"); + } else { + mask = 077; + snprintf(base, PATH_MAX, "/tmp/aucat-%u", uid); + } + if (mkdir(base, 0777 & ~mask) < 0) { if (errno != EEXIST) err(1, "mkdir(\"%s\")", base); } if (stat(base, &sb) < 0) err(1, "stat(\"%s\")", base); - if (sb.st_uid != uid || (sb.st_mode & 077) != 0) + if (sb.st_uid != uid || (sb.st_mode & mask) != 0) errx(1, "%s has wrong permissions", base); } void +privdrop(void) +{ + struct passwd *pw; + struct stat sb; + + if ((pw = getpwnam(SNDIO_USER)) == NULL) + err(1, "getpwnam"); + if (stat(pw->pw_dir, &sb) < 0) + err(1, "stat(\"%s\")", pw->pw_dir); + if (sb.st_uid != 0 || (sb.st_mode & 022) != 0) + errx(1, "%s has wrong permissions", pw->pw_dir); + 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"); +} + +void stopall(char *base) { struct file *f; @@ -647,6 +686,8 @@ aucat_main(int argc, char **argv) snprintf(path, sizeof(path), "%s/%s%u", base, DEFAULT_SOFTAUDIO, unit); listen_new(&listen_ops, path); + if (geteuid() == 0) + privdrop(); if (!d_flag && daemon(0, 0) < 0) err(1, "daemon"); } @@ -705,7 +746,7 @@ aucat_main(int argc, char **argv) dev_done(); filelist_done(); if (l_flag) { - if (rmdir(base) < 0 && errno != ENOTEMPTY) + if (rmdir(base) < 0 && errno != ENOTEMPTY && errno != EPERM) warn("rmdir(\"%s\")", base); } unsetsig(); @@ -817,6 +858,8 @@ midicat_main(int argc, char **argv) snprintf(path, sizeof(path), "%s/%s%u", base, DEFAULT_MIDITHRU, unit); listen_new(&listen_ops, path); + if (geteuid() == 0) + privdrop(); if (!d_flag && daemon(0, 0) < 0) err(1, "daemon"); } @@ -873,7 +916,7 @@ midicat_main(int argc, char **argv) dev_done(); filelist_done(); if (l_flag) { - if (rmdir(base) < 0 && errno != ENOTEMPTY) + if (rmdir(base) < 0 && errno != ENOTEMPTY && errno != EPERM) warn("rmdir(\"%s\")", base); } unsetsig(); diff --git a/usr.bin/aucat/midicat.1 b/usr.bin/aucat/midicat.1 index 6baec0a512c..0d760141c98 100644 --- a/usr.bin/aucat/midicat.1 +++ b/usr.bin/aucat/midicat.1 @@ -1,4 +1,4 @@ -.\" $OpenBSD: midicat.1,v 1.9 2010/04/06 20:07:01 ratchov Exp $ +.\" $OpenBSD: midicat.1,v 1.10 2010/04/22 17:43:30 ratchov Exp $ .\" .\" Copyright (c) 2006 Alexandre Ratchov <alex@caoua.org> .\" @@ -14,7 +14,7 @@ .\" ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF .\" OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. .\" -.Dd $Mdocdate: April 6 2010 $ +.Dd $Mdocdate: April 22 2010 $ .Dt MIDICAT 1 .Os .Sh NAME @@ -101,6 +101,16 @@ This feature is provided to allow multiple applications acting as sources to keep their connection open while idling; it does not replace a fully featured MIDI merger. .Pp +A +.Nm +process may be started by the super-user, +in which case any user will be able to connect to it. +For privacy reasons, only one user may have connections to +it at a given time. +.Pp +Alternatively, each user may start its own +.Nm +process. It is generally not desirable to have multiple instances of .Nm running in server mode, so it is good practice to start it thus: diff --git a/usr.bin/aucat/sock.c b/usr.bin/aucat/sock.c index da0fff1be1c..18f2f8f366f 100644 --- a/usr.bin/aucat/sock.c +++ b/usr.bin/aucat/sock.c @@ -1,4 +1,4 @@ -/* $OpenBSD: sock.c,v 1.43 2010/04/21 06:13:07 ratchov Exp $ */ +/* $OpenBSD: sock.c,v 1.44 2010/04/22 17:43:30 ratchov Exp $ */ /* * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org> * @@ -15,6 +15,9 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. */ +#include <sys/types.h> +#include <sys/socket.h> +#include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <string.h> @@ -35,11 +38,12 @@ int sock_read(struct sock *); int sock_write(struct sock *); int sock_execmsg(struct sock *); void sock_reset(struct sock *); +void sock_close(struct file *); struct fileops sock_ops = { "sock", sizeof(struct sock), - pipe_close, + sock_close, pipe_read, pipe_write, NULL, /* start */ @@ -83,6 +87,16 @@ struct ctl_ops ctl_sockops = { sock_locreq }; +unsigned sock_sesrefs = 0; /* connections to the session */ +uid_t sock_sesuid; /* owner of the session */ + +void +sock_close(struct file *f) +{ + sock_sesrefs--; + pipe_close(f); +} + void rsock_done(struct aproc *p) { @@ -296,6 +310,27 @@ sock_new(struct fileops *ops, int fd) { struct aproc *rproc, *wproc; struct sock *f; + uid_t uid, gid; + + /* + * ensure that all connections belong to the same user, + * for privacy reasons. + * + * XXX: is there a portable way of doing this ? + */ + if (getpeereid(fd, &uid, &gid) < 0) { + close(fd); + return NULL; + } + if (sock_sesrefs == 0) { + /* start a new session */ + sock_sesuid = uid; + } else if (uid != sock_sesuid) { + /* session owned by another user, drop connection */ + close(fd); + return NULL; + } + sock_sesrefs++; f = (struct sock *)pipe_new(ops, fd, "sock"); if (f == NULL) |