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