chiark / gitweb /
server: add a private socket for root
authorRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 1 Dec 2012 12:48:23 +0000 (12:48 +0000)
committerRichard Kettlewell <rjk@terraraq.org.uk>
Sat, 1 Dec 2012 12:48:23 +0000 (12:48 +0000)
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
server/disorder-server.h
server/play.c
server/server.c
server/speaker.c
server/state.c

index 476117ce93fe524bcbd6027452ea510622430e9d..897260e133e34f09d295a1df717e805ac22a2ab4 100644 (file)
@@ -25,6 +25,7 @@
 #include <sys/un.h>
 #include <errno.h>
 #include <netdb.h>
+#include <unistd.h>
 
 #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;
index a110aa6bbd2ed490f73260dfe0eac5c214a702c1..bdc0ef9e433a317a7dabbdb3cb6f7ecec83cd625 100644 (file)
@@ -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);
index b6f67fb82a5b703e238472e2ebaf1a4ca6f416a6..866096b260db4082ef3d16ef07f7d929691a06c3 100644 (file)
@@ -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);
index c93701a136afba49cd63d0764fdd72bc98a539dc..2b7180c75fb87b2e95945e0ef9fc93dc35c292df 100644 (file)
@@ -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);
index 5100958023501200e00631a56cc0346c63bea525..1db365830fa70bdf0895683a06c26c1d8c10b7fd 100644 (file)
@@ -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);
index b1254170fe4df78ac181210f902eda4855dcdbcc..8a5b6cee1f6048304407ece0745d6d34be2b1fb1 100644 (file)
  */
 #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;