#include <errno.h>
#include <fcntl.h>
+#include <pwd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void
control_free(struct fd_list *fd)
{
-#ifdef PRIVSEP
- if (fd->ctx->ps_control_client == fd)
- fd->ctx->ps_control_client = NULL;
-#endif
-
eloop_event_delete(fd->ctx->eloop, fd->fd);
close(fd->fd);
TAILQ_REMOVE(&fd->ctx->control_fds, fd, next);
static void
control_hangup(struct fd_list *fd)
{
-#ifdef PRIVSEP
- if (IN_PRIVSEP(fd->ctx)) {
- if (ps_ctl_sendeof(fd) == -1)
- logerr(__func__);
- }
-#endif
control_free(fd);
}
static int
control_handle_read(struct fd_list *fd)
{
- char buffer[1024];
- ssize_t bytes;
-
- bytes = read(fd->fd, buffer, sizeof(buffer) - 1);
+ uid_t uid;
+ gid_t gid;
+ char buf[BUFSIZ];
+ struct iovec iov[] = { {
+ .iov_base = buf,
+ .iov_len = sizeof(buf),
+ } };
+ struct msghdr msg = {
+ .msg_iov = iov,
+ .msg_iovlen = __arraycount(iov),
+ };
+ ssize_t bytes, err;
+
+ bytes = recvmsg(fd->fd, &msg, 0);
if (bytes == -1)
logerr(__func__);
if (bytes == -1 || bytes == 0)
return (int)bytes;
+ if (getpeereid(fd->fd, &uid, &gid) == -1) {
+ logerr("%s: getpeereid", __func__);
+ return -1;
+ }
+
#ifdef PRIVSEP
if (IN_PRIVSEP(fd->ctx)) {
- ssize_t err;
+ ssize_t perr;
+
+ perr = ps_root_user_ispriv(fd->ctx, uid, gid);
+ if (perr == -1) {
+ logerr(__func__);
+ return -1;
+ }
+ if (perr == 0)
+ fd->flags &= ~FD_PRIV;
+ else
+ fd->flags |= FD_PRIV;
fd->flags |= FD_SENDLEN;
- err = ps_ctl_handleargs(fd, buffer, (size_t)bytes);
+ perr = ps_ctl_handleargs(fd, buf, (size_t)bytes);
fd->flags &= ~FD_SENDLEN;
- if (err == -1) {
+ if (perr == -1) {
logerr(__func__);
return 0;
}
- if (err == 1 &&
- ps_ctl_sendargs(fd, buffer, (size_t)bytes) == -1) {
+ iov[0].iov_len = (size_t)bytes;
+ if (perr == 1 && ps_ctl_sendmsg(fd, &msg) == -1) {
logerr(__func__);
return -1;
}
}
#endif
- return control_recvdata(fd, buffer, (size_t)bytes);
+ err = control_user_ispriv(fd->ctx, uid, gid);
+ if (err == -1) {
+ logerr(__func__);
+ return -1;
+ }
+ if (err == 0)
+ fd->flags &= ~FD_PRIV;
+ else
+ fd->flags |= FD_PRIV;
+ return control_recvmsg(fd, &msg, (size_t)bytes);
}
static int
if (TAILQ_FIRST(&fd->queue) != NULL)
return 0;
-#ifdef PRIVSEP
- if (IN_PRIVSEP_SE(fd->ctx) && !(fd->flags & FD_LISTEN)) {
- if (ps_ctl_sendeof(fd) == -1)
- logerr(__func__);
- }
-#endif
-
/* Done sending data, stop watching write to fd */
if (eloop_event_add(fd->ctx->eloop, fd->fd, ELE_READ,
control_handle_data, fd) == -1)
}
if (events & ELE_READ) {
err = control_handle_read(fd);
- if (err == -1 || err == 0)
+ if ((err == -1 && errno != EPERM) || err == 0)
goto hangup;
}
if (events & ELE_HANGUP)
}
int
-control_recvdata(struct fd_list *fd, char *data, size_t len)
+control_recvmsg(struct fd_list *fd, struct msghdr *msg, size_t len)
{
- char *p = data, *e;
+ struct iovec *iov;
+ char *p, *e;
char *argvp[255], **ap;
int argc;
+ if (msg->msg_iovlen == 0) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ iov = msg->msg_iov;
+ p = (char *)iov->iov_base;
+
/* Each command is \n terminated
* Each argument is NULL separated */
while (len != 0) {
*ap = NULL;
if (dhcpcd_handleargs(fd->ctx, fd, argc, argvp) == -1) {
logerr(__func__);
- if (errno != EINTR && errno != EAGAIN)
+ if (errno != EINTR && errno != EAGAIN && errno != EPERM)
return -1;
}
}
return 1;
}
+struct fd_list *
+control_find(struct dhcpcd_ctx *ctx, int fd)
+{
+ struct fd_list *l;
+
+ TAILQ_FOREACH(l, &ctx->control_fds, next) {
+ if (l->fd == fd)
+ return l;
+ }
+
+ errno = ESRCH;
+ return NULL;
+}
+
struct fd_list *
control_new(struct dhcpcd_ctx *ctx, int fd, unsigned int flags)
{
struct fd_list *l;
+ l = control_find(ctx, fd);
+ if (l != NULL) {
+ l->flags = flags;
+ return l;
+ }
+
l = malloc(sizeof(*l));
if (l == NULL)
return NULL;
struct sockaddr_un run;
socklen_t len;
struct fd_list *l;
- int fd, flags;
+ int fd, flags = 1;
if (events != ELE_READ)
logerrx("%s: unexpected event 0x%04x", __func__, events);
if (eloop_event_add(ctx->eloop, l->fd, ELE_READ, control_handle_data,
l) == -1)
logerr("%s: eloop_event_add", __func__);
+
return;
error:
control_handle1(ctx, ctx->control_fd, 0, events);
}
-static void
-control_handle_unpriv(void *arg, unsigned short events)
-{
- struct dhcpcd_ctx *ctx = arg;
-
- control_handle1(ctx, ctx->control_unpriv_fd, FD_UNPRIV, events);
-}
-
static int
-make_path(char *path, size_t len, const char *ifname, sa_family_t family,
- bool unpriv)
+make_path(char *path, size_t len, const char *ifname, sa_family_t family)
{
const char *per;
- const char *sunpriv;
switch (family) {
case AF_INET:
per = "";
break;
}
- if (unpriv)
- sunpriv = ifname ? ".unpriv" : "unpriv.";
- else
- sunpriv = "";
return snprintf(path, len, CONTROLSOCKET, ifname ? ifname : "",
- ifname ? per : "", sunpriv, ifname ? "." : "");
+ ifname ? per : "", "", ifname ? "." : "");
}
static int
-make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family,
- bool unpriv)
+make_sock(struct sockaddr_un *sa, const char *ifname, sa_family_t family)
{
int fd;
- if ((fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0)) == -1)
+ if ((fd = xsocket(AF_UNIX, SOCK_STREAM | SOCK_CLOEXEC, 0)) == -1)
return -1;
memset(sa, 0, sizeof(*sa));
sa->sun_family = AF_UNIX;
- make_path(sa->sun_path, sizeof(sa->sun_path), ifname, family, unpriv);
+ make_path(sa->sun_path, sizeof(sa->sun_path), ifname, family);
return fd;
}
-#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)
+#define SOCK_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH)
static int
-control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family,
- mode_t fmode)
+control_start1(struct dhcpcd_ctx *ctx, const char *ifname, sa_family_t family)
{
struct sockaddr_un sa;
int fd;
socklen_t len;
- fd = make_sock(&sa, ifname, family, (fmode & S_UNPRIV) == S_UNPRIV);
+ fd = make_sock(&sa, ifname, family);
if (fd == -1)
return -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 ||
+ chmod(sa.sun_path, 0666) == -1 ||
(ctx->control_group &&
chown(sa.sun_path, geteuid(), ctx->control_group) == -1) ||
- listen(fd, sizeof(ctx->control_fds)) == -1) {
- close(fd);
- unlink(sa.sun_path);
- return -1;
- }
+ listen(fd, sizeof(ctx->control_fds)) == -1)
+ goto err;
#ifdef PRIVSEP_RIGHTS
- if (IN_PRIVSEP(ctx) && ps_rights_limit_fd_fctnl(fd) == -1) {
- close(fd);
- unlink(sa.sun_path);
- return -1;
- }
+ if (IN_PRIVSEP(ctx) && ps_rights_limit_fd_fctnl(fd) == -1)
+ goto err;
#endif
- if ((fmode & S_UNPRIV) == S_UNPRIV)
- strlcpy(ctx->control_sock_unpriv, sa.sun_path,
- sizeof(ctx->control_sock_unpriv));
- else
- strlcpy(ctx->control_sock, sa.sun_path,
- sizeof(ctx->control_sock));
+ strlcpy(ctx->control_sock, sa.sun_path, sizeof(ctx->control_sock));
return fd;
+
+err:
+ close(fd);
+ unlink(sa.sun_path);
+ return -1;
}
int
#ifdef PRIVSEP
if (IN_PRIVSEP_SE(ctx)) {
make_path(ctx->control_sock, sizeof(ctx->control_sock), ifname,
- family, false);
- make_path(ctx->control_sock_unpriv,
- sizeof(ctx->control_sock_unpriv), ifname, family, true);
+ family);
return 0;
}
#endif
- if ((fd = control_start1(ctx, ifname, family, S_PRIV)) == -1)
+ if ((fd = control_start1(ctx, ifname, family)) == -1)
return -1;
ctx->control_fd = fd;
-1)
logerr("%s: eloop_event_add", __func__);
- if ((fd = control_start1(ctx, ifname, family, S_UNPRIV)) != -1) {
- ctx->control_unpriv_fd = fd;
- if (eloop_event_add(ctx->eloop, fd, ELE_READ,
- control_handle_unpriv, ctx) == -1)
- logerr("%s: eloop_event_add", __func__);
- }
return ctx->control_fd;
}
if (ctx->control_sock[0] != '\0' &&
ps_root_unlink(ctx, ctx->control_sock) == -1)
retval = -1;
- if (ctx->control_sock_unpriv[0] != '\0' &&
- ps_root_unlink(ctx, ctx->control_sock_unpriv) == -1)
- retval = -1;
return retval;
} else if (ctx->options & DHCPCD_FORKED)
return retval;
retval = -1;
}
- if (ctx->control_unpriv_fd != -1) {
- eloop_event_delete(ctx->eloop, ctx->control_unpriv_fd);
- close(ctx->control_unpriv_fd);
- ctx->control_unpriv_fd = -1;
- if (control_unlink(ctx, ctx->control_sock_unpriv) == -1)
- retval = -1;
- }
-
return retval;
}
int
-control_open(const char *ifname, sa_family_t family, bool unpriv)
+control_open(const char *ifname, sa_family_t family)
{
struct sockaddr_un sa;
int fd;
- if ((fd = make_sock(&sa, ifname, family, unpriv)) != -1) {
+ if ((fd = make_sock(&sa, ifname, family)) != -1) {
socklen_t len;
len = (socklen_t)SUN_LEN(&sa);
return eloop_event_add(fd->ctx->eloop, fd->fd, events,
control_handle_data, fd);
}
+
+int
+control_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid)
+{
+ gid_t *groups = NULL, *gp;
+ struct passwd *pw;
+ int ngroups = 10, err = -1;
+
+ pw = getpwuid(uid);
+ if (pw == NULL)
+ return -1;
+
+ groups = reallocarray(groups, (size_t)ngroups, sizeof(*groups));
+ if (groups == NULL)
+ return -1;
+
+ if (getgrouplist(pw->pw_name, gid, groups, &ngroups) == -1) {
+ gp = reallocarray(groups, (size_t)ngroups, sizeof(*groups));
+ if (gp == NULL)
+ goto out;
+ groups = gp;
+ if (getgrouplist(pw->pw_name, gid, groups, &ngroups) == -1)
+ goto out;
+ }
+
+ for (gp = groups; ngroups != 0; ngroups--, gp++) {
+ if (*gp == ctx->control_group) {
+ err = 1;
+ goto out;
+ }
+ }
+ err = 0;
+
+out:
+ free(groups);
+ return err;
+}
TAILQ_HEAD(fd_list_head, fd_list);
#define FD_LISTEN 0x01U
-#define FD_UNPRIV 0x02U
+#define FD_PRIV 0x02U
#define FD_SENDLEN 0x04U
int control_start(struct dhcpcd_ctx *, const char *, sa_family_t);
int control_stop(struct dhcpcd_ctx *);
-int control_open(const char *, sa_family_t, bool);
+int control_open(const char *, sa_family_t);
ssize_t control_send(struct dhcpcd_ctx *, int, char *const *);
+struct fd_list *control_find(struct dhcpcd_ctx *, int);
struct fd_list *control_new(struct dhcpcd_ctx *, int, unsigned int);
void control_free(struct fd_list *);
void control_delete(struct fd_list *);
int control_queue(struct fd_list *, void *, size_t);
-int control_recvdata(struct fd_list *fd, char *, size_t);
+int control_recvmsg(struct fd_list *, struct msghdr *, size_t);
+int control_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid);
#endif
.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
.\" SUCH DAMAGE.
.\"
-.Dd May 8, 2025
+.Dd July 5, 2026
.Dt DHCPCD 8
.Os
.Sh NAME
.Ar interface .
.It Pa @RUNDIR@/sock
Control socket to the manager daemon.
-.It Pa @RUNDIR@/unpriv.sock
-Unprivileged socket to the manager daemon, only allows state retrieval.
.It Pa @RUNDIR@/ Ns Ar interface Ns .sock
Control socket to per interface daemon.
-.It Pa @RUNDIR@/ Ns Ar interface Ns .unpriv.sock
-Unprivileged socket to per interface daemon, only allows state retrieval.
.El
.Sh SEE ALSO
.Xr fnmatch 3 ,
}
/* Only privileged users can control dhcpcd via the socket. */
- if (fd->flags & FD_UNPRIV) {
+ if (!(fd->flags & FD_PRIV)) {
errno = EPERM;
return -1;
}
ifo = NULL;
ctx.cffile = CONFIG;
ctx.script = UNCONST(dhcpcd_default_script);
- ctx.control_fd = ctx.control_unpriv_fd = ctx.link_fd = -1;
+ ctx.control_fd = ctx.link_fd = -1;
ctx.pf_inet_fd = -1;
#ifdef PF_LINK
ctx.pf_link_fd = -1;
if (!(ctx.options & DHCPCD_TEST)) {
ctx.options |= DHCPCD_FORKED; /* avoid socket unlink */
if (!(ctx.options & DHCPCD_MANAGER))
- ctx.control_fd = control_open(argv[optind], family,
- ctx.options & DHCPCD_DUMPLEASE);
+ ctx.control_fd = control_open(argv[optind], family);
if (!(ctx.options & DHCPCD_MANAGER) && ctx.control_fd == -1)
- ctx.control_fd = control_open(argv[optind], AF_UNSPEC,
- ctx.options & DHCPCD_DUMPLEASE);
+ ctx.control_fd = control_open(argv[optind], AF_UNSPEC);
if (ctx.control_fd == -1)
- ctx.control_fd = control_open(NULL, AF_UNSPEC,
- ctx.options & DHCPCD_DUMPLEASE);
+ ctx.control_fd = control_open(NULL, AF_UNSPEC);
if (ctx.control_fd != -1) {
-#ifdef PRIVSEP
- if (IN_PRIVSEP(&ctx) &&
- ps_managersandbox(&ctx, NULL) == -1)
- goto exit_failure;
-#endif
if (!(ctx.options & DHCPCD_DUMPLEASE))
loginfox("sending commands to dhcpcd process");
len = control_send(&ctx, argc, argv);
logdebugx("spawned manager process on PID %d", (int)getpid());
start_manager:
+
+ logdebugx("spawned manager process on PID %d", (int)getpid());
ctx.options |= DHCPCD_STARTED;
if ((pid = pidfile_lock(ctx.pidfile)) != 0) {
logerr("%s: pidfile_lock %d", __func__, (int)pid);
size_t io_buflen;
int control_fd;
- int control_unpriv_fd;
struct fd_list_head control_fds;
char control_sock[sizeof(CONTROLSOCKET) + IF_NAMESIZE];
- char control_sock_unpriv[sizeof(CONTROLSOCKET) + IF_NAMESIZE + 7];
gid_t control_group;
/* DHCP Enterprise options, RFC3925 */
struct ps_process *ps_root;
struct ps_process *ps_inet;
struct ps_process *ps_ctl;
- int ps_data_fd; /* data returned from processes */
- int ps_log_fd; /* chroot logging */
- int ps_log_root_fd; /* outside chroot log reader */
- struct fd_list *ps_control; /* Queue for the above */
- struct fd_list *ps_control_client; /* Queue for the above */
- void *ps_buf; /* IPC buffer */
- size_t ps_buflen; /* IPC buffer length */
+ int ps_data_fd; /* data returned from processes */
+ int ps_log_fd; /* chroot logging */
+ int ps_log_root_fd; /* outside chroot log reader */
+ struct fd_list *ps_control; /* Queue for the above */
+ void *ps_buf; /* IPC buffer */
+ size_t ps_buflen; /* IPC buffer length */
#endif
#ifdef INET
#include "logerr.h"
#include "privsep.h"
+#define PS_CTL_FD(ctx) (ctx)->ps_ctl->psp_fd
+#define PS_CTL_FLAGS_PRIV ~(~0UL >> 1)
+
/* We expect to have open 2 privsep STREAM, 2 STREAM and 2 file STREAM fds */
static int
fd->flags |= FD_LISTEN;
return 0;
}
-
- if (fd->ctx->ps_control_client != NULL &&
- fd->ctx->ps_control_client != fd) {
- logerrx("%s: cannot handle another client", __func__);
- return 0;
- }
return 1;
}
ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
{
struct dhcpcd_ctx *ctx = arg;
- struct iovec *iov = msg->msg_iov;
struct fd_list *fd;
unsigned int fd_flags = FD_SENDLEN;
int err;
- switch (psm->ps_flags) {
- case PS_CTL_PRIV:
- break;
- case PS_CTL_UNPRIV:
- fd_flags |= FD_UNPRIV;
- break;
- }
-
switch (psm->ps_cmd) {
+ case PS_CTL_PRIV:
+ fd_flags |= FD_PRIV; /* FALLTHROUGH */
case PS_CTL:
if (msg->msg_iovlen != 1) {
errno = EINVAL;
return -1;
}
- if (ctx->ps_control_client != NULL) {
- logerrx("%s: cannot handle another client", __func__);
- return 0;
- }
fd = control_new(ctx, ctx->ps_ctl->psp_work_fd, fd_flags);
if (fd == NULL)
return -1;
- ctx->ps_control_client = fd;
- err = control_recvdata(fd, iov->iov_base, iov->iov_len);
- if (err == -1 || err == 0) {
+ err = control_recvmsg(fd, msg, psm->ps_datalen);
+ if (err == -1 || err == 0)
control_free(fd);
- ctx->ps_control_client = NULL;
- }
- break;
- case PS_CTL_EOF:
- ctx->ps_control_client = NULL;
break;
default:
errno = ENOTSUP;
static void
ps_ctl_recv(void *arg, unsigned short events)
{
- struct dhcpcd_ctx *ctx = arg;
char buf[BUFSIZ];
+ struct dhcpcd_ctx *ctx = arg;
ssize_t len;
+ struct fd_list *fd;
- if (!(events & (ELE_READ | ELE_HANGUP)))
+ if (events & ELE_HANGUP) {
+ hangup:
+ eloop_exit(ctx->eloop, EXIT_SUCCESS);
+ return;
+ }
+
+ if (!(events & ELE_READ)) {
logerrx("%s: unexpected event 0x%04x", __func__, events);
+ return;
+ }
- if (events & ELE_READ) {
- len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf));
- if (len == -1)
- logerr("%s: read", __func__);
- else if (len == 0)
- // FIXME: Why does this happen?
- ;
- else if (ctx->ps_control_client == NULL)
- logerrx("%s: clientfd #%d disconnected (len=%zd)",
- __func__, ctx->ps_ctl->psp_work_fd, len);
- else {
- errno = 0;
- if (control_queue(ctx->ps_control_client, buf,
- (size_t)len) == -1)
- logerr("%s: control_queue", __func__);
- }
+ len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf));
+ if (len == -1) {
+ logerr("%s: read", __func__);
+ return;
+ } else if (len == 0)
+ goto hangup;
+
+ TAILQ_FOREACH(fd, &ctx->control_fds, next) {
+ if (fd->flags & FD_LISTEN)
+ continue;
+ if (control_queue(fd, buf, (size_t)len) == -1)
+ logerr("%s: control_queue", __func__);
}
}
ssize_t len;
struct fd_list *fd;
+ if (events & ELE_HANGUP) {
+ hangup:
+ eloop_exit(ctx->eloop, EXIT_SUCCESS);
+ return;
+ }
+
if (!(events & ELE_READ))
logerrx("%s: unexpected event 0x%04x", __func__, events);
eloop_exit(ctx->eloop, EXIT_FAILURE);
return;
}
+ if (len == 0)
+ goto hangup;
/* Send to our listeners */
TAILQ_FOREACH(fd, &ctx->control_fds, next) {
- if (!(fd->flags & FD_LISTEN))
+ if (fd == ctx->ps_control || !(fd->flags & FD_LISTEN))
continue;
if (control_queue(fd, buf, (size_t)len) == -1)
logerr("%s: control_queue", __func__);
ctx) == -1)
return -1;
- ctx->ps_control = control_new(ctx, listen_fd[1], 0);
+ ctx->ps_control = control_new(ctx, listen_fd[1], FD_LISTEN);
if (ctx->ps_control == NULL)
return -1;
if (eloop_event_add(ctx->eloop, ctx->ps_control->fd, ELE_READ,
}
ssize_t
-ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len)
-{
- struct dhcpcd_ctx *ctx = fd->ctx;
-
- if (ctx->ps_control_client != NULL && ctx->ps_control_client != fd)
- logerrx("%s: cannot deal with another client", __func__);
- ctx->ps_control_client = fd;
- return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL,
- fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV, data, len);
-}
-
-ssize_t
-ps_ctl_sendeof(struct fd_list *fd)
+ps_ctl_sendmsg(struct fd_list *fd, const struct msghdr *msg)
{
struct dhcpcd_ctx *ctx = fd->ctx;
+ uint16_t cmd = fd->flags & FD_PRIV ? PS_CTL_PRIV : PS_CTL;
+ unsigned long flags = (unsigned long)fd->fd;
- return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL_EOF, 0, NULL, 0);
+ return ps_sendmsg(ctx, PS_CTL_FD(ctx), cmd, flags, msg);
}
pid_t ps_ctl_start(struct dhcpcd_ctx *);
int ps_ctl_stop(struct dhcpcd_ctx *);
ssize_t ps_ctl_handleargs(struct fd_list *, char *, size_t);
-ssize_t ps_ctl_sendargs(struct fd_list *, void *, size_t);
-ssize_t ps_ctl_sendeof(struct fd_list *fd);
+ssize_t ps_ctl_sendmsg(struct fd_list *, const struct msghdr *);
+ssize_t ps_ctl_sendeof(struct dhcpcd_ctx *);
#endif
return writefile(file, mode, nc, len - flen);
}
+static ssize_t
+ps_root_douser_ispriv(struct dhcpcd_ctx *ctx, void *data, size_t len)
+{
+ uid_t uid;
+ gid_t gid;
+ uint8_t *p = data;
+
+ if (len != sizeof(uid) + sizeof(gid)) {
+ errno = EINVAL;
+ return -1;
+ }
+
+ p = data;
+ memcpy(&uid, p, sizeof(uid));
+ p += sizeof(uid);
+ memcpy(&gid, p, sizeof(gid));
+
+ return control_user_ispriv(ctx, uid, gid);
+}
+
#ifdef AUTH
static ssize_t
ps_root_monordm(uint64_t *rdm, size_t len)
case PS_LOGREOPEN:
err = logopen(ctx->logfile);
break;
+ case PS_USER_ISPRIV:
+ err = ps_root_douser_ispriv(ctx, data, len);
+ break;
#ifdef AUTH
case PS_AUTH_MONORDM:
err = ps_root_monordm(data, len);
return ps_root_readerror(ctx, NULL, 0);
}
+ssize_t
+ps_root_user_ispriv(struct dhcpcd_ctx *ctx, uid_t uid, gid_t gid)
+{
+ struct iovec iov[] = { {
+ .iov_base = &uid,
+ .iov_len = sizeof(uid),
+ },
+ {
+ .iov_base = &gid,
+ .iov_len = sizeof(gid),
+ } };
+ struct msghdr msg = {
+ .msg_iov = iov,
+ .msg_iovlen = __arraycount(iov),
+ };
+
+ if (ps_sendmsg(ctx, PS_ROOT_FD(ctx), PS_USER_ISPRIV, 0, &msg) == -1) {
+ logerr(__func__);
+ return -1;
+ }
+ return ps_root_readerror(ctx, NULL, 0);
+}
+
#ifdef PRIVSEP_GETHOSTNAME
int
ps_root_gethostname(struct dhcpcd_ctx *ctx, char *hname, size_t hnamelen)
ssize_t ps_root_script(struct dhcpcd_ctx *, const void *, size_t);
ssize_t ps_root_stopprocesses(struct dhcpcd_ctx *);
int ps_root_getauthrdm(struct dhcpcd_ctx *, uint64_t *);
+ssize_t ps_root_user_ispriv(struct dhcpcd_ctx *, uid_t, gid_t);
#ifdef PRIVSEP_GETHOSTNAME
int ps_root_gethostname(struct dhcpcd_ctx *, char *, size_t);
#endif
/* Close things we no longer need */
pidfile_unlock();
- eloop_closefdwaiter(ctx->eloop);
+ if (ctx->ps_ctl != psp)
+ eloop_closefdwaiter(ctx->eloop);
/* Close more if we are not root */
if (ctx->ps_root != psp) {
- ps_root_close(ctx);
+ if (ctx->ps_ctl != psp)
+ ps_root_close(ctx);
#ifdef PLUGIN_DEV
dev_stop(ctx);
#endif
if (ctx->ps_root != psp) {
ctx->options &= ~DHCPCD_PRIVSEPROOT;
- ctx->ps_root = NULL;
if (ctx->ps_log_root_fd != -1) {
/* Already removed from eloop thanks to above clear. */
close(ctx->ps_log_root_fd);
TAILQ_FOREACH_SAFE(psp, &ctx->ps_processes, next, psn) {
if (psp == notthis)
continue;
+ /* control needs root access to work out user group */
+ if (ctx->ps_ctl == notthis && psp == ctx->ps_root)
+ continue;
ps_freeprocess(psp);
}
}
#define PS_FILEMTIME 0x0016
#define PS_AUTH_MONORDM 0x0017
#define PS_CTL 0x0018
-#define PS_CTL_EOF 0x0019
+#define PS_CTL_PRIV 0x0019
#define PS_LOGREOPEN 0x0020
#define PS_STOPPROCS 0x0021
#define PS_DAEMONISED 0x0022
+#define PS_USER_ISPRIV 0x0023
/* Domains */
#define PS_ROOT 0x0101
#define PS_DEV_IFREMOVED 0x0002
#define PS_DEV_IFUPDATED 0x0003
-/* Control Type (via flags) */
-#define PS_CTL_PRIV 0x0004
-#define PS_CTL_UNPRIV 0x0005
-
/* Sysctl Needs (via flags) */
#define PS_SYSCTL_OLEN 0x0001
#define PS_SYSCTL_ODATA 0x0002