Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
279 changes: 178 additions & 101 deletions src/control.c

Large diffs are not rendered by default.

8 changes: 5 additions & 3 deletions src/control.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,18 @@ struct fd_list {
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
6 changes: 1 addition & 5 deletions src/dhcpcd.8.in
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
.\" 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
Expand Down Expand Up @@ -861,12 +861,8 @@ running on the
.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 ,
Expand Down
20 changes: 7 additions & 13 deletions src/dhcpcd.c
Original file line number Diff line number Diff line change
Expand Up @@ -1704,7 +1704,7 @@ dhcpcd_handleargs(struct dhcpcd_ctx *ctx, struct fd_list *fd, int argc,
}

/* Only privileged users can control dhcpcd via the socket. */
if (fd->flags & FD_UNPRIV) {
if (!(fd->flags & FD_PRIV)) {
errno = EPERM;
return -1;
}
Expand Down Expand Up @@ -2078,7 +2078,7 @@ main(int argc, char **argv, char **envp)
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;
Expand Down Expand Up @@ -2421,20 +2421,12 @@ main(int argc, char **argv, char **envp)
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);
Expand Down Expand Up @@ -2580,6 +2572,8 @@ main(int argc, char **argv, char **envp)
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);
Expand Down
15 changes: 6 additions & 9 deletions src/dhcpcd.h
Original file line number Diff line number Diff line change
Expand Up @@ -174,10 +174,8 @@ struct dhcpcd_ctx {
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 */
Expand All @@ -199,13 +197,12 @@ struct dhcpcd_ctx {
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
Expand Down
104 changes: 43 additions & 61 deletions src/privsep-control.c
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,8 @@
#include "logerr.h"
#include "privsep.h"

#define PS_CTL_FD(ctx) (ctx)->ps_ctl->psp_fd

/* We expect to have open 2 privsep STREAM, 2 STREAM and 2 file STREAM fds */

static int
Expand Down Expand Up @@ -93,54 +95,31 @@ ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len)
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;
}

static ssize_t
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);
Comment thread
coderabbitai[bot] marked this conversation as resolved.
ctx->ps_control_client = NULL;
}
break;
case PS_CTL_EOF:
ctx->ps_control_client = NULL;
break;
default:
errno = ENOTSUP;
Expand All @@ -162,29 +141,34 @@ ps_ctl_dodispatch(void *arg, unsigned short events)
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__);
}
}

Expand All @@ -196,6 +180,12 @@ ps_ctl_listen(void *arg, unsigned short events)
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);

Expand All @@ -205,10 +195,12 @@ ps_ctl_listen(void *arg, unsigned short 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__);
Expand Down Expand Up @@ -263,7 +255,7 @@ ps_ctl_start(struct dhcpcd_ctx *ctx)
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,
Expand All @@ -281,21 +273,11 @@ ps_ctl_stop(struct dhcpcd_ctx *ctx)
}

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);
}
4 changes: 2 additions & 2 deletions src/privsep-control.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@
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
46 changes: 46 additions & 0 deletions src/privsep-root.c
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,26 @@ ps_root_dowritefile(const struct dhcpcd_ctx *ctx, mode_t mode, void *data,
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;

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)
Expand Down Expand Up @@ -563,6 +583,9 @@ ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
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);
Expand Down Expand Up @@ -1119,6 +1142,29 @@ ps_root_logreopen(struct dhcpcd_ctx *ctx)
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)
Expand Down
1 change: 1 addition & 0 deletions src/privsep-root.h
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ ssize_t ps_root_logreopen(struct dhcpcd_ctx *);
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
Expand Down
Loading