xref: /dflybsd-src/contrib/dhcpcd/src/privsep-control.c (revision 6a6d63c5317abf314a78f8c8300ef73c2bc0c39e)
17f8103cdSRoy Marples /* SPDX-License-Identifier: BSD-2-Clause */
27f8103cdSRoy Marples /*
37f8103cdSRoy Marples  * Privilege Separation for dhcpcd, control proxy
480aa9461SRoy Marples  * Copyright (c) 2006-2023 Roy Marples <roy@marples.name>
57f8103cdSRoy Marples  * All rights reserved
67f8103cdSRoy Marples 
77f8103cdSRoy Marples  * Redistribution and use in source and binary forms, with or without
87f8103cdSRoy Marples  * modification, are permitted provided that the following conditions
97f8103cdSRoy Marples  * are met:
107f8103cdSRoy Marples  * 1. Redistributions of source code must retain the above copyright
117f8103cdSRoy Marples  *    notice, this list of conditions and the following disclaimer.
127f8103cdSRoy Marples  * 2. Redistributions in binary form must reproduce the above copyright
137f8103cdSRoy Marples  *    notice, this list of conditions and the following disclaimer in the
147f8103cdSRoy Marples  *    documentation and/or other materials provided with the distribution.
157f8103cdSRoy Marples  *
167f8103cdSRoy Marples  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
177f8103cdSRoy Marples  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
187f8103cdSRoy Marples  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
197f8103cdSRoy Marples  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
207f8103cdSRoy Marples  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
217f8103cdSRoy Marples  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
227f8103cdSRoy Marples  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
237f8103cdSRoy Marples  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
247f8103cdSRoy Marples  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
257f8103cdSRoy Marples  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
267f8103cdSRoy Marples  * SUCH DAMAGE.
277f8103cdSRoy Marples  */
287f8103cdSRoy Marples 
297f8103cdSRoy Marples #include <errno.h>
307f8103cdSRoy Marples #include <stdlib.h>
317f8103cdSRoy Marples #include <string.h>
327f8103cdSRoy Marples 
337f8103cdSRoy Marples #include "dhcpcd.h"
347f8103cdSRoy Marples #include "control.h"
357f8103cdSRoy Marples #include "eloop.h"
367f8103cdSRoy Marples #include "logerr.h"
377f8103cdSRoy Marples #include "privsep.h"
387f8103cdSRoy Marples 
39*54175cefSRoy Marples /* We expect to have open 2 SEQPACKET, 2 STREAM and 2 file STREAM fds */
40*54175cefSRoy Marples 
417f8103cdSRoy Marples static int
ps_ctl_startcb(struct ps_process * psp)4280aa9461SRoy Marples ps_ctl_startcb(struct ps_process *psp)
437f8103cdSRoy Marples {
4480aa9461SRoy Marples 	struct dhcpcd_ctx *ctx = psp->psp_ctx;
457f8103cdSRoy Marples 	sa_family_t af;
467f8103cdSRoy Marples 
470a68f8d2SRoy Marples 	if (ctx->options & DHCPCD_MANAGER) {
487f8103cdSRoy Marples 		setproctitle("[control proxy]");
497f8103cdSRoy Marples 		af = AF_UNSPEC;
507f8103cdSRoy Marples 	} else {
517f8103cdSRoy Marples 		setproctitle("[control proxy] %s%s%s",
527f8103cdSRoy Marples 		    ctx->ifv[0],
537f8103cdSRoy Marples 		    ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
547f8103cdSRoy Marples 		    ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
557f8103cdSRoy Marples 		if ((ctx->options &
567f8103cdSRoy Marples 		    (DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV4)
577f8103cdSRoy Marples 			af = AF_INET;
587f8103cdSRoy Marples 		else if ((ctx->options &
597f8103cdSRoy Marples 		    (DHCPCD_IPV4 | DHCPCD_IPV6)) == DHCPCD_IPV6)
607f8103cdSRoy Marples 			af = AF_INET6;
617f8103cdSRoy Marples 		else
627f8103cdSRoy Marples 			af = AF_UNSPEC;
637f8103cdSRoy Marples 	}
647f8103cdSRoy Marples 
657f8103cdSRoy Marples 	return control_start(ctx,
660a68f8d2SRoy Marples 	    ctx->options & DHCPCD_MANAGER ? NULL : *ctx->ifv, af);
677f8103cdSRoy Marples }
687f8103cdSRoy Marples 
697f8103cdSRoy Marples static void
ps_ctl_recvmsg(void * arg,unsigned short events)7080aa9461SRoy Marples ps_ctl_recvmsg(void *arg, unsigned short events)
717f8103cdSRoy Marples {
7280aa9461SRoy Marples 	struct ps_process *psp = arg;
737f8103cdSRoy Marples 
74a85d0907SRoy Marples 	if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events, NULL, NULL) == -1)
757f8103cdSRoy Marples 		logerr(__func__);
767f8103cdSRoy Marples }
777f8103cdSRoy Marples 
787f8103cdSRoy Marples ssize_t
ps_ctl_handleargs(struct fd_list * fd,char * data,size_t len)797f8103cdSRoy Marples ps_ctl_handleargs(struct fd_list *fd, char *data, size_t len)
807f8103cdSRoy Marples {
817f8103cdSRoy Marples 
827f8103cdSRoy Marples 	/* Make any change here in dhcpcd.c as well. */
837f8103cdSRoy Marples 	if (strncmp(data, "--version",
847f8103cdSRoy Marples 	    MIN(strlen("--version"), len)) == 0) {
857f8103cdSRoy Marples 		return control_queue(fd, UNCONST(VERSION),
867f8103cdSRoy Marples 		    strlen(VERSION) + 1);
877f8103cdSRoy Marples 	} else if (strncmp(data, "--getconfigfile",
887f8103cdSRoy Marples 	    MIN(strlen("--getconfigfile"), len)) == 0) {
897f8103cdSRoy Marples 		return control_queue(fd, UNCONST(fd->ctx->cffile),
907f8103cdSRoy Marples 		    strlen(fd->ctx->cffile) + 1);
917f8103cdSRoy Marples 	} else if (strncmp(data, "--listen",
927f8103cdSRoy Marples 	    MIN(strlen("--listen"), len)) == 0) {
937f8103cdSRoy Marples 		fd->flags |= FD_LISTEN;
947f8103cdSRoy Marples 		return 0;
957f8103cdSRoy Marples 	}
967f8103cdSRoy Marples 
977f8103cdSRoy Marples 	if (fd->ctx->ps_control_client != NULL &&
987f8103cdSRoy Marples 	    fd->ctx->ps_control_client != fd)
997f8103cdSRoy Marples 	{
1007f8103cdSRoy Marples 		logerrx("%s: cannot handle another client", __func__);
1017f8103cdSRoy Marples 		return 0;
1027f8103cdSRoy Marples 	}
1037f8103cdSRoy Marples 	return 1;
1047f8103cdSRoy Marples }
1057f8103cdSRoy Marples 
1067f8103cdSRoy Marples static ssize_t
ps_ctl_dispatch(void * arg,struct ps_msghdr * psm,struct msghdr * msg)1077f8103cdSRoy Marples ps_ctl_dispatch(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
1087f8103cdSRoy Marples {
1097f8103cdSRoy Marples 	struct dhcpcd_ctx *ctx = arg;
1107f8103cdSRoy Marples 	struct iovec *iov = msg->msg_iov;
1117f8103cdSRoy Marples 	struct fd_list *fd;
1127f8103cdSRoy Marples 	unsigned int fd_flags = FD_SENDLEN;
1137f8103cdSRoy Marples 
1147f8103cdSRoy Marples 	switch (psm->ps_flags) {
1157f8103cdSRoy Marples 	case PS_CTL_PRIV:
1167f8103cdSRoy Marples 		break;
1177f8103cdSRoy Marples 	case PS_CTL_UNPRIV:
1187f8103cdSRoy Marples 		fd_flags |= FD_UNPRIV;
1197f8103cdSRoy Marples 		break;
1207f8103cdSRoy Marples 	}
1217f8103cdSRoy Marples 
1227f8103cdSRoy Marples 	switch (psm->ps_cmd) {
1237f8103cdSRoy Marples 	case PS_CTL:
1247f8103cdSRoy Marples 		if (msg->msg_iovlen != 1) {
1257f8103cdSRoy Marples 			errno = EINVAL;
1267f8103cdSRoy Marples 			return -1;
1277f8103cdSRoy Marples 		}
1287f8103cdSRoy Marples 		if (ctx->ps_control_client != NULL) {
1297f8103cdSRoy Marples 			logerrx("%s: cannot handle another client", __func__);
1307f8103cdSRoy Marples 			return 0;
1317f8103cdSRoy Marples 		}
13280aa9461SRoy Marples 		fd = control_new(ctx, ctx->ps_ctl->psp_work_fd, fd_flags);
1337f8103cdSRoy Marples 		if (fd == NULL)
1347f8103cdSRoy Marples 			return -1;
1357f8103cdSRoy Marples 		ctx->ps_control_client = fd;
1367f8103cdSRoy Marples 		control_recvdata(fd, iov->iov_base, iov->iov_len);
1377f8103cdSRoy Marples 		break;
1387f8103cdSRoy Marples 	case PS_CTL_EOF:
13980aa9461SRoy Marples 		ctx->ps_control_client = NULL;
1407f8103cdSRoy Marples 		break;
1417f8103cdSRoy Marples 	default:
1427f8103cdSRoy Marples 		errno = ENOTSUP;
1437f8103cdSRoy Marples 		return -1;
1447f8103cdSRoy Marples 	}
1457f8103cdSRoy Marples 	return 0;
1467f8103cdSRoy Marples }
1477f8103cdSRoy Marples 
1487f8103cdSRoy Marples static void
ps_ctl_dodispatch(void * arg,unsigned short events)14980aa9461SRoy Marples ps_ctl_dodispatch(void *arg, unsigned short events)
1507f8103cdSRoy Marples {
15180aa9461SRoy Marples 	struct ps_process *psp = arg;
1527f8103cdSRoy Marples 
15380aa9461SRoy Marples 	if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
15480aa9461SRoy Marples 	    ps_ctl_dispatch, psp->psp_ctx) == -1)
1557f8103cdSRoy Marples 		logerr(__func__);
1567f8103cdSRoy Marples }
1577f8103cdSRoy Marples 
1587f8103cdSRoy Marples static void
ps_ctl_recv(void * arg,unsigned short events)15980aa9461SRoy Marples ps_ctl_recv(void *arg, unsigned short events)
1607f8103cdSRoy Marples {
1617f8103cdSRoy Marples 	struct dhcpcd_ctx *ctx = arg;
1627f8103cdSRoy Marples 	char buf[BUFSIZ];
1637f8103cdSRoy Marples 	ssize_t len;
1647f8103cdSRoy Marples 
165f3744ac9SRoy Marples 	if (!(events & (ELE_READ | ELE_HANGUP)))
16680aa9461SRoy Marples 		logerrx("%s: unexpected event 0x%04x", __func__, events);
16780aa9461SRoy Marples 
168f3744ac9SRoy Marples 	if (events & ELE_READ) {
16980aa9461SRoy Marples 		len = read(ctx->ps_ctl->psp_work_fd, buf, sizeof(buf));
170f3744ac9SRoy Marples 		if (len == -1)
1717f8103cdSRoy Marples 			logerr("%s: read", __func__);
172f3744ac9SRoy Marples 		else if (len == 0)
173f3744ac9SRoy Marples 			// FIXME: Why does this happen?
174f3744ac9SRoy Marples 			;
175f3744ac9SRoy Marples 		else if (ctx->ps_control_client == NULL)
176f3744ac9SRoy Marples 			logerrx("%s: clientfd #%d disconnected (len=%zd)",
177f3744ac9SRoy Marples 			    __func__, ctx->ps_ctl->psp_work_fd, len);
178f3744ac9SRoy Marples 		else {
1797f8103cdSRoy Marples 			errno = 0;
180f3744ac9SRoy Marples 			if (control_queue(ctx->ps_control_client,
181f3744ac9SRoy Marples 			    buf, (size_t)len) == -1)
1827f8103cdSRoy Marples 				logerr("%s: control_queue", __func__);
1837f8103cdSRoy Marples 		}
184f3744ac9SRoy Marples 	}
185f3744ac9SRoy Marples }
1867f8103cdSRoy Marples 
1877f8103cdSRoy Marples static void
ps_ctl_listen(void * arg,unsigned short events)18880aa9461SRoy Marples ps_ctl_listen(void *arg, unsigned short events)
1897f8103cdSRoy Marples {
1907f8103cdSRoy Marples 	struct dhcpcd_ctx *ctx = arg;
1917f8103cdSRoy Marples 	char buf[BUFSIZ];
1927f8103cdSRoy Marples 	ssize_t len;
1937f8103cdSRoy Marples 	struct fd_list *fd;
1947f8103cdSRoy Marples 
19580aa9461SRoy Marples 	if (!(events & ELE_READ))
19680aa9461SRoy Marples 		logerrx("%s: unexpected event 0x%04x", __func__, events);
19780aa9461SRoy Marples 
1987f8103cdSRoy Marples 	len = read(ctx->ps_control->fd, buf, sizeof(buf));
19980aa9461SRoy Marples 	if (len == -1) {
2007f8103cdSRoy Marples 		logerr("%s: read", __func__);
2017f8103cdSRoy Marples 		eloop_exit(ctx->eloop, EXIT_FAILURE);
2027f8103cdSRoy Marples 		return;
2037f8103cdSRoy Marples 	}
2047f8103cdSRoy Marples 
2057f8103cdSRoy Marples 	/* Send to our listeners */
2067f8103cdSRoy Marples 	TAILQ_FOREACH(fd, &ctx->control_fds, next) {
2077f8103cdSRoy Marples 		if (!(fd->flags & FD_LISTEN))
2087f8103cdSRoy Marples 			continue;
2097f8103cdSRoy Marples 		if (control_queue(fd, buf, (size_t)len)== -1)
2107f8103cdSRoy Marples 			logerr("%s: control_queue", __func__);
2117f8103cdSRoy Marples 	}
2127f8103cdSRoy Marples }
2137f8103cdSRoy Marples 
2147f8103cdSRoy Marples pid_t
ps_ctl_start(struct dhcpcd_ctx * ctx)2157f8103cdSRoy Marples ps_ctl_start(struct dhcpcd_ctx *ctx)
2167f8103cdSRoy Marples {
21780aa9461SRoy Marples 	struct ps_id id = {
21880aa9461SRoy Marples 		.psi_ifindex = 0,
21980aa9461SRoy Marples 		.psi_cmd = PS_CTL,
22080aa9461SRoy Marples 	};
22180aa9461SRoy Marples 	struct ps_process *psp;
222*54175cefSRoy Marples 	int work_fd[2], listen_fd[2];
2237f8103cdSRoy Marples 	pid_t pid;
2247f8103cdSRoy Marples 
225*54175cefSRoy Marples 	if_closesockets(ctx);
226*54175cefSRoy Marples 
227*54175cefSRoy Marples 	if (xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, work_fd) == -1 ||
228b8b69544SRoy Marples 	    xsocketpair(AF_UNIX, SOCK_STREAM | SOCK_CXNB, 0, listen_fd) == -1)
2297f8103cdSRoy Marples 		return -1;
2307f8103cdSRoy Marples #ifdef PRIVSEP_RIGHTS
231*54175cefSRoy Marples 	if (ps_rights_limit_fdpair(work_fd) == -1 ||
232b8b69544SRoy Marples 	    ps_rights_limit_fdpair(listen_fd) == -1)
2337f8103cdSRoy Marples 		return -1;
2347f8103cdSRoy Marples #endif
2357f8103cdSRoy Marples 
23680aa9461SRoy Marples 	psp = ctx->ps_ctl = ps_newprocess(ctx, &id);
23780aa9461SRoy Marples 	strlcpy(psp->psp_name, "control proxy", sizeof(psp->psp_name));
23880aa9461SRoy Marples 	pid = ps_startprocess(psp, ps_ctl_recvmsg, ps_ctl_dodispatch,
23980aa9461SRoy Marples 	    ps_ctl_startcb, NULL, PSF_DROPPRIVS);
2407f8103cdSRoy Marples 
2417f8103cdSRoy Marples 	if (pid == -1)
2427f8103cdSRoy Marples 		return -1;
2437f8103cdSRoy Marples 	else if (pid != 0) {
244*54175cefSRoy Marples 		psp->psp_work_fd = work_fd[0];
245*54175cefSRoy Marples 		close(work_fd[1]);
246*54175cefSRoy Marples 		close(listen_fd[1]);
2477f8103cdSRoy Marples 		ctx->ps_control = control_new(ctx,
248*54175cefSRoy Marples 		    listen_fd[0], FD_SENDLEN | FD_LISTEN);
2497f8103cdSRoy Marples 		if (ctx->ps_control == NULL)
2507f8103cdSRoy Marples 			return -1;
2517f8103cdSRoy Marples 		return pid;
2527f8103cdSRoy Marples 	}
2537f8103cdSRoy Marples 
254*54175cefSRoy Marples 	close(work_fd[0]);
255*54175cefSRoy Marples 	close(listen_fd[0]);
256*54175cefSRoy Marples 
257*54175cefSRoy Marples 	psp->psp_work_fd = work_fd[1];
25880aa9461SRoy Marples 	if (eloop_event_add(ctx->eloop, psp->psp_work_fd, ELE_READ,
2597f8103cdSRoy Marples 	    ps_ctl_recv, ctx) == -1)
2607f8103cdSRoy Marples 		return -1;
2617f8103cdSRoy Marples 
262*54175cefSRoy Marples 	ctx->ps_control = control_new(ctx, listen_fd[1], 0);
2637f8103cdSRoy Marples 	if (ctx->ps_control == NULL)
2647f8103cdSRoy Marples 		return -1;
26580aa9461SRoy Marples 	if (eloop_event_add(ctx->eloop, ctx->ps_control->fd, ELE_READ,
2667f8103cdSRoy Marples 	    ps_ctl_listen, ctx) == -1)
2677f8103cdSRoy Marples 		return -1;
2687f8103cdSRoy Marples 
269a0d9933aSRoy Marples 	ps_entersandbox("stdio inet", NULL);
2707f8103cdSRoy Marples 	return 0;
2717f8103cdSRoy Marples }
2727f8103cdSRoy Marples 
2737f8103cdSRoy Marples int
ps_ctl_stop(struct dhcpcd_ctx * ctx)2747f8103cdSRoy Marples ps_ctl_stop(struct dhcpcd_ctx *ctx)
2757f8103cdSRoy Marples {
2767f8103cdSRoy Marples 
27780aa9461SRoy Marples 	return ps_stopprocess(ctx->ps_ctl);
2787f8103cdSRoy Marples }
2797f8103cdSRoy Marples 
2807f8103cdSRoy Marples ssize_t
ps_ctl_sendargs(struct fd_list * fd,void * data,size_t len)2817f8103cdSRoy Marples ps_ctl_sendargs(struct fd_list *fd, void *data, size_t len)
2827f8103cdSRoy Marples {
2837f8103cdSRoy Marples 	struct dhcpcd_ctx *ctx = fd->ctx;
2847f8103cdSRoy Marples 
2857f8103cdSRoy Marples 	if (ctx->ps_control_client != NULL && ctx->ps_control_client != fd)
2867f8103cdSRoy Marples 		logerrx("%s: cannot deal with another client", __func__);
2877f8103cdSRoy Marples 	ctx->ps_control_client = fd;
28880aa9461SRoy Marples 	return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL,
2897f8103cdSRoy Marples 	    fd->flags & FD_UNPRIV ? PS_CTL_UNPRIV : PS_CTL_PRIV,
2907f8103cdSRoy Marples 	    data, len);
2917f8103cdSRoy Marples }
2927f8103cdSRoy Marples 
2937f8103cdSRoy Marples ssize_t
ps_ctl_sendeof(struct fd_list * fd)2947f8103cdSRoy Marples ps_ctl_sendeof(struct fd_list *fd)
2957f8103cdSRoy Marples {
2967f8103cdSRoy Marples 	struct dhcpcd_ctx *ctx = fd->ctx;
2977f8103cdSRoy Marples 
29880aa9461SRoy Marples 	return ps_sendcmd(ctx, ctx->ps_ctl->psp_fd, PS_CTL_EOF, 0, NULL, 0);
2997f8103cdSRoy Marples }
300