]> git.ipfire.org Git - thirdparty/dhcpcd.git/commitdiff
Add an unprivileged control socket so that normal users can
authorRoy Marples <roy@marples.name>
Sun, 14 Sep 2014 19:12:57 +0000 (19:12 +0000)
committerRoy Marples <roy@marples.name>
Sun, 14 Sep 2014 19:12:57 +0000 (19:12 +0000)
obtain dhcpcd running state.

control.c
control.h
defs.h
dhcpcd.8.in
dhcpcd.c
dhcpcd.h
script.c

index 2b300a8cf33bd4f48abc596d5fe4f836d17bd9ad..5c655806ff559cfd29a0d57a2133764323c53348 100644 (file)
--- a/control.c
+++ b/control.c
@@ -139,23 +139,26 @@ control_handle_data(void *arg)
                        }
                }
                *ap = NULL;
-               if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1)
+               if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) {
                        syslog(LOG_ERR, "%s: dhcpcd_handleargs: %m", __func__);
+                       if (errno != EINTR && errno != EAGAIN) {
+                               control_delete(fd);
+                               return;
+                       }
+               }
        }
 }
 
 static void
-control_handle(void *arg)
+control_handle1(struct dhcpcd_ctx *ctx, int lfd, unsigned int fd_flags)
 {
-       struct dhcpcd_ctx *ctx;
        struct sockaddr_un run;
        socklen_t len;
        struct fd_list *l;
        int fd, flags;
 
-       ctx = arg;
        len = sizeof(run);
-       if ((fd = accept(ctx->control_fd, (struct sockaddr *)&run, &len)) == -1)
+       if ((fd = accept(lfd, (struct sockaddr *)&run, &len)) == -1)
                return;
        if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
            fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
@@ -173,7 +176,7 @@ control_handle(void *arg)
        if (l) {
                l->ctx = ctx;
                l->fd = fd;
-               l->listener = 0;
+               l->flags = fd_flags;
                TAILQ_INIT(&l->queue);
                TAILQ_INIT(&l->free_queue);
                TAILQ_INSERT_TAIL(&ctx->control_fds, l, next);
@@ -183,66 +186,109 @@ control_handle(void *arg)
                close(fd);
 }
 
-static socklen_t
-make_sock(struct dhcpcd_ctx *ctx, struct sockaddr_un *sa, const char *ifname)
+static void
+control_handle(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+
+       control_handle1(ctx, ctx->control_fd, 0);
+}
+
+static void
+control_handle_unpriv(void *arg)
+{
+       struct dhcpcd_ctx *ctx = arg;
+
+       control_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV);
+}
+
+static int
+make_sock(struct sockaddr_un *sa, const char *ifname, int unpriv)
 {
+       int fd;
 
 #ifdef SOCK_CLOEXEC
-       if ((ctx->control_fd = socket(AF_UNIX,
+       if ((fd = socket(AF_UNIX,
            SOCK_STREAM | SOCK_CLOEXEC | SOCK_NONBLOCK, 0)) == -1)
-               return 0;
+               return -1;
 #else
        int flags;
 
-       if ((ctx->control_fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
-               return 0;
-       if ((flags = fcntl(ctx->control_fd, F_GETFD, 0)) == -1 ||
-           fcntl(ctx->control_fd, F_SETFD, flags | FD_CLOEXEC) == -1)
+       if ((fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1)
+               return -1;
+       if ((flags = fcntl(fd, F_GETFD, 0)) == -1 ||
+           fcntl(fd, F_SETFD, flags | FD_CLOEXEC) == -1)
        {
-               close(ctx->control_fd);
-               ctx->control_fd = -1;
-               return 0;
+               close(fd);
+               return -1;
        }
-       if ((flags = fcntl(ctx->control_fd, F_GETFL, 0)) == -1 ||
-           fcntl(ctx->control_fd, F_SETFL, flags | O_NONBLOCK) == -1)
+       if ((flags = fcntl(fd, F_GETFL, 0)) == -1 ||
+           fcntl(fd, F_SETFL, flags | O_NONBLOCK) == -1)
        {
-               close(ctx->control_fd);
-               ctx->control_fd = -1;
-               return 0;
+               close(fd);
+               return -1;
        }
 #endif
        memset(sa, 0, sizeof(*sa));
        sa->sun_family = AF_UNIX;
-       snprintf(sa->sun_path, sizeof(sa->sun_path), CONTROLSOCKET,
-           ifname ? "-" : "", ifname ? ifname : "");
-       strlcpy(ctx->control_sock, sa->sun_path, sizeof(ctx->control_sock));
-       return (socklen_t)SUN_LEN(sa);
+       if (unpriv)
+               strlcpy(sa->sun_path, UNPRIVSOCKET, sizeof(sa->sun_path));
+       else {
+               snprintf(sa->sun_path, sizeof(sa->sun_path), CONTROLSOCKET,
+                   ifname ? "-" : "", ifname ? ifname : "");
+       }
+       return fd;
 }
 
-int
-control_start(struct dhcpcd_ctx *ctx, const char *ifname)
+#define S_PRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP)
+#define S_UNPRIV (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
+
+static int
+control_start1(struct dhcpcd_ctx *ctx, const char *ifname, mode_t fmode)
 {
        struct sockaddr_un sa;
+       int fd;
        socklen_t len;
 
-       if ((len = make_sock(ctx, &sa, ifname)) == 0)
+       if ((fd = make_sock(&sa, ifname, (fmode & S_UNPRIV) == S_UNPRIV)) == -1)
                return -1;
-       unlink(ctx->control_sock);
-       if (bind(ctx->control_fd, (struct sockaddr *)&sa, len) == -1 ||
-           chmod(ctx->control_sock,
-               S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP) == -1 ||
+       len = (socklen_t)SUN_LEN(&sa);
+       unlink(sa.sun_path);
+       if (bind(fd, (struct sockaddr *)&sa, len) == -1 ||
+           chmod(sa.sun_path, fmode) == -1 ||
            (ctx->control_group &&
-               chown(ctx->control_sock,
-               geteuid(), ctx->control_group) == -1) ||
-           listen(ctx->control_fd, sizeof(ctx->control_fds)) == -1)
+           chown(sa.sun_path, geteuid(), ctx->control_group) == -1) ||
+           listen(fd, sizeof(ctx->control_fds)) == -1)
        {
-               close(ctx->control_fd);
-               ctx->control_fd = -1;
-               unlink(ctx->control_sock);
+               close(fd);
+               unlink(sa.sun_path);
                return -1;
        }
-       eloop_event_add(ctx->eloop, ctx->control_fd,
-           control_handle, ctx, NULL, NULL);
+       
+       if ((fmode & S_UNPRIV) != S_UNPRIV)
+               strlcpy(ctx->control_sock, sa.sun_path,
+                   sizeof(ctx->control_sock));
+       return fd;
+}
+
+int
+control_start(struct dhcpcd_ctx *ctx, const char *ifname)
+{
+       int fd;
+
+       if ((fd = control_start1(ctx, ifname, S_PRIV)) == -1)
+               return -1;
+
+       ctx->control_fd = fd;
+       eloop_event_add(ctx->eloop, fd, control_handle, ctx, NULL, NULL);
+
+       if (ifname == NULL && (fd = control_start1(ctx, NULL, S_UNPRIV)) != -1){
+               /* We must be in master mode, so create an unpriviledged socket
+                * to allow normal users to learn the status of dhcpcd. */
+               ctx->control_unpriv_fd = fd;
+               eloop_event_add(ctx->eloop, fd, control_handle_unpriv,
+                   ctx, NULL, NULL);
+       }
        return ctx->control_fd;
 }
 
@@ -260,6 +306,14 @@ control_stop(struct dhcpcd_ctx *ctx)
        if (unlink(ctx->control_sock) == -1)
                retval = -1;
 
+       if (ctx->control_unpriv_fd != -1) {
+               eloop_event_delete(ctx->eloop, ctx->control_unpriv_fd, 0);
+               close(ctx->control_unpriv_fd);
+               ctx->control_unpriv_fd = -1;
+               if (unlink(UNPRIVSOCKET) == -1)
+                       retval = -1;
+       }
+
        while ((l = TAILQ_FIRST(&ctx->control_fds))) {
                TAILQ_REMOVE(&ctx->control_fds, l, next);
                eloop_event_delete(ctx->eloop, l->fd, 0);
@@ -277,8 +331,9 @@ control_open(struct dhcpcd_ctx *ctx, const char *ifname)
        struct sockaddr_un sa;
        socklen_t len;
 
-       if ((len = make_sock(ctx, &sa, ifname)) == 0)
+       if ((ctx->control_fd = make_sock(&sa, ifname, 0)) == -1)
                return -1;
+       len = (socklen_t)SUN_LEN(&sa);
        if (connect(ctx->control_fd, (struct sockaddr *)&sa, len) == -1) {
                close(ctx->control_fd);
                ctx->control_fd = -1;
index 7f5559772a742acfc94d1e6c611302d5dcd0da2d..260f126b65281f8a060c58af7f7e9ecd54a77683 100644 (file)
--- a/control.h
+++ b/control.h
@@ -45,12 +45,15 @@ struct fd_list {
        TAILQ_ENTRY(fd_list) next;
        struct dhcpcd_ctx *ctx;
        int fd;
-       int listener;
+       unsigned int flags;
        struct fd_data_head queue;
        struct fd_data_head free_queue;
 };
 TAILQ_HEAD(fd_list_head, fd_list);
 
+#define FD_LISTEN      (0<<1)
+#define FD_UNPRIV      (1<<1)
+
 int control_start(struct dhcpcd_ctx *, const char *);
 int control_stop(struct dhcpcd_ctx *);
 int control_open(struct dhcpcd_ctx *, const char *);
diff --git a/defs.h b/defs.h
index 62864a1358d55803d67ce882054732cf6c0be03c..3be515d0b3a5a0fcde4f4f26c9414f4f320dde9e 100644 (file)
--- a/defs.h
+++ b/defs.h
@@ -57,6 +57,9 @@
 #ifndef CONTROLSOCKET
 # define CONTROLSOCKET         RUNDIR "/" PACKAGE "%s%s.sock"
 #endif
+#ifndef UNPRIVSOCKET
+# define UNPRIVSOCKET          RUNDIR "/" PACKAGE ".unpriv.sock"
+#endif
 #ifndef RDM_MONOFILE
 # define RDM_MONOFILE          DBDIR "/" PACKAGE "-rdm.monotonic"
 #endif
index 4cf803f83ad76f222fc81fa1fd69ab9e6a999f2f..26edfbbdbdd72d5c4739c06924b879d4944387ae 100644 (file)
@@ -22,7 +22,7 @@
 .\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 .\" SUCH DAMAGE.
 .\"
-.Dd September 13, 2014
+.Dd September 14, 2014
 .Dt DHCPCD 8
 .Os
 .Sh NAME
@@ -664,6 +664,9 @@ running on the
 .Ar interface .
 .It Pa @RUNDIR@/dhcpcd.sock
 Control socket to the master daemon.
+.It Pa @RUNDIR@/dhcpcd.unpriv.sock
+Unpriviledged socket to the master daemon, only allows state retrieval.
+Control socket to the master daemon.
 .It Pa @RUNDIR@/dhcpcd\- Ns Ar interface Ns .sock
 Control socket to per interface daemon.
 .El
index ece6507a4121610640acbdc8455dde2addb050bc..250b9e53395ec53bf208396b256cf607db7ee73d 100644 (file)
--- a/dhcpcd.c
+++ b/dhcpcd.c
@@ -1114,25 +1114,29 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd,
        size_t len, l;
        char *tmp, *p;
 
-       if (fd != NULL) {
-               /* Special commands for our control socket
-                * as the other end should be blocking until it gets the
-                * expected reply we should be safely able just to change the
-                * write callback on the fd */
-               if (strcmp(*argv, "--version") == 0) {
-                       return control_queue(fd, UNCONST(VERSION),
-                           strlen(VERSION) + 1, 0);
-               } else if (strcmp(*argv, "--getconfigfile") == 0) {
-                       return control_queue(fd, UNCONST(fd->ctx->cffile),
-                           strlen(fd->ctx->cffile) + 1, 0);
-               } else if (strcmp(*argv, "--getinterfaces") == 0) {
-                       eloop_event_add(fd->ctx->eloop, fd->fd, NULL, NULL,
-                           dhcpcd_getinterfaces, fd);
-                       return 0;
-               } else if (strcmp(*argv, "--listen") == 0) {
-                       fd->listener = 1;
-                       return 0;
-               }
+       /* Special commands for our control socket
+        * as the other end should be blocking until it gets the
+        * expected reply we should be safely able just to change the
+        * write callback on the fd */
+       if (strcmp(*argv, "--version") == 0) {
+               return control_queue(fd, UNCONST(VERSION),
+                   strlen(VERSION) + 1, 0);
+       } else if (strcmp(*argv, "--getconfigfile") == 0) {
+               return control_queue(fd, UNCONST(fd->ctx->cffile),
+                   strlen(fd->ctx->cffile) + 1, 0);
+       } else if (strcmp(*argv, "--getinterfaces") == 0) {
+               eloop_event_add(fd->ctx->eloop, fd->fd, NULL, NULL,
+                   dhcpcd_getinterfaces, fd);
+               return 0;
+       } else if (strcmp(*argv, "--listen") == 0) {
+               fd->flags |= FD_LISTEN;
+               return 0;
+       }
+
+       /* Only priviledged users can control dhcpcd via the socket. */
+       if (fd->flags & FD_UNPRIV) {
+               errno = EPERM;
+               return -1;
        }
 
        /* Log the command */
@@ -1247,7 +1251,7 @@ main(int argc, char **argv)
 
        ifo = NULL;
        ctx.cffile = CONFIG;
-       ctx.pid_fd = ctx.control_fd = ctx.link_fd = -1;
+       ctx.pid_fd = ctx.control_fd = ctx.control_unpriv_fd = ctx.link_fd = -1;
        TAILQ_INIT(&ctx.control_fds);
 #ifdef PLUGIN_DEV
        ctx.dev_fd = -1;
index 4b6624b97c36ce05acb56c4f5565a828d945a89d..df4af79a6e64259854a6ffae1bd9aea324cb4e7f 100644 (file)
--- a/dhcpcd.h
+++ b/dhcpcd.h
@@ -96,6 +96,7 @@ struct dhcpcd_ctx {
        struct eloop_ctx *eloop;
 
        int control_fd;
+       int control_unpriv_fd;
        struct fd_list_head control_fds;
        char control_sock[sizeof(CONTROLSOCKET) + IF_NAMESIZE];
        gid_t control_group;
index f0f8d27c01f42b66e4ab73a650ef3eded6590312..b244ff19458b6233d2cd0c5e8e8df478708010a0 100644 (file)
--- a/script.c
+++ b/script.c
@@ -546,9 +546,12 @@ send_interface1(struct fd_list *fd, const struct interface *iface,
 
        if (make_env(iface, reason, &env) == -1)
                return -1;
+       s = NULL;
        elen = (size_t)arraytostr((const char *const *)env, &s);
-       if ((ssize_t)elen == -1)
+       if ((ssize_t)elen == -1) {
+               free(s);
                return -1;
+       }
        retval = control_queue(fd, s, elen, 1);
        ep = env;
        while (*ep)
@@ -694,7 +697,7 @@ script_runreason(const struct interface *ifp, const char *reason)
        bigenv = NULL;
        status = 0;
        TAILQ_FOREACH(fd, &ifp->ctx->control_fds, next) {
-               if (!fd->listener)
+               if (!(fd->flags & FD_LISTEN))
                        continue;
                if (bigenv == NULL) {
                        elen = (size_t)arraytostr((const char *const *)env,