From: Roy Marples Date: Sun, 14 Sep 2014 19:12:57 +0000 (+0000) Subject: Add an unprivileged control socket so that normal users can X-Git-Tag: v6.4.4~10 X-Git-Url: http://git.ipfire.org/?a=commitdiff_plain;h=9b72ef1ca1cbbc09a23a74ec282da9f8eb34015c;p=thirdparty%2Fdhcpcd.git Add an unprivileged control socket so that normal users can obtain dhcpcd running state. --- diff --git a/control.c b/control.c index 2b300a8c..5c655806 100644 --- 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; diff --git a/control.h b/control.h index 7f555977..260f126b 100644 --- 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 62864a13..3be515d0 100644 --- 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 diff --git a/dhcpcd.8.in b/dhcpcd.8.in index 4cf803f8..26edfbbd 100644 --- a/dhcpcd.8.in +++ b/dhcpcd.8.in @@ -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 diff --git a/dhcpcd.c b/dhcpcd.c index ece6507a..250b9e53 100644 --- 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; diff --git a/dhcpcd.h b/dhcpcd.h index 4b6624b9..df4af79a 100644 --- 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; diff --git a/script.c b/script.c index f0f8d27c..b244ff19 100644 --- 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,