From de37b64085838de48894200ae2b9d49417668a76 Mon Sep 17 00:00:00 2001 Message-Id: From: Mark Wooding Date: Sat, 1 Dec 2012 12:48:23 +0000 Subject: [PATCH] server: add a private socket for root Organization: Straylight/Edgeware From: Richard Kettlewell root will try and use it if possible; anything connecting on it is allowed through without knowing a password. The jukebox user will be able to use it too although it doesn't actually try. Both these users have RW access to the database so they aren't being given any privilege they don't have already here. The speaker socket moves to the same private directory. --- lib/client-common.c | 14 ++++++-- server/disorder-server.h | 3 +- server/play.c | 2 +- server/server.c | 12 +++++-- server/speaker.c | 6 ++-- server/state.c | 75 ++++++++++++++++++++++++---------------- 6 files changed, 72 insertions(+), 40 deletions(-) diff --git a/lib/client-common.c b/lib/client-common.c index 476117c..897260e 100644 --- a/lib/client-common.c +++ b/lib/client-common.c @@ -25,6 +25,7 @@ #include #include #include +#include #include "log.h" #include "configuration.h" @@ -43,7 +44,7 @@ socklen_t find_server(struct config *c, struct sockaddr *sa; struct sockaddr_un su; struct addrinfo *res = 0; - char *name; + char *name = NULL; socklen_t len; if(c->connect.af != -1) { @@ -53,7 +54,16 @@ socklen_t find_server(struct config *c, sa = res->ai_addr; len = res->ai_addrlen; } else { - name = config_get_file2(c, "socket"); + if(getuid() == 0) { + /* root will use the private socket if possible (which it should be) */ + name = config_get_file2(c, "private/socket"); + if(access(name, R_OK) != 0) { + xfree(name); + name = NULL; + } + } + if(!name) + name = config_get_file2(c, "socket"); if(strlen(name) >= sizeof su.sun_path) { disorder_error(errno, "socket path is too long"); return -1; diff --git a/server/disorder-server.h b/server/disorder-server.h index a110aa6..bdc0ef9 100644 --- a/server/disorder-server.h +++ b/server/disorder-server.h @@ -234,7 +234,8 @@ void add_random_track(ev_source *ev); int server_start(ev_source *ev, int pf, size_t socklen, const struct sockaddr *sa, - const char *name); + const char *name, + int privileged); /* start listening. Return the fd. */ int server_stop(ev_source *ev, int fd); diff --git a/server/play.c b/server/play.c index b6f67fb..866096b 100644 --- a/server/play.c +++ b/server/play.c @@ -422,7 +422,7 @@ static int prepare_child(struct queue_entry *q, memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; snprintf(addr.sun_path, sizeof addr.sun_path, - "%s/speaker/socket", config->home); + "%s/private/speaker", config->home); int sfd = xsocket(PF_UNIX, SOCK_STREAM, 0); if(connect(sfd, (const struct sockaddr *)&addr, sizeof addr) < 0) disorder_fatal(errno, "connecting to %s", addr.sun_path); diff --git a/server/server.c b/server/server.c index c93701a..2b7180c 100644 --- a/server/server.c +++ b/server/server.c @@ -42,6 +42,7 @@ int wideopen; struct listener { const char *name; int pf; + int privileged; }; struct conn; @@ -589,7 +590,7 @@ static int c_user(struct conn *c, /* check whether the response is right */ res = authhash(c->nonce, sizeof c->nonce, password, config->authorization_algorithm); - if(wideopen || (res && !strcmp(res, vec[1]))) { + if(wideopen || c->l->privileged || (res && !strcmp(res, vec[1]))) { c->who = vec[0]; c->rights = rights; /* currently we only bother logging remote connections */ @@ -2136,12 +2137,16 @@ static int listen_callback(ev_source *ev, int server_start(ev_source *ev, int pf, size_t socklen, const struct sockaddr *sa, - const char *name) { + const char *name, + int privileged) { int fd; struct listener *l = xmalloc(sizeof *l); static const int one = 1; - D(("server_init socket %s", name)); + D(("server_init socket %s privileged=%d", name, privileged)); + /* Sanity check */ + if(privileged && pf != AF_UNIX) + disorder_fatal(0, "cannot create a privileged listener on a non-local port"); fd = xsocket(pf, SOCK_STREAM, 0); xsetsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof one); if(bind(fd, sa, socklen) < 0) { @@ -2153,6 +2158,7 @@ int server_start(ev_source *ev, int pf, cloexec(fd); l->name = name; l->pf = pf; + l->privileged = privileged; if(ev_listen(ev, fd, listen_callback, l, "server listener")) exit(EXIT_FAILURE); disorder_info("listening on %s", name); diff --git a/server/speaker.c b/server/speaker.c index 5100958..1db3658 100644 --- a/server/speaker.c +++ b/server/speaker.c @@ -796,8 +796,8 @@ int main(int argc, char **argv) { if(backend->configure) backend->configure(); backend->start(speaker_callback, NULL); - /* create the socket directory */ - byte_xasprintf(&dir, "%s/speaker", config->home); + /* create the private socket directory */ + byte_xasprintf(&dir, "%s/private", config->home); unlink(dir); /* might be a leftover socket */ if(mkdir(dir, 0700) < 0 && errno != EEXIST) disorder_fatal(errno, "error creating %s", dir); @@ -805,7 +805,7 @@ int main(int argc, char **argv) { listenfd = xsocket(PF_UNIX, SOCK_STREAM, 0); memset(&addr, 0, sizeof addr); addr.sun_family = AF_UNIX; - snprintf(addr.sun_path, sizeof addr.sun_path, "%s/speaker/socket", + snprintf(addr.sun_path, sizeof addr.sun_path, "%s/private/speaker", config->home); if(unlink(addr.sun_path) < 0 && errno != ENOENT) disorder_error(errno, "removing %s", addr.sun_path); diff --git a/server/state.c b/server/state.c index b125417..8a5b6ce 100644 --- a/server/state.c +++ b/server/state.c @@ -20,11 +20,13 @@ */ #include "disorder-server.h" -/** @brief Current AF_UNIX socket path */ -static const char *current_unix; +/** @brief A unix domain socket */ +struct unix_socket { + const char *path; + int fd; +}; -/** @brief Current AF_UNIX socket */ -static int current_unix_fd; +struct unix_socket main_socket[1], priv_socket[1]; /** @brief TCP listener definition */ struct listener { @@ -77,40 +79,53 @@ static struct sockaddr *copy_sockaddr(const struct addrinfo *addr) { return sa; } -/** @brief Create and destroy sockets to set current configuration */ -void reset_sockets(ev_source *ev) { - const char *new_unix; - struct addrinfo *res, *r; - struct listener *l, **ll; +static void reset_unix_socket(ev_source *ev, + struct unix_socket *s, + const char *new_path, + int privileged) { struct sockaddr_un sun; - - /* unix first */ - new_unix = config_get_file("socket"); - if(!current_unix || strcmp(current_unix, new_unix)) { + if(!s->path || strcmp(s->path, new_path)) { /* either there was no socket, or there was but a different path */ - if(current_unix) { + if(s->path) { /* stop the old one and remove it from the filesystem */ - server_stop(ev, current_unix_fd); - if(unlink(current_unix) < 0) - disorder_fatal(errno, "unlink %s", current_unix); + server_stop(ev, s->fd); + if(unlink(s->path) < 0) + disorder_fatal(errno, "unlink %s", s->path); } /* start the new one */ - if(strlen(new_unix) >= sizeof sun.sun_path) - disorder_fatal(0, "socket path %s is too long", new_unix); + if(strlen(new_path) >= sizeof sun.sun_path) + disorder_fatal(0, "socket path %s is too long", new_path); memset(&sun, 0, sizeof sun); sun.sun_family = AF_UNIX; - strcpy(sun.sun_path, new_unix); - if(unlink(new_unix) < 0 && errno != ENOENT) - disorder_fatal(errno, "unlink %s", new_unix); - if((current_unix_fd = server_start(ev, PF_UNIX, sizeof sun, - (const struct sockaddr *)&sun, - new_unix)) >= 0) { - current_unix = new_unix; - if(chmod(new_unix, 0777) < 0) - disorder_fatal(errno, "error calling chmod %s", new_unix); + strcpy(sun.sun_path, new_path); + if(unlink(new_path) < 0 && errno != ENOENT) + disorder_fatal(errno, "unlink %s", new_path); + if((s->fd = server_start(ev, PF_UNIX, sizeof sun, + (const struct sockaddr *)&sun, + new_path, + privileged)) >= 0) { + s->path = new_path; + if(chmod(new_path, 0777) < 0) /* TODO */ + disorder_fatal(errno, "error calling chmod %s", new_path); } else - current_unix = 0; + s->path = 0; } +} + +/** @brief Create and destroy sockets to set current configuration */ +void reset_sockets(ev_source *ev) { + struct addrinfo *res, *r; + struct listener *l, **ll; + const char *private_dir = config_get_file("private"); + + /* create the private socket directory */ + unlink(private_dir); + if(mkdir(private_dir, 0700) < 0 && errno != EEXIST) + disorder_fatal(errno, "error creating %s", private_dir); + + /* unix first */ + reset_unix_socket(ev, main_socket, config_get_file("socket"), 0); + reset_unix_socket(ev, priv_socket, config_get_file("private/socket"), 1); /* get the new listen config */ if(config->listen.af != -1) @@ -142,7 +157,7 @@ void reset_sockets(ev_source *ev) { if(!l) { /* Didn't find a match, need a new listener */ int fd = server_start(ev, r->ai_family, r->ai_addrlen, r->ai_addr, - format_sockaddr(r->ai_addr)); + format_sockaddr(r->ai_addr), 0); if(fd >= 0) { l = xmalloc(sizeof *l); l->next = listeners; -- [mdw]