Home | History | Annotate | Line # | Download | only in src
      1 /* SPDX-License-Identifier: BSD-2-Clause */
      2 /*
      3  * Privilege Separation for dhcpcd, privileged proxy
      4  * Copyright (c) 2006-2025 Roy Marples <roy (at) marples.name>
      5  * All rights reserved
      6 
      7  * Redistribution and use in source and binary forms, with or without
      8  * modification, are permitted provided that the following conditions
      9  * are met:
     10  * 1. Redistributions of source code must retain the above copyright
     11  *    notice, this list of conditions and the following disclaimer.
     12  * 2. Redistributions in binary form must reproduce the above copyright
     13  *    notice, this list of conditions and the following disclaimer in the
     14  *    documentation and/or other materials provided with the distribution.
     15  *
     16  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
     17  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
     18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
     19  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
     20  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
     21  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
     22  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
     23  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
     24  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
     25  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
     26  * SUCH DAMAGE.
     27  */
     28 
     29 #include <sys/ioctl.h>
     30 #include <sys/socket.h>
     31 #include <sys/stat.h>
     32 #include <sys/time.h>
     33 #include <sys/types.h>
     34 #include <sys/wait.h>
     35 
     36 #include <assert.h>
     37 #include <errno.h>
     38 #include <fcntl.h>
     39 #include <pwd.h>
     40 #include <signal.h>
     41 #include <stddef.h>
     42 #include <stdlib.h>
     43 #include <string.h>
     44 #include <unistd.h>
     45 
     46 #include "auth.h"
     47 #include "common.h"
     48 #include "dev.h"
     49 #include "dhcpcd.h"
     50 #include "dhcp6.h"
     51 #include "eloop.h"
     52 #include "if.h"
     53 #include "ipv6nd.h"
     54 #include "logerr.h"
     55 #include "privsep.h"
     56 #include "sa.h"
     57 #include "script.h"
     58 
     59 __CTASSERT(sizeof(ioctl_request_t) <= sizeof(unsigned long));
     60 
     61 struct psr_error
     62 {
     63 	ssize_t psr_result;
     64 	int psr_errno;
     65 	char psr_pad[sizeof(ssize_t) - sizeof(int)];
     66 	size_t psr_datalen;
     67 };
     68 
     69 struct psr_ctx {
     70 	struct dhcpcd_ctx *psr_ctx;
     71 	struct psr_error psr_error;
     72 	size_t psr_datalen;
     73 	void *psr_data;
     74 	bool psr_mallocdata;
     75 };
     76 
     77 static ssize_t
     78 ps_root_readerrorcb(struct psr_ctx *pc)
     79 {
     80 	struct dhcpcd_ctx *ctx = pc->psr_ctx;
     81 	int fd = PS_ROOT_FD(ctx);
     82 	struct psr_error *psr_error = &pc->psr_error;
     83 	struct iovec iov[] = {
     84 		{ .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
     85 		{ .iov_base = pc->psr_data, .iov_len = pc->psr_datalen },
     86 	};
     87 	struct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) };
     88 	ssize_t len;
     89 
     90 #define PSR_ERROR(e)				\
     91 	do {					\
     92 		psr_error->psr_errno = (e);	\
     93 		goto error;			\
     94 	} while (0 /* CONSTCOND */)
     95 
     96 	if (eloop_waitfd(fd) == -1)
     97 		PSR_ERROR(errno);
     98 
     99 	if (!pc->psr_mallocdata)
    100 		goto recv;
    101 
    102 	/* We peek at the psr_error structure to tell us how much of a buffer
    103 	 * we need to read the whole packet. */
    104 	msg.msg_iovlen--;
    105 	len = recvmsg(fd, &msg, MSG_PEEK | MSG_WAITALL);
    106 	if (len == -1)
    107 		PSR_ERROR(errno);
    108 
    109 	/* After this point, we MUST do another recvmsg even on a failure
    110 	 * to remove the message after peeking. */
    111 	if ((size_t)len < sizeof(*psr_error)) {
    112 		/* We can't use the header to work out buffers, so
    113 		 * remove the message and bail. */
    114 		(void)recvmsg(fd, &msg, MSG_WAITALL);
    115 		PSR_ERROR(EINVAL);
    116 	}
    117 
    118 	/* No data to read? Unlikely but ... */
    119 	if (psr_error->psr_datalen == 0)
    120 		goto recv;
    121 
    122 	pc->psr_data = malloc(psr_error->psr_datalen);
    123 	if (pc->psr_data != NULL) {
    124 		iov[1].iov_base = pc->psr_data;
    125 		iov[1].iov_len = psr_error->psr_datalen;
    126 		msg.msg_iovlen++;
    127 	}
    128 
    129 recv:
    130 	len = recvmsg(fd, &msg, MSG_WAITALL);
    131 	if (len == -1)
    132 		PSR_ERROR(errno);
    133 	else if ((size_t)len < sizeof(*psr_error))
    134 		PSR_ERROR(EINVAL);
    135 	else if (msg.msg_flags & MSG_TRUNC)
    136 		PSR_ERROR(ENOBUFS);
    137 	else if ((size_t)len != sizeof(*psr_error) + psr_error->psr_datalen) {
    138 #ifdef PRIVSEP_DEBUG
    139 		logerrx("%s: recvmsg returned %zd, expecting %zu", __func__,
    140 		    len, sizeof(*psr_error) + psr_error->psr_datalen);
    141 #endif
    142 		PSR_ERROR(EBADMSG);
    143 	}
    144 	return len;
    145 
    146 error:
    147 	psr_error->psr_result = -1;
    148 	if (pc->psr_mallocdata && pc->psr_data != NULL) {
    149 		free(pc->psr_data);
    150 		pc->psr_data = NULL;
    151 	}
    152 	return -1;
    153 }
    154 
    155 ssize_t
    156 ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len)
    157 {
    158 	struct psr_ctx pc = {
    159 		.psr_ctx = ctx,
    160 		.psr_data = data,
    161 		.psr_datalen = len,
    162 		.psr_mallocdata = false
    163 	};
    164 
    165 	ps_root_readerrorcb(&pc);
    166 
    167 	errno = pc.psr_error.psr_errno;
    168 	return pc.psr_error.psr_result;
    169 }
    170 
    171 ssize_t
    172 ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len)
    173 {
    174 	struct psr_ctx pc = {
    175 		.psr_ctx = ctx,
    176 		.psr_data = NULL,
    177 		.psr_datalen = 0,
    178 		.psr_mallocdata = true
    179 	};
    180 
    181 	ps_root_readerrorcb(&pc);
    182 
    183 	errno = pc.psr_error.psr_errno;
    184 	*data = pc.psr_data;
    185 	*len = pc.psr_error.psr_datalen;
    186 	return pc.psr_error.psr_result;
    187 }
    188 
    189 static ssize_t
    190 ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result,
    191     void *data, size_t len)
    192 {
    193 	struct psr_error psr = {
    194 		.psr_result = result,
    195 		.psr_errno = errno,
    196 		.psr_datalen = len,
    197 	};
    198 	struct iovec iov[] = {
    199 		{ .iov_base = &psr, .iov_len = sizeof(psr) },
    200 		{ .iov_base = data, .iov_len = len },
    201 	};
    202 	struct msghdr msg = { .msg_iov = iov, .msg_iovlen = __arraycount(iov) };
    203 	ssize_t err;
    204 	int fd = PS_ROOT_FD(ctx);
    205 
    206 #ifdef PRIVSEP_DEBUG
    207 	logdebugx("%s: result %zd errno %d", __func__, result, errno);
    208 #endif
    209 
    210 	if (len == 0)
    211 		msg.msg_iovlen = 1;
    212 	err = sendmsg(fd, &msg, MSG_EOR);
    213 
    214 	/* Error sending the message? Try sending the error of sending. */
    215 	if (err == -1 && errno != EPIPE) {
    216 		logerr("%s: result=%zd, data=%p, len=%zu",
    217 		    __func__, result, data, len);
    218 		psr.psr_result = err;
    219 		psr.psr_errno = errno;
    220 		psr.psr_datalen = 0;
    221 		msg.msg_iovlen = 1;
    222 		err = sendmsg(fd, &msg, MSG_EOR);
    223 	}
    224 
    225 	return err;
    226 }
    227 
    228 static ssize_t
    229 ps_root_doioctl(unsigned long req, void *data, size_t len)
    230 {
    231 	int s, err;
    232 
    233 	/* Only allow these ioctls */
    234 	switch(req) {
    235 #ifdef SIOCAIFADDR
    236 	case SIOCAIFADDR:	/* FALLTHROUGH */
    237 	case SIOCDIFADDR:	/* FALLTHROUGH */
    238 #endif
    239 #ifdef SIOCSIFHWADDR
    240 	case SIOCSIFHWADDR:	/* FALLTHROUGH */
    241 #endif
    242 #ifdef SIOCGIFPRIORITY
    243 	case SIOCGIFPRIORITY:	/* FALLTHROUGH */
    244 #endif
    245 	case SIOCSIFFLAGS:	/* FALLTHROUGH */
    246 	case SIOCGIFMTU:
    247 		break;
    248 	default:
    249 		errno = EPERM;
    250 		return -1;
    251 	}
    252 
    253 	s = xsocket(PF_INET, SOCK_DGRAM, 0);
    254 	if (s != -1)
    255 #ifdef IOCTL_REQUEST_TYPE
    256 	{
    257 		ioctl_request_t reqt;
    258 
    259 		memcpy(&reqt, &req, sizeof(reqt));
    260 		err = ioctl(s, reqt, data, len);
    261 	}
    262 #else
    263 		err = ioctl(s, req, data, len);
    264 #endif
    265 	else
    266 		err = -1;
    267 	if (s != -1)
    268 		close(s);
    269 	return err;
    270 }
    271 
    272 static ssize_t
    273 ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
    274 {
    275 	const char *envbuf = data;
    276 	char * const argv[] = { ctx->script, NULL };
    277 	pid_t pid;
    278 	int status;
    279 
    280 	if (len == 0)
    281 		return 0;
    282 
    283 	if (script_buftoenv(ctx, UNCONST(envbuf), len) == NULL)
    284 		return -1;
    285 
    286 	pid = script_exec(argv, ctx->script_env);
    287 	if (pid == -1)
    288 		return -1;
    289 
    290 	/* Wait for the script to finish */
    291 	while (waitpid(pid, &status, 0) == -1) {
    292 		if (errno != EINTR) {
    293 			logerr(__func__);
    294 			status = 0;
    295 			break;
    296 		}
    297 	}
    298 	return status;
    299 }
    300 
    301 static bool
    302 ps_root_validpath(const struct dhcpcd_ctx *ctx, uint16_t cmd, const char *path)
    303 {
    304 
    305 	/* Avoid a previous directory attack to avoid /proc/../
    306 	 * dhcpcd should never use a path with double dots. */
    307 	if (strstr(path, "..") != NULL)
    308 		return false;
    309 
    310 	if (cmd == PS_READFILE) {
    311 #ifdef EMBEDDED_CONFIG
    312 		if (strcmp(ctx->cffile, EMBEDDED_CONFIG) == 0)
    313 			return true;
    314 #endif
    315 		if (strcmp(ctx->cffile, path) == 0)
    316 			return true;
    317 	}
    318 	if (strncmp(DBDIR, path, strlen(DBDIR)) == 0)
    319 		return true;
    320 	if (strncmp(RUNDIR, path, strlen(RUNDIR)) == 0)
    321 		return true;
    322 
    323 #ifdef __linux__
    324 	if (strncmp("/proc/net/", path, strlen("/proc/net/")) == 0 ||
    325 	    strncmp("/proc/sys/net/", path, strlen("/proc/sys/net/")) == 0 ||
    326 	    strncmp("/sys/class/net/", path, strlen("/sys/class/net/")) == 0)
    327 		return true;
    328 #endif
    329 
    330 	errno = EPERM;
    331 	return false;
    332 }
    333 
    334 static ssize_t
    335 ps_root_dowritefile(const struct dhcpcd_ctx *ctx,
    336     mode_t mode, void *data, size_t len)
    337 {
    338 	char *file = data, *nc;
    339 
    340 	nc = memchr(file, '\0', len);
    341 	if (nc == NULL) {
    342 		errno = EINVAL;
    343 		return -1;
    344 	}
    345 
    346 	if (!ps_root_validpath(ctx, PS_WRITEFILE, file))
    347 		return -1;
    348 	nc++;
    349 	return writefile(file, mode, nc, len - (size_t)(nc - file));
    350 }
    351 
    352 #ifdef AUTH
    353 static ssize_t
    354 ps_root_monordm(uint64_t *rdm, size_t len)
    355 {
    356 
    357 	if (len != sizeof(*rdm)) {
    358 		errno = EINVAL;
    359 		return -1;
    360 	}
    361 	return auth_get_rdm_monotonic(rdm);
    362 }
    363 #endif
    364 
    365 #ifdef PRIVSEP_GETIFADDRS
    366 #define	IFA_NADDRS	4
    367 static ssize_t
    368 ps_root_dogetifaddrs(void **rdata, size_t *rlen)
    369 {
    370 	struct ifaddrs *ifaddrs, *ifa;
    371 	size_t len;
    372 	uint8_t *buf, *sap;
    373 	socklen_t salen;
    374 
    375 	if (getifaddrs(&ifaddrs) == -1)
    376 		return -1;
    377 	if (ifaddrs == NULL) {
    378 		*rdata = NULL;
    379 		*rlen = 0;
    380 		return 0;
    381 	}
    382 
    383 	/* Work out the buffer length required.
    384 	 * Ensure everything is aligned correctly, which does
    385 	 * create a larger buffer than what is needed to send,
    386 	 * but makes creating the same structure in the client
    387 	 * much easier. */
    388 	len = 0;
    389 	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
    390 		len += ALIGN(sizeof(*ifa));
    391 		len += ALIGN(IFNAMSIZ);
    392 		len += ALIGN(sizeof(salen) * IFA_NADDRS);
    393 		if (ifa->ifa_addr != NULL)
    394 			len += ALIGN(sa_len(ifa->ifa_addr));
    395 		if (ifa->ifa_netmask != NULL)
    396 			len += ALIGN(sa_len(ifa->ifa_netmask));
    397 		if (ifa->ifa_broadaddr != NULL)
    398 			len += ALIGN(sa_len(ifa->ifa_broadaddr));
    399 #ifdef BSD
    400 		/*
    401 		 * On BSD we need to carry ifa_data so we can access
    402 		 * if_data->ifi_link_state
    403 		 */
    404 		if (ifa->ifa_addr != NULL &&
    405 		    ifa->ifa_addr->sa_family == AF_LINK)
    406 			len += ALIGN(sizeof(struct if_data));
    407 #endif
    408 	}
    409 
    410 	/* Use calloc to set everything to zero.
    411 	 * This satisfies memory sanitizers because don't write
    412 	 * where we don't need to. */
    413 	buf = calloc(1, len);
    414 	if (buf == NULL) {
    415 		freeifaddrs(ifaddrs);
    416 		return -1;
    417 	}
    418 	*rdata = buf;
    419 	*rlen = len;
    420 
    421 	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
    422 		memcpy(buf, ifa, sizeof(*ifa));
    423 		buf += ALIGN(sizeof(*ifa));
    424 
    425 		strlcpy((char *)buf, ifa->ifa_name, IFNAMSIZ);
    426 		buf += ALIGN(IFNAMSIZ);
    427 		sap = buf;
    428 		buf += ALIGN(sizeof(salen) * IFA_NADDRS);
    429 
    430 #define	COPYINSA(addr)						\
    431 	do {							\
    432 		if ((addr) != NULL)				\
    433 			salen = sa_len((addr));			\
    434 		else						\
    435 			salen = 0;				\
    436 		if (salen != 0) {				\
    437 			memcpy(sap, &salen, sizeof(salen));	\
    438 			memcpy(buf, (addr), salen);		\
    439 			buf += ALIGN(salen);			\
    440 		}						\
    441 		sap += sizeof(salen);				\
    442 	} while (0 /*CONSTCOND */)
    443 
    444 		COPYINSA(ifa->ifa_addr);
    445 		COPYINSA(ifa->ifa_netmask);
    446 		COPYINSA(ifa->ifa_broadaddr);
    447 
    448 #ifdef BSD
    449 		if (ifa->ifa_addr != NULL &&
    450 		    ifa->ifa_addr->sa_family == AF_LINK)
    451 		{
    452 			salen = (socklen_t)sizeof(struct if_data);
    453 			memcpy(buf, ifa->ifa_data, salen);
    454 			buf += ALIGN(salen);
    455 		} else
    456 #endif
    457 			salen = 0;
    458 		memcpy(sap, &salen, sizeof(salen));
    459 	}
    460 
    461 	freeifaddrs(ifaddrs);
    462 	return 0;
    463 }
    464 #endif
    465 
    466 static ssize_t
    467 ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
    468 {
    469 	struct dhcpcd_ctx *ctx = arg;
    470 	uint16_t cmd;
    471 	struct ps_process *psp;
    472 	struct iovec *iov = msg->msg_iov;
    473 	void *data = iov->iov_base, *rdata = NULL;
    474 	size_t len = iov->iov_len, rlen = 0;
    475 	uint8_t buf[PS_BUFLEN];
    476 	time_t mtime;
    477 	ssize_t err;
    478 	bool free_rdata = false;
    479 
    480 	cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
    481 	psp = ps_findprocess(ctx, &psm->ps_id);
    482 
    483 #ifdef PRIVSEP_DEBUG
    484 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
    485 #endif
    486 
    487 	if (psp != NULL) {
    488 		if (psm->ps_cmd & PS_STOP) {
    489 			return ps_stopprocess(psp);
    490 		} else if (psm->ps_cmd & PS_START) {
    491 			/* Process has already started .... */
    492 			logdebugx("%s%sprocess %s already started on pid %d",
    493 			    psp->psp_ifname,
    494 			    psp->psp_ifname[0] != '\0' ? ": " : "",
    495 			    psp->psp_name, psp->psp_pid);
    496 			return 0;
    497 		}
    498 
    499 		err = ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg);
    500 		if (err == -1) {
    501 			logerr("%s: failed to send message to pid %d",
    502 			    __func__, psp->psp_pid);
    503 			ps_freeprocess(psp);
    504 		}
    505 		return 0;
    506 	}
    507 
    508 	if (psm->ps_cmd & PS_STOP && psp == NULL)
    509 		return 0;
    510 
    511 	switch (cmd) {
    512 #ifdef INET
    513 #ifdef ARP
    514 	case PS_BPF_ARP:	/* FALLTHROUGH */
    515 #endif
    516 	case PS_BPF_BOOTP:
    517 		return ps_bpf_cmd(ctx, psm, msg);
    518 #endif
    519 #ifdef INET
    520 	case PS_BOOTP:
    521 		return ps_inet_cmd(ctx, psm, msg);
    522 #endif
    523 #ifdef INET6
    524 #ifdef DHCP6
    525 	case PS_DHCP6:	/* FALLTHROUGH */
    526 #endif
    527 	case PS_ND:
    528 		return ps_inet_cmd(ctx, psm, msg);
    529 #endif
    530 	default:
    531 		break;
    532 	}
    533 
    534 	assert(msg->msg_iovlen == 0 || msg->msg_iovlen == 1);
    535 
    536 	/* Reset errno */
    537 	errno = 0;
    538 
    539 	switch (psm->ps_cmd) {
    540 	case PS_IOCTL:
    541 		err = ps_root_doioctl(psm->ps_flags, data, len);
    542 		if (err != -1) {
    543 			rdata = data;
    544 			rlen = len;
    545 		}
    546 		break;
    547 	case PS_SCRIPT:
    548 		err = ps_root_run_script(ctx, data, len);
    549 		break;
    550 	case PS_STOPPROCS:
    551 		ctx->options |= DHCPCD_EXITING;
    552 		TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
    553 			if (psp != ctx->ps_root)
    554 				ps_stopprocess(psp);
    555 		}
    556 		err = ps_stopwait(ctx);
    557 		break;
    558 	case PS_UNLINK:
    559 		if (!ps_root_validpath(ctx, psm->ps_cmd, data)) {
    560 			err = -1;
    561 			break;
    562 		}
    563 		err = unlink(data);
    564 		break;
    565 	case PS_READFILE:
    566 		if (!ps_root_validpath(ctx, psm->ps_cmd, data)) {
    567 			err = -1;
    568 			break;
    569 		}
    570 		err = readfile(data, buf, sizeof(buf));
    571 		if (err != -1) {
    572 			rdata = buf;
    573 			rlen = (size_t)err;
    574 		}
    575 		break;
    576 	case PS_WRITEFILE:
    577 		err = ps_root_dowritefile(ctx, (mode_t)psm->ps_flags,
    578 		    data, len);
    579 		break;
    580 	case PS_FILEMTIME:
    581 		err = filemtime(data, &mtime);
    582 		if (err != -1) {
    583 			rdata = &mtime;
    584 			rlen = sizeof(mtime);
    585 		}
    586 		break;
    587 	case PS_LOGREOPEN:
    588 		err = logopen(ctx->logfile);
    589 		break;
    590 #ifdef AUTH
    591 	case PS_AUTH_MONORDM:
    592 		err = ps_root_monordm(data, len);
    593 		if (err != -1) {
    594 			rdata = data;
    595 			rlen = len;
    596 		}
    597 		break;
    598 #endif
    599 #ifdef PRIVSEP_GETIFADDRS
    600 	case PS_GETIFADDRS:
    601 		err = ps_root_dogetifaddrs(&rdata, &rlen);
    602 		free_rdata = true;
    603 		break;
    604 #endif
    605 #ifdef PLUGIN_DEV
    606 	case PS_DEV_INITTED:
    607 		err = dev_initialised(ctx, data);
    608 		break;
    609 	case PS_DEV_LISTENING:
    610 		err = dev_listening(ctx);
    611 		break;
    612 #endif
    613 	default:
    614 		err = ps_root_os(ctx, psm, msg, &rdata, &rlen, &free_rdata);
    615 		break;
    616 	}
    617 
    618 	err = ps_root_writeerror(ctx, err, rdata, rlen);
    619 	if (free_rdata)
    620 		free(rdata);
    621 	return err;
    622 }
    623 
    624 /* Receive from state engine, do an action. */
    625 static void
    626 ps_root_recvmsg(void *arg, unsigned short events)
    627 {
    628 	struct ps_process *psp = arg;
    629 
    630 	if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
    631 	    ps_root_recvmsgcb, psp->psp_ctx) == -1)
    632 		logerr(__func__);
    633 }
    634 
    635 #ifdef PLUGIN_DEV
    636 static int
    637 ps_root_handleinterface(void *arg, int action, const char *ifname)
    638 {
    639 	struct dhcpcd_ctx *ctx = arg;
    640 	unsigned long flag;
    641 
    642 	if (action == 1)
    643 		flag = PS_DEV_IFADDED;
    644 	else if (action == -1)
    645 		flag = PS_DEV_IFREMOVED;
    646 	else if (action == 0)
    647 		flag = PS_DEV_IFUPDATED;
    648 	else {
    649 		errno = EINVAL;
    650 		return -1;
    651 	}
    652 
    653 	return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD,
    654 	    flag, ifname, strlen(ifname) + 1);
    655 }
    656 #endif
    657 
    658 static int
    659 ps_root_startcb(struct ps_process *psp)
    660 {
    661 	struct dhcpcd_ctx *ctx = psp->psp_ctx;
    662 
    663 	if (ctx->options & DHCPCD_MANAGER)
    664 		setproctitle("[privileged proxy]");
    665 	else
    666 		setproctitle("[privileged proxy] %s%s%s",
    667 		    ctx->ifv[0],
    668 		    ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
    669 		    ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
    670 	ctx->options |= DHCPCD_PRIVSEPROOT;
    671 
    672 	if (if_opensockets(ctx) == -1)
    673 		logerr("%s: if_opensockets", __func__);
    674 
    675 	/* Open network sockets for sending.
    676 	 * This is a small bit wasteful for non sandboxed OS's
    677 	 * but makes life very easy for unicasting DHCPv6 in non manager
    678 	 * mode as we no longer care about address selection.
    679 	 * We can't call shutdown SHUT_RD on the socket because it's
    680 	 * not connected. All we can do is try and set a zero sized
    681 	 * receive buffer and just let it overflow.
    682 	 * Reading from it just to drain it is a waste of CPU time. */
    683 #ifdef INET
    684 	if (ctx->options & DHCPCD_IPV4) {
    685 		int buflen = 1;
    686 
    687 		ctx->udp_wfd = xsocket(PF_INET,
    688 		    SOCK_RAW | SOCK_CXNB, IPPROTO_UDP);
    689 		if (ctx->udp_wfd == -1)
    690 			logerr("%s: dhcp_openraw", __func__);
    691 		else if (setsockopt(ctx->udp_wfd, SOL_SOCKET, SO_RCVBUF,
    692 		    &buflen, sizeof(buflen)) == -1)
    693 			logerr("%s: setsockopt SO_RCVBUF DHCP", __func__);
    694 	}
    695 #endif
    696 #ifdef INET6
    697 	if (ctx->options & DHCPCD_IPV6) {
    698 		int buflen = 1;
    699 
    700 		ctx->nd_fd = ipv6nd_open(false);
    701 		if (ctx->nd_fd == -1)
    702 			logerr("%s: ipv6nd_open", __func__);
    703 		else if (setsockopt(ctx->nd_fd, SOL_SOCKET, SO_RCVBUF,
    704 		    &buflen, sizeof(buflen)) == -1)
    705 			logerr("%s: setsockopt SO_RCVBUF ND", __func__);
    706 	}
    707 #endif
    708 #ifdef DHCP6
    709 	if (ctx->options & DHCPCD_IPV6) {
    710 		int buflen = 1;
    711 
    712 		ctx->dhcp6_wfd = dhcp6_openraw();
    713 		if (ctx->dhcp6_wfd == -1)
    714 			logerr("%s: dhcp6_openraw", __func__);
    715 		else if (setsockopt(ctx->dhcp6_wfd, SOL_SOCKET, SO_RCVBUF,
    716 		    &buflen, sizeof(buflen)) == -1)
    717 			logerr("%s: setsockopt SO_RCVBUF DHCP6", __func__);
    718 	}
    719 #endif
    720 
    721 #ifdef PLUGIN_DEV
    722 	/* Start any dev listening plugin which may want to
    723 	 * change the interface name provided by the kernel */
    724 	if ((ctx->options & (DHCPCD_MANAGER | DHCPCD_DEV)) ==
    725 	    (DHCPCD_MANAGER | DHCPCD_DEV))
    726 		dev_start(ctx, ps_root_handleinterface);
    727 #endif
    728 
    729 	return 0;
    730 }
    731 
    732 void
    733 ps_root_signalcb(int sig, void *arg)
    734 {
    735 	struct dhcpcd_ctx *ctx = arg;
    736 	int status;
    737 	pid_t pid;
    738 	const char *ifname, *name;
    739 	struct ps_process *psp;
    740 
    741 	if (sig != SIGCHLD)
    742 		return;
    743 
    744 	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
    745 		psp = ps_findprocesspid(ctx, pid);
    746 		if (psp != NULL) {
    747 			ifname = psp->psp_ifname;
    748 			name = psp->psp_name;
    749 		} else {
    750 			/* Ignore logging the double fork */
    751 			if (ctx->options & DHCPCD_LAUNCHER)
    752 				continue;
    753 			ifname = "";
    754 			name = "unknown process";
    755 		}
    756 
    757 		if (WIFEXITED(status) && WEXITSTATUS(status) != 0)
    758 			logerrx("%s%s%s exited unexpectedly from PID %d,"
    759 			    " code=%d",
    760 			    ifname, ifname[0] != '\0' ? ": " : "",
    761 			    name, pid, WEXITSTATUS(status));
    762 		else if (WIFSIGNALED(status))
    763 			logerrx("%s%s%s exited unexpectedly from PID %d,"
    764 			    " signal=%s",
    765 			    ifname, ifname[0] != '\0' ? ": " : "",
    766 			    name, pid, strsignal(WTERMSIG(status)));
    767 		else
    768 			logdebugx("%s%s%s exited from PID %d",
    769 			    ifname, ifname[0] != '\0' ? ": " : "",
    770 			    name, pid);
    771 
    772 		if (psp != NULL)
    773 			ps_freeprocess(psp);
    774 	}
    775 
    776 	if (!(ctx->options & DHCPCD_EXITING))
    777 		return;
    778 	if (!(ps_waitforprocs(ctx)))
    779 		eloop_exit(ctx->eloop, EXIT_SUCCESS);
    780 }
    781 
    782 int (*handle_interface)(void *, int, const char *);
    783 
    784 #ifdef PLUGIN_DEV
    785 static ssize_t
    786 ps_root_devcb(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
    787 {
    788 	int action;
    789 	struct iovec *iov = msg->msg_iov;
    790 
    791 	if (msg->msg_iovlen != 1) {
    792 		errno = EINVAL;
    793 		return -1;
    794 	}
    795 
    796 	switch(psm->ps_flags) {
    797 	case PS_DEV_IFADDED:
    798 		action = 1;
    799 		break;
    800 	case PS_DEV_IFREMOVED:
    801 		action = -1;
    802 		break;
    803 	case PS_DEV_IFUPDATED:
    804 		action = 0;
    805 		break;
    806 	default:
    807 		errno = EINVAL;
    808 		return -1;
    809 	}
    810 
    811 	return dhcpcd_handleinterface(ctx, action, iov->iov_base);
    812 }
    813 #endif
    814 
    815 static ssize_t
    816 ps_root_dispatchcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
    817 {
    818 	struct dhcpcd_ctx *ctx = arg;
    819 	ssize_t err;
    820 
    821 	switch(psm->ps_cmd) {
    822 #ifdef PLUGIN_DEV
    823 	case PS_DEV_IFCMD:
    824 		err = ps_root_devcb(ctx, psm, msg);
    825 		break;
    826 #endif
    827 	default:
    828 #ifdef INET
    829 		err = ps_bpf_dispatch(ctx, psm, msg);
    830 		if (err == -1 && errno == ENOTSUP)
    831 #endif
    832 			err = ps_inet_dispatch(ctx, psm, msg);
    833 	}
    834 	return err;
    835 }
    836 
    837 static void
    838 ps_root_dispatch(void *arg, unsigned short events)
    839 {
    840 	struct dhcpcd_ctx *ctx = arg;
    841 
    842 	if (ps_recvpsmsg(ctx, ctx->ps_data_fd, events,
    843 	    ps_root_dispatchcb, ctx) == -1)
    844 		logerr(__func__);
    845 }
    846 
    847 static void
    848 ps_root_log(void *arg, unsigned short events)
    849 {
    850 	struct dhcpcd_ctx *ctx = arg;
    851 
    852 	if (events != ELE_READ)
    853 		logerrx("%s: unexpected event 0x%04x", __func__, events);
    854 
    855 	if (logreadfd(ctx->ps_log_root_fd) == -1)
    856 		logerr(__func__);
    857 }
    858 
    859 pid_t
    860 ps_root_start(struct dhcpcd_ctx *ctx)
    861 {
    862 	struct ps_id id = {
    863 		.psi_ifindex = 0,
    864 		.psi_cmd = PS_ROOT,
    865 	};
    866 	struct ps_process *psp;
    867 	int logfd[2] = { -1, -1}, datafd[2] = { -1, -1};
    868 	pid_t pid;
    869 
    870 	if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, logfd) == -1)
    871 		return -1;
    872 #ifdef PRIVSEP_RIGHTS
    873 	if (ps_rights_limit_fdpair(logfd) == -1)
    874 		return -1;
    875 #endif
    876 
    877 	if (xsocketpair(AF_UNIX, SOCK_SEQPACKET | SOCK_CXNB, 0, datafd) == -1)
    878 		return -1;
    879 
    880 	if (ps_setbuf_fdpair(datafd) == -1)
    881 		return -1;
    882 #ifdef PRIVSEP_RIGHTS
    883 	if (ps_rights_limit_fdpair(datafd) == -1)
    884 		return -1;
    885 #endif
    886 
    887 	psp = ctx->ps_root = ps_newprocess(ctx, &id);
    888 	if (psp == NULL)
    889 		return -1;
    890 
    891 	strlcpy(psp->psp_name, "privileged proxy", sizeof(psp->psp_name));
    892 	pid = ps_startprocess(psp, ps_root_recvmsg, NULL,
    893 	    ps_root_startcb, PSF_ELOOP);
    894 	if (pid == -1)
    895 		return -1;
    896 
    897 	if (pid == 0) {
    898 		ctx->ps_log_fd = logfd[0]; /* Keep open to pass to processes */
    899 		ctx->ps_log_root_fd = logfd[1];
    900 		if (eloop_event_add(ctx->eloop, ctx->ps_log_root_fd, ELE_READ,
    901 		    ps_root_log, ctx) == -1)
    902 			return -1;
    903 		ctx->ps_data_fd = datafd[1];
    904 		close(datafd[0]);
    905 		return 0;
    906 	} else if (pid == -1)
    907 		return -1;
    908 
    909 	logsetfd(logfd[0]);
    910 	close(logfd[1]);
    911 
    912 	ctx->ps_data_fd = datafd[0];
    913 	close(datafd[1]);
    914 	if (eloop_event_add(ctx->eloop, ctx->ps_data_fd, ELE_READ,
    915 	    ps_root_dispatch, ctx) == -1)
    916 		return -1;
    917 
    918 	return pid;
    919 }
    920 
    921 void
    922 ps_root_close(struct dhcpcd_ctx *ctx)
    923 {
    924 
    925 	if_closesockets(ctx);
    926 
    927 #ifdef INET
    928 	if (ctx->udp_wfd != -1) {
    929 		close(ctx->udp_wfd);
    930 		ctx->udp_wfd = -1;
    931 	}
    932 #endif
    933 #ifdef INET6
    934 	if (ctx->nd_fd != -1) {
    935 		close(ctx->nd_fd);
    936 		ctx->nd_fd = -1;
    937 	}
    938 #endif
    939 #ifdef DHCP6
    940 	if (ctx->dhcp6_wfd != -1) {
    941 		close(ctx->dhcp6_wfd);
    942 		ctx->dhcp6_wfd = -1;
    943 	}
    944 #endif
    945 }
    946 
    947 int
    948 ps_root_stop(struct dhcpcd_ctx *ctx)
    949 {
    950 	struct ps_process *psp = ctx->ps_root;
    951 	int err;
    952 
    953 	if (!(ctx->options & DHCPCD_PRIVSEP))
    954 		return 0;
    955 
    956 	/* If we are the root process then remove the pidfile */
    957 	if (ctx->options & DHCPCD_PRIVSEPROOT) {
    958 		if (!(ctx->options & DHCPCD_TEST) && unlink(ctx->pidfile) == -1)
    959 			logerr("%s: unlink: %s", __func__, ctx->pidfile);
    960 
    961 		/* drain the log */
    962 		if (ctx->ps_log_root_fd != -1) {
    963 			ssize_t loglen;
    964 
    965 #ifdef __linux__
    966 			/* Seems to help to get the last parts,
    967 			 * sched_yield(2) does not. */
    968 			sleep(0);
    969 #endif
    970 			do {
    971 				loglen = logreadfd(ctx->ps_log_root_fd);
    972 			} while (loglen != 0 && loglen != -1);
    973 			close(ctx->ps_log_root_fd);
    974 			ctx->ps_log_root_fd = -1;
    975 		}
    976 	}
    977 
    978 	if (ctx->ps_data_fd != -1) {
    979 		eloop_event_delete(ctx->eloop, ctx->ps_data_fd);
    980 		close(ctx->ps_data_fd);
    981 		ctx->ps_data_fd = -1;
    982 	}
    983 
    984 	/* Only the manager process gets past this point. */
    985 	if (ctx->options & DHCPCD_FORKED)
    986 		return 0;
    987 
    988 	/* We cannot log the root process exited before we
    989 	 * log dhcpcd exits because the latter requires the former.
    990 	 * So we just log the intent to exit.
    991 	 * Even sending this will be a race to exit. */
    992 	if (psp) {
    993 		logdebugx("%s%s%s will exit from PID %d",
    994 		    psp->psp_ifname,
    995 		    psp->psp_ifname[0] != '\0' ? ": " : "",
    996 		    psp->psp_name, psp->psp_pid);
    997 
    998 		if (ps_stopprocess(psp) == -1)
    999 			return -1;
   1000 	} /* else the root process has already exited :( */
   1001 
   1002 	err = ps_stopwait(ctx);
   1003 	if (ctx->ps_root != NULL)
   1004 		ps_freeprocess(ctx->ps_root);
   1005 	return err;
   1006 }
   1007 
   1008 ssize_t
   1009 ps_root_stopprocesses(struct dhcpcd_ctx *ctx)
   1010 {
   1011 
   1012 	if (!(IN_PRIVSEP_SE(ctx)))
   1013 		return 0;
   1014 
   1015 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_STOPPROCS, 0,
   1016 	    NULL, 0) == -1)
   1017 		return -1;
   1018 	return ps_root_readerror(ctx, NULL, 0);
   1019 }
   1020 
   1021 ssize_t
   1022 ps_root_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
   1023 {
   1024 
   1025 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_SCRIPT,
   1026 	    0, data, len) == -1)
   1027 		return -1;
   1028 	return ps_root_readerror(ctx, NULL, 0);
   1029 }
   1030 
   1031 ssize_t
   1032 ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,
   1033     size_t len)
   1034 {
   1035 	int fd = PS_ROOT_FD(ctx);
   1036 #ifdef IOCTL_REQUEST_TYPE
   1037 	unsigned long ulreq = 0;
   1038 
   1039 	memcpy(&ulreq, &req, sizeof(req));
   1040 	if (ps_sendcmd(ctx, fd, PS_IOCTL, ulreq, data, len) == -1)
   1041 		return -1;
   1042 #else
   1043 	if (ps_sendcmd(ctx, fd, PS_IOCTL, req, data, len) == -1)
   1044 		return -1;
   1045 #endif
   1046 	return ps_root_readerror(ctx, data, len);
   1047 }
   1048 
   1049 ssize_t
   1050 ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file)
   1051 {
   1052 
   1053 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_UNLINK, 0,
   1054 	    file, strlen(file) + 1) == -1)
   1055 		return -1;
   1056 	return ps_root_readerror(ctx, NULL, 0);
   1057 }
   1058 
   1059 ssize_t
   1060 ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file,
   1061     void *data, size_t len)
   1062 {
   1063 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_READFILE, 0,
   1064 	    file, strlen(file) + 1) == -1)
   1065 		return -1;
   1066 	return ps_root_readerror(ctx, data, len);
   1067 }
   1068 
   1069 ssize_t
   1070 ps_root_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
   1071     const void *data, size_t len)
   1072 {
   1073 	char buf[PS_BUFLEN];
   1074 	size_t flen;
   1075 
   1076 	flen = strlcpy(buf, file, sizeof(buf));
   1077 	flen += 1;
   1078 	if (flen > sizeof(buf) || flen + len > sizeof(buf)) {
   1079 		errno = ENOBUFS;
   1080 		return -1;
   1081 	}
   1082 	memcpy(buf + flen, data, len);
   1083 
   1084 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_WRITEFILE, mode,
   1085 	    buf, flen + len) == -1)
   1086 		return -1;
   1087 	return ps_root_readerror(ctx, NULL, 0);
   1088 }
   1089 
   1090 ssize_t
   1091 ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
   1092 {
   1093 
   1094 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_FILEMTIME, 0,
   1095 	    file, strlen(file) + 1) == -1)
   1096 		return -1;
   1097 	return ps_root_readerror(ctx, time, sizeof(*time));
   1098 }
   1099 
   1100 ssize_t
   1101 ps_root_logreopen(struct dhcpcd_ctx *ctx)
   1102 {
   1103 
   1104 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_LOGREOPEN, 0,
   1105 	    NULL, 0) == -1)
   1106 		return -1;
   1107 	return ps_root_readerror(ctx, NULL, 0);
   1108 }
   1109 
   1110 #ifdef PRIVSEP_GETIFADDRS
   1111 int
   1112 ps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead)
   1113 {
   1114 	struct ifaddrs *ifa;
   1115 	void *buf = NULL;
   1116 	char *bp, *sap;
   1117 	socklen_t salen;
   1118 	size_t len;
   1119 	ssize_t err;
   1120 
   1121 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx),
   1122 	    PS_GETIFADDRS, 0, NULL, 0) == -1)
   1123 		return -1;
   1124 	err = ps_root_mreaderror(ctx, &buf, &len);
   1125 
   1126 	if (err == -1)
   1127 		return -1;
   1128 
   1129 	/* Should be impossible - lo0 will always exist. */
   1130 	if (len == 0) {
   1131 		*ifahead = NULL;
   1132 		return 0;
   1133 	}
   1134 
   1135 	bp = buf;
   1136 	*ifahead = (struct ifaddrs *)(void *)bp;
   1137 	for (ifa = *ifahead; ifa != NULL; ifa = ifa->ifa_next) {
   1138 		if (len < ALIGN(sizeof(*ifa)) +
   1139 		    ALIGN(IFNAMSIZ) + ALIGN(sizeof(salen) * IFA_NADDRS))
   1140 			goto err;
   1141 		bp += ALIGN(sizeof(*ifa));
   1142 		ifa->ifa_name = bp;
   1143 		bp += ALIGN(IFNAMSIZ);
   1144 		sap = bp;
   1145 		bp += ALIGN(sizeof(salen) * IFA_NADDRS);
   1146 		len -= ALIGN(sizeof(*ifa)) +
   1147 		    ALIGN(IFNAMSIZ) + ALIGN(sizeof(salen) * IFA_NADDRS);
   1148 
   1149 #define	COPYOUTSA(addr)							\
   1150 	do {								\
   1151 		memcpy(&salen, sap, sizeof(salen));			\
   1152 		if (len < salen)					\
   1153 			goto err;					\
   1154 		if (salen != 0) {					\
   1155 			(addr) = (struct sockaddr *)(void *)bp;		\
   1156 			bp += ALIGN(salen);				\
   1157 			len -= ALIGN(salen);				\
   1158 		}							\
   1159 		sap += sizeof(salen);					\
   1160 	} while (0 /* CONSTCOND */)
   1161 
   1162 		COPYOUTSA(ifa->ifa_addr);
   1163 		COPYOUTSA(ifa->ifa_netmask);
   1164 		COPYOUTSA(ifa->ifa_broadaddr);
   1165 
   1166 		memcpy(&salen, sap, sizeof(salen));
   1167 		if (len < salen)
   1168 			goto err;
   1169 		if (salen != 0) {
   1170 			ifa->ifa_data = bp;
   1171 			bp += ALIGN(salen);
   1172 			len -= ALIGN(salen);
   1173 		} else
   1174 			ifa->ifa_data = NULL;
   1175 
   1176 		if (len != 0)
   1177 			ifa->ifa_next = (struct ifaddrs *)(void *)bp;
   1178 		else
   1179 			ifa->ifa_next = NULL;
   1180 	}
   1181 	return 0;
   1182 
   1183 err:
   1184 	free(buf);
   1185 	*ifahead = NULL;
   1186 	errno = EINVAL;
   1187 	return -1;
   1188 }
   1189 #endif
   1190 
   1191 #ifdef AUTH
   1192 int
   1193 ps_root_getauthrdm(struct dhcpcd_ctx *ctx, uint64_t *rdm)
   1194 {
   1195 
   1196 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_AUTH_MONORDM, 0,
   1197 	    rdm, sizeof(*rdm))== -1)
   1198 		return -1;
   1199 	return (int)ps_root_readerror(ctx, rdm, sizeof(*rdm));
   1200 }
   1201 #endif
   1202 
   1203 #ifdef PLUGIN_DEV
   1204 int
   1205 ps_root_dev_initialised(struct dhcpcd_ctx *ctx, const char *ifname)
   1206 {
   1207 
   1208 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_DEV_INITTED, 0,
   1209 	    ifname, strlen(ifname) + 1)== -1)
   1210 		return -1;
   1211 	return (int)ps_root_readerror(ctx, NULL, 0);
   1212 }
   1213 
   1214 int
   1215 ps_root_dev_listening(struct dhcpcd_ctx * ctx)
   1216 {
   1217 
   1218 	if (ps_sendcmd(ctx, PS_ROOT_FD(ctx), PS_DEV_LISTENING,
   1219 	    0, NULL, 0) == -1)
   1220 		return -1;
   1221 	return (int)ps_root_readerror(ctx, NULL, 0);
   1222 }
   1223 #endif
   1224