xref: /dragonfly/contrib/dhcpcd/src/privsep-root.c (revision 335b9e93)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * Privilege Separation for dhcpcd, privileged actioneer
4  * Copyright (c) 2006-2020 Roy Marples <roy@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 "common.h"
47 #include "dev.h"
48 #include "dhcpcd.h"
49 #include "dhcp6.h"
50 #include "eloop.h"
51 #include "if.h"
52 #include "ipv6nd.h"
53 #include "logerr.h"
54 #include "privsep.h"
55 #include "sa.h"
56 #include "script.h"
57 
58 __CTASSERT(sizeof(ioctl_request_t) <= sizeof(unsigned long));
59 
60 struct psr_error
61 {
62 	ssize_t psr_result;
63 	int psr_errno;
64 	char psr_pad[sizeof(ssize_t) - sizeof(int)];
65 	size_t psr_datalen;
66 };
67 
68 struct psr_ctx {
69 	struct dhcpcd_ctx *psr_ctx;
70 	struct psr_error psr_error;
71 	size_t psr_datalen;
72 	void *psr_data;
73 };
74 
75 static void
76 ps_root_readerrorsig(__unused int sig, void *arg)
77 {
78 	struct dhcpcd_ctx *ctx = arg;
79 
80 	eloop_exit(ctx->ps_eloop, EXIT_FAILURE);
81 }
82 
83 static void
84 ps_root_readerrorcb(void *arg)
85 {
86 	struct psr_ctx *psr_ctx = arg;
87 	struct dhcpcd_ctx *ctx = psr_ctx->psr_ctx;
88 	struct psr_error *psr_error = &psr_ctx->psr_error;
89 	struct iovec iov[] = {
90 		{ .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
91 		{ .iov_base = psr_ctx->psr_data,
92 		  .iov_len = psr_ctx->psr_datalen },
93 	};
94 	ssize_t len;
95 	int exit_code = EXIT_FAILURE;
96 
97 #define PSR_ERROR(e)				\
98 	do {					\
99 		psr_error->psr_result = -1;	\
100 		psr_error->psr_errno = (e);	\
101 		goto out;			\
102 	} while (0 /* CONSTCOND */)
103 
104 	len = readv(ctx->ps_root_fd, iov, __arraycount(iov));
105 	if (len == -1)
106 		PSR_ERROR(errno);
107 	else if ((size_t)len < sizeof(*psr_error))
108 		PSR_ERROR(EINVAL);
109 	exit_code = EXIT_SUCCESS;
110 
111 out:
112 	eloop_exit(ctx->ps_eloop, exit_code);
113 }
114 
115 ssize_t
116 ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len)
117 {
118 	struct psr_ctx psr_ctx = {
119 	    .psr_ctx = ctx,
120 	    .psr_data = data, .psr_datalen = len,
121 	};
122 
123 	if (eloop_event_add(ctx->ps_eloop, ctx->ps_root_fd,
124 	    ps_root_readerrorcb, &psr_ctx) == -1)
125 		return -1;
126 
127 	eloop_start(ctx->ps_eloop, &ctx->sigset);
128 
129 	errno = psr_ctx.psr_error.psr_errno;
130 	return psr_ctx.psr_error.psr_result;
131 }
132 
133 #ifdef HAVE_CAPSICUM
134 static void
135 ps_root_mreaderrorcb(void *arg)
136 {
137 	struct psr_ctx *psr_ctx = arg;
138 	struct dhcpcd_ctx *ctx = psr_ctx->psr_ctx;
139 	struct psr_error *psr_error = &psr_ctx->psr_error;
140 	struct iovec iov[] = {
141 		{ .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
142 		{ .iov_base = NULL, .iov_len = 0 },
143 	};
144 	ssize_t len;
145 	int exit_code = EXIT_FAILURE;
146 
147 	len = recv(ctx->ps_root_fd, psr_error, sizeof(*psr_error), MSG_PEEK);
148 	if (len == -1)
149 		PSR_ERROR(errno);
150 	else if ((size_t)len < sizeof(*psr_error))
151 		PSR_ERROR(EINVAL);
152 
153 	if (psr_error->psr_datalen != 0) {
154 		psr_ctx->psr_data = malloc(psr_error->psr_datalen);
155 		if (psr_ctx->psr_data == NULL)
156 			PSR_ERROR(errno);
157 		psr_ctx->psr_datalen = psr_error->psr_datalen;
158 		iov[1].iov_base = psr_ctx->psr_data;
159 		iov[1].iov_len = psr_ctx->psr_datalen;
160 	}
161 
162 	len = readv(ctx->ps_root_fd, iov, __arraycount(iov));
163 	if (len == -1)
164 		PSR_ERROR(errno);
165 	else if ((size_t)len != sizeof(*psr_error) + psr_ctx->psr_datalen)
166 		PSR_ERROR(EINVAL);
167 	exit_code = EXIT_SUCCESS;
168 
169 out:
170 	eloop_exit(ctx->ps_eloop, exit_code);
171 }
172 
173 ssize_t
174 ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len)
175 {
176 	struct psr_ctx psr_ctx = {
177 	    .psr_ctx = ctx,
178 	};
179 
180 	if (eloop_event_add(ctx->ps_eloop, ctx->ps_root_fd,
181 	    ps_root_mreaderrorcb, &psr_ctx) == -1)
182 		return -1;
183 
184 	eloop_start(ctx->ps_eloop, &ctx->sigset);
185 
186 	errno = psr_ctx.psr_error.psr_errno;
187 	*data = psr_ctx.psr_data;
188 	*len = psr_ctx.psr_datalen;
189 	return psr_ctx.psr_error.psr_result;
190 }
191 #endif
192 
193 static ssize_t
194 ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result,
195     void *data, size_t len)
196 {
197 	struct psr_error psr = {
198 		.psr_result = result,
199 		.psr_errno = errno,
200 		.psr_datalen = len,
201 	};
202 	struct iovec iov[] = {
203 		{ .iov_base = &psr, .iov_len = sizeof(psr) },
204 		{ .iov_base = data, .iov_len = len },
205 	};
206 
207 #ifdef PRIVSEP_DEBUG
208 	logdebugx("%s: result %zd errno %d", __func__, result, errno);
209 #endif
210 
211 	return writev(ctx->ps_root_fd, iov, __arraycount(iov));
212 }
213 
214 static ssize_t
215 ps_root_doioctl(unsigned long req, void *data, size_t len)
216 {
217 	int s, err;
218 
219 	/* Only allow these ioctls */
220 	switch(req) {
221 #ifdef SIOCAIFADDR
222 	case SIOCAIFADDR:	/* FALLTHROUGH */
223 	case SIOCDIFADDR:	/* FALLTHROUGH */
224 #endif
225 #ifdef SIOCSIFHWADDR
226 	case SIOCSIFHWADDR:	/* FALLTHROUGH */
227 #endif
228 #ifdef SIOCGIFPRIORITY
229 	case SIOCGIFPRIORITY:	/* FALLTHROUGH */
230 #endif
231 	case SIOCSIFFLAGS:	/* FALLTHROUGH */
232 	case SIOCGIFMTU:	/* FALLTHROUGH */
233 	case SIOCSIFMTU:
234 		break;
235 	default:
236 		errno = EPERM;
237 		return -1;
238 	}
239 
240 	s = socket(PF_INET, SOCK_DGRAM, 0);
241 	if (s != -1)
242 #ifdef IOCTL_REQUEST_TYPE
243 	{
244 		ioctl_request_t reqt;
245 
246 		memcpy(&reqt, &req, sizeof(reqt));
247 		err = ioctl(s, reqt, data, len);
248 	}
249 #else
250 		err = ioctl(s, req, data, len);
251 #endif
252 	else
253 		err = -1;
254 	if (s != -1)
255 		close(s);
256 	return err;
257 }
258 
259 static ssize_t
260 ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
261 {
262 	const char *envbuf = data;
263 	char * const argv[] = { ctx->script, NULL };
264 	pid_t pid;
265 	int status;
266 
267 	if (len == 0)
268 		return 0;
269 
270 	if (script_buftoenv(ctx, UNCONST(envbuf), len) == NULL)
271 		return -1;
272 
273 	pid = script_exec(argv, ctx->script_env);
274 	if (pid == -1)
275 		return -1;
276 	/* Wait for the script to finish */
277 	while (waitpid(pid, &status, 0) == -1) {
278 		if (errno != EINTR) {
279 			logerr(__func__);
280 			status = 0;
281 			break;
282 		}
283 	}
284 	return status;
285 }
286 
287 static bool
288 ps_root_validpath(const struct dhcpcd_ctx *ctx, uint16_t cmd, const char *path)
289 {
290 
291 	/* Avoid a previous directory attack to avoid /proc/../
292 	 * dhcpcd should never use a path with double dots. */
293 	if (strstr(path, "..") != NULL)
294 		return false;
295 
296 	if (cmd == PS_READFILE) {
297 		if (strcmp(ctx->cffile, path) == 0)
298 			return true;
299 	}
300 	if (strncmp(DBDIR, path, strlen(DBDIR)) == 0)
301 		return true;
302 	if (strncmp(RUNDIR, path, strlen(RUNDIR)) == 0)
303 		return true;
304 
305 #ifdef __linux__
306 	if (strncmp("/proc/net/", path, strlen("/proc/net/")) == 0 ||
307 	    strncmp("/proc/sys/net/", path, strlen("/proc/sys/net/")) == 0 ||
308 	    strncmp("/sys/class/net/", path, strlen("/sys/class/net/")) == 0)
309 		return true;
310 #endif
311 
312 	errno = EPERM;
313 	return false;
314 }
315 
316 static ssize_t
317 ps_root_dowritefile(const struct dhcpcd_ctx *ctx,
318     mode_t mode, void *data, size_t len)
319 {
320 	char *file = data, *nc;
321 
322 	nc = memchr(file, '\0', len);
323 	if (nc == NULL) {
324 		errno = EINVAL;
325 		return -1;
326 	}
327 
328 	if (!ps_root_validpath(ctx, PS_WRITEFILE, file))
329 		return -1;
330 	nc++;
331 	return writefile(file, mode, nc, len - (size_t)(nc - file));
332 }
333 
334 #ifdef HAVE_CAPSICUM
335 #define	IFA_NADDRS	3
336 static ssize_t
337 ps_root_dogetifaddrs(void **rdata, size_t *rlen)
338 {
339 	struct ifaddrs *ifaddrs, *ifa;
340 	size_t len;
341 	uint8_t *buf, *sap;
342 	socklen_t salen;
343 	void *ifdata;
344 
345 	if (getifaddrs(&ifaddrs) == -1)
346 		return -1;
347 	if (ifaddrs == NULL) {
348 		*rdata = NULL;
349 		*rlen = 0;
350 		return 0;
351 	}
352 
353 	/* Work out the buffer length required.
354 	 * Ensure everything is aligned correctly, which does
355 	 * create a larger buffer than what is needed to send,
356 	 * but makes creating the same structure in the client
357 	 * much easier. */
358 	len = 0;
359 	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
360 		len += ALIGN(sizeof(*ifa));
361 		len += ALIGN(IFNAMSIZ);
362 		len += ALIGN(sizeof(salen) * IFA_NADDRS);
363 		if (ifa->ifa_addr != NULL)
364 			len += ALIGN(sa_len(ifa->ifa_addr));
365 		if (ifa->ifa_netmask != NULL)
366 			len += ALIGN(sa_len(ifa->ifa_netmask));
367 		if (ifa->ifa_broadaddr != NULL)
368 			len += ALIGN(sa_len(ifa->ifa_broadaddr));
369 	}
370 
371 	/* Use calloc to set everything to zero.
372 	 * This satisfies memory sanitizers because don't write
373 	 * where we don't need to. */
374 	buf = calloc(1, len);
375 	if (buf == NULL) {
376 		freeifaddrs(ifaddrs);
377 		return -1;
378 	}
379 	*rdata = buf;
380 	*rlen = len;
381 
382 	for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
383 		/* Don't carry ifa_data. */
384 		ifdata = ifa->ifa_data;
385 		ifa->ifa_data = NULL;
386 		memcpy(buf, ifa, sizeof(*ifa));
387 		buf += ALIGN(sizeof(*ifa));
388 		ifa->ifa_data = ifdata;
389 
390 		strlcpy((char *)buf, ifa->ifa_name, IFNAMSIZ);
391 		buf += ALIGN(IFNAMSIZ);
392 		sap = buf;
393 		buf += ALIGN(sizeof(salen) * IFA_NADDRS);
394 
395 #define	COPYINSA(addr)						\
396 	do {							\
397 		salen = sa_len((addr));				\
398 		if (salen != 0) {				\
399 			memcpy(sap, &salen, sizeof(salen));	\
400 			memcpy(buf, (addr), salen);		\
401 			buf += ALIGN(salen);			\
402 		}						\
403 		sap += sizeof(salen);				\
404 	} while (0 /*CONSTCOND */)
405 
406 		if (ifa->ifa_addr != NULL)
407 			COPYINSA(ifa->ifa_addr);
408 		if (ifa->ifa_netmask != NULL)
409 			COPYINSA(ifa->ifa_netmask);
410 		if (ifa->ifa_broadaddr != NULL)
411 			COPYINSA(ifa->ifa_broadaddr);
412 	}
413 
414 	freeifaddrs(ifaddrs);
415 	return 0;
416 }
417 #endif
418 
419 static ssize_t
420 ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
421 {
422 	struct dhcpcd_ctx *ctx = arg;
423 	uint16_t cmd;
424 	struct ps_process *psp;
425 	struct iovec *iov = msg->msg_iov;
426 	void *data = iov->iov_base, *rdata = NULL;
427 	size_t len = iov->iov_len, rlen = 0;
428 	uint8_t buf[PS_BUFLEN];
429 	time_t mtime;
430 	ssize_t err;
431 	bool free_rdata = false;
432 
433 	cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
434 	psp = ps_findprocess(ctx, &psm->ps_id);
435 
436 #ifdef PRIVSEP_DEBUG
437 	logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
438 #endif
439 
440 	if (psp != NULL) {
441 		if (psm->ps_cmd & PS_STOP) {
442 			int ret = ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
443 
444 			ps_freeprocess(psp);
445 			return ret;
446 		} else if (!(psm->ps_cmd & PS_START))
447 			return ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg);
448 		/* Process has already started .... */
449 		return 0;
450 	}
451 
452 	if (psm->ps_cmd & PS_STOP && psp == NULL)
453 		return 0;
454 
455 	switch (cmd) {
456 #ifdef INET
457 #ifdef ARP
458 	case PS_BPF_ARP:	/* FALLTHROUGH */
459 #endif
460 	case PS_BPF_BOOTP:
461 		return ps_bpf_cmd(ctx, psm, msg);
462 #endif
463 #ifdef INET
464 	case PS_BOOTP:
465 		return ps_inet_cmd(ctx, psm, msg);
466 #endif
467 #ifdef INET6
468 #ifdef DHCP6
469 	case PS_DHCP6:	/* FALLTHROUGH */
470 #endif
471 	case PS_ND:
472 		return ps_inet_cmd(ctx, psm, msg);
473 #endif
474 	default:
475 		break;
476 	}
477 
478 	assert(msg->msg_iovlen == 0 || msg->msg_iovlen == 1);
479 
480 	/* Reset errno */
481 	errno = 0;
482 
483 	switch (psm->ps_cmd) {
484 	case PS_IOCTL:
485 		err = ps_root_doioctl(psm->ps_flags, data, len);
486 		if (err != -1) {
487 			rdata = data;
488 			rlen = len;
489 		}
490 		break;
491 	case PS_SCRIPT:
492 		err = ps_root_run_script(ctx, data, len);
493 		break;
494 	case PS_UNLINK:
495 		if (!ps_root_validpath(ctx, psm->ps_cmd, data)) {
496 			err = -1;
497 			break;
498 		}
499 		err = unlink(data);
500 		break;
501 	case PS_READFILE:
502 		if (!ps_root_validpath(ctx, psm->ps_cmd, data)) {
503 			err = -1;
504 			break;
505 		}
506 		err = readfile(data, buf, sizeof(buf));
507 		if (err != -1) {
508 			rdata = buf;
509 			rlen = (size_t)err;
510 		}
511 		break;
512 	case PS_WRITEFILE:
513 		err = ps_root_dowritefile(ctx, (mode_t)psm->ps_flags,
514 		    data, len);
515 		break;
516 	case PS_FILEMTIME:
517 		err = filemtime(data, &mtime);
518 		if (err != -1) {
519 			rdata = &mtime;
520 			rlen = sizeof(mtime);
521 		}
522 		break;
523 #ifdef HAVE_CAPSICUM
524 	case PS_GETIFADDRS:
525 		err = ps_root_dogetifaddrs(&rdata, &rlen);
526 		free_rdata = true;
527 		break;
528 #endif
529 #if defined(INET6) && (defined(__linux__) || defined(HAVE_PLEDGE))
530 	case PS_IP6FORWARDING:
531 		 err = ip6_forwarding(data);
532 		 break;
533 #endif
534 #ifdef PLUGIN_DEV
535 	case PS_DEV_INITTED:
536 		err = dev_initialized(ctx, data);
537 		break;
538 	case PS_DEV_LISTENING:
539 		err = dev_listening(ctx);
540 		break;
541 #endif
542 	default:
543 		err = ps_root_os(psm, msg);
544 		break;
545 	}
546 
547 	err = ps_root_writeerror(ctx, err, rlen != 0 ? rdata : 0, rlen);
548 	if (free_rdata)
549 		free(rdata);
550 	return err;
551 }
552 
553 /* Receive from state engine, do an action. */
554 static void
555 ps_root_recvmsg(void *arg)
556 {
557 	struct dhcpcd_ctx *ctx = arg;
558 
559 	if (ps_recvpsmsg(ctx, ctx->ps_root_fd, ps_root_recvmsgcb, ctx) == -1 &&
560 	    errno != ECONNRESET)
561 		logerr(__func__);
562 }
563 
564 #ifdef PLUGIN_DEV
565 static int
566 ps_root_handleinterface(void *arg, int action, const char *ifname)
567 {
568 	struct dhcpcd_ctx *ctx = arg;
569 	unsigned long flag;
570 
571 	if (action == 1)
572 		flag = PS_DEV_IFADDED;
573 	else if (action == -1)
574 		flag = PS_DEV_IFREMOVED;
575 	else if (action == 0)
576 		flag = PS_DEV_IFUPDATED;
577 	else {
578 		errno = EINVAL;
579 		return -1;
580 	}
581 
582 	return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD, flag,
583 	    ifname, strlen(ifname) + 1);
584 }
585 #endif
586 
587 static int
588 ps_root_startcb(void *arg)
589 {
590 	struct dhcpcd_ctx *ctx = arg;
591 
592 	if (ctx->options & DHCPCD_MASTER)
593 		setproctitle("[privileged actioneer]");
594 	else
595 		setproctitle("[privileged actioneer] %s%s%s",
596 		    ctx->ifv[0],
597 		    ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
598 		    ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
599 	ctx->ps_root_pid = getpid();
600 	ctx->options |= DHCPCD_PRIVSEPROOT;
601 
602 	/* Open network sockets for sending.
603 	 * This is a small bit wasteful for non sandboxed OS's
604 	 * but makes life very easy for unicasting DHCPv6 in non master
605 	 * mode as we no longer care about address selection. */
606 #ifdef INET
607 	ctx->udp_wfd = xsocket(PF_INET, SOCK_RAW | SOCK_CXNB, IPPROTO_UDP);
608 	if (ctx->udp_wfd == -1)
609 		return -1;
610 #endif
611 #ifdef INET6
612 	ctx->nd_fd = ipv6nd_open(false);
613 	if (ctx->nd_fd == -1)
614 		return -1;
615 #endif
616 #ifdef DHCP6
617 	ctx->dhcp6_wfd = dhcp6_openraw();
618 	if (ctx->dhcp6_wfd == -1)
619 		return -1;
620 #endif
621 
622 #ifdef PLUGIN_DEV
623 	/* Start any dev listening plugin which may want to
624 	 * change the interface name provided by the kernel */
625 	if ((ctx->options & (DHCPCD_MASTER | DHCPCD_DEV)) ==
626 	    (DHCPCD_MASTER | DHCPCD_DEV))
627 		dev_start(ctx, ps_root_handleinterface);
628 #endif
629 
630 	return 0;
631 }
632 
633 static void
634 ps_root_signalcb(int sig, void *arg)
635 {
636 	struct dhcpcd_ctx *ctx = arg;
637 
638 	/* Ignore SIGINT, respect PS_STOP command or SIGTERM. */
639 	if (sig == SIGINT)
640 		return;
641 
642 	logerrx("process %d unexpectedly terminating on signal %d",
643 	    getpid(), sig);
644 	if (ctx->ps_root_pid == getpid()) {
645 		shutdown(ctx->ps_root_fd, SHUT_RDWR);
646 		shutdown(ctx->ps_data_fd, SHUT_RDWR);
647 	}
648 	eloop_exit(ctx->eloop, sig == SIGTERM ? EXIT_SUCCESS : EXIT_FAILURE);
649 }
650 
651 int (*handle_interface)(void *, int, const char *);
652 
653 #ifdef PLUGIN_DEV
654 static ssize_t
655 ps_root_devcb(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
656 {
657 	int action;
658 	struct iovec *iov = msg->msg_iov;
659 
660 	if (msg->msg_iovlen != 1) {
661 		errno = EINVAL;
662 		return -1;
663 	}
664 
665 	switch(psm->ps_flags) {
666 	case PS_DEV_IFADDED:
667 		action = 1;
668 		break;
669 	case PS_DEV_IFREMOVED:
670 		action = -1;
671 		break;
672 	case PS_DEV_IFUPDATED:
673 		action = 0;
674 		break;
675 	default:
676 		errno = EINVAL;
677 		return -1;
678 	}
679 
680 	return dhcpcd_handleinterface(ctx, action, iov->iov_base);
681 }
682 #endif
683 
684 static ssize_t
685 ps_root_dispatchcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
686 {
687 	struct dhcpcd_ctx *ctx = arg;
688 	ssize_t err;
689 
690 	switch(psm->ps_cmd) {
691 #ifdef PLUGIN_DEV
692 	case PS_DEV_IFCMD:
693 		err = ps_root_devcb(ctx, psm, msg);
694 		break;
695 #endif
696 	default:
697 #ifdef INET
698 		err = ps_bpf_dispatch(ctx, psm, msg);
699 		if (err == -1 && errno == ENOTSUP)
700 #endif
701 			err = ps_inet_dispatch(ctx, psm, msg);
702 	}
703 	return err;
704 }
705 
706 static void
707 ps_root_dispatch(void *arg)
708 {
709 	struct dhcpcd_ctx *ctx = arg;
710 
711 	if (ps_recvpsmsg(ctx, ctx->ps_data_fd, ps_root_dispatchcb, ctx) == -1)
712 		logerr(__func__);
713 }
714 
715 pid_t
716 ps_root_start(struct dhcpcd_ctx *ctx)
717 {
718 	int fd[2];
719 	pid_t pid;
720 
721 	if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1)
722 		return -1;
723 
724 	pid = ps_dostart(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd,
725 	    ps_root_recvmsg, NULL, ctx,
726 	    ps_root_startcb, ps_root_signalcb, 0);
727 
728 	if (pid == 0) {
729 		ctx->ps_data_fd = fd[1];
730 		close(fd[0]);
731 		return 0;
732 	} else if (pid == -1)
733 		return -1;
734 
735 	ctx->ps_data_fd = fd[0];
736 	close(fd[1]);
737 	if (eloop_event_add(ctx->eloop, ctx->ps_data_fd,
738 	    ps_root_dispatch, ctx) == -1)
739 		return -1;
740 
741 	if ((ctx->ps_eloop = eloop_new()) == NULL)
742 		return -1;
743 
744 	if (eloop_signal_set_cb(ctx->ps_eloop,
745 	    dhcpcd_signals, dhcpcd_signals_len,
746 	    ps_root_readerrorsig, ctx) == -1)
747 		return -1;
748 
749 	return pid;
750 }
751 
752 int
753 ps_root_stop(struct dhcpcd_ctx *ctx)
754 {
755 
756 	return ps_dostop(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd);
757 }
758 
759 ssize_t
760 ps_root_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
761 {
762 
763 	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_SCRIPT, 0, data, len) == -1)
764 		return -1;
765 	return ps_root_readerror(ctx, NULL, 0);
766 }
767 
768 ssize_t
769 ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,
770     size_t len)
771 {
772 #ifdef IOCTL_REQUEST_TYPE
773 	unsigned long ulreq = 0;
774 
775 	memcpy(&ulreq, &req, sizeof(req));
776 	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, ulreq, data, len) == -1)
777 		return -1;
778 #else
779 	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, req, data, len) == -1)
780 		return -1;
781 #endif
782 	return ps_root_readerror(ctx, data, len);
783 }
784 
785 ssize_t
786 ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file)
787 {
788 
789 	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_UNLINK, 0,
790 	    file, strlen(file) + 1) == -1)
791 		return -1;
792 	return ps_root_readerror(ctx, NULL, 0);
793 }
794 
795 ssize_t
796 ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file,
797     void *data, size_t len)
798 {
799 	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_READFILE, 0,
800 	    file, strlen(file) + 1) == -1)
801 		return -1;
802 	return ps_root_readerror(ctx, data, len);
803 }
804 
805 ssize_t
806 ps_root_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
807     const void *data, size_t len)
808 {
809 	char buf[PS_BUFLEN];
810 	size_t flen;
811 
812 	flen = strlcpy(buf, file, sizeof(buf));
813 	flen += 1;
814 	if (flen > sizeof(buf) || flen + len > sizeof(buf)) {
815 		errno = ENOBUFS;
816 		return -1;
817 	}
818 	memcpy(buf + flen, data, len);
819 
820 	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_WRITEFILE, mode,
821 	    buf, flen + len) == -1)
822 		return -1;
823 	return ps_root_readerror(ctx, NULL, 0);
824 }
825 
826 ssize_t
827 ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
828 {
829 
830 	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_FILEMTIME, 0,
831 	    file, strlen(file) + 1) == -1)
832 		return -1;
833 	return ps_root_readerror(ctx, time, sizeof(*time));
834 }
835 
836 #ifdef HAVE_CAPSICUM
837 int
838 ps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead)
839 {
840 	struct ifaddrs *ifa;
841 	void *buf = NULL;
842 	char *bp, *sap;
843 	socklen_t salen;
844 	size_t len;
845 	ssize_t err;
846 
847 	if (ps_sendcmd(ctx, ctx->ps_root_fd,
848 	    PS_GETIFADDRS, 0, NULL, 0) == -1)
849 		return -1;
850 	err = ps_root_mreaderror(ctx, &buf, &len);
851 
852 	if (err == -1)
853 		return -1;
854 
855 	/* Should be impossible - lo0 will always exist. */
856 	if (len == 0) {
857 		*ifahead = NULL;
858 		return 0;
859 	}
860 
861 	bp = buf;
862 	*ifahead = (struct ifaddrs *)(void *)bp;
863 	for (ifa = *ifahead; len != 0; ifa = ifa->ifa_next) {
864 		if (len < ALIGN(sizeof(*ifa)) +
865 		    ALIGN(IFNAMSIZ) + ALIGN(sizeof(salen) * IFA_NADDRS))
866 			goto err;
867 		bp += ALIGN(sizeof(*ifa));
868 		ifa->ifa_name = bp;
869 		bp += ALIGN(IFNAMSIZ);
870 		sap = bp;
871 		bp += ALIGN(sizeof(salen) * IFA_NADDRS);
872 		len -= ALIGN(sizeof(*ifa)) +
873 		    ALIGN(IFNAMSIZ) + ALIGN(sizeof(salen) * IFA_NADDRS);
874 
875 #define	COPYOUTSA(addr)						\
876 	do {							\
877 		memcpy(&salen, sap, sizeof(salen));		\
878 		if (len < salen)				\
879 			goto err;				\
880 		if (salen != 0) {				\
881 			(addr) = (struct sockaddr *)bp;		\
882 			bp += ALIGN(salen);			\
883 			len -= ALIGN(salen);			\
884 		}						\
885 		sap += sizeof(salen);				\
886 	} while (0 /* CONSTCOND */)
887 
888 		COPYOUTSA(ifa->ifa_addr);
889 		COPYOUTSA(ifa->ifa_netmask);
890 		COPYOUTSA(ifa->ifa_broadaddr);
891 		ifa->ifa_next = (struct ifaddrs *)(void *)bp;
892 	}
893 	ifa->ifa_next = NULL;
894 	return 0;
895 
896 err:
897 	free(buf);
898 	*ifahead = NULL;
899 	errno = EINVAL;
900 	return -1;
901 }
902 #endif
903 
904 #if defined(__linux__) || defined(HAVE_PLEDGE)
905 ssize_t
906 ps_root_ip6forwarding(struct dhcpcd_ctx *ctx, const char *ifname)
907 {
908 
909 	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IP6FORWARDING, 0,
910 	    ifname, ifname != NULL ? strlen(ifname) + 1 : 0) == -1)
911 		return -1;
912 	return ps_root_readerror(ctx, NULL, 0);
913 }
914 #endif
915 
916 #ifdef PLUGIN_DEV
917 int
918 ps_root_dev_initialized(struct dhcpcd_ctx *ctx, const char *ifname)
919 {
920 
921 	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_DEV_INITTED, 0,
922 	    ifname, strlen(ifname) + 1)== -1)
923 		return -1;
924 	return (int)ps_root_readerror(ctx, NULL, 0);
925 }
926 
927 int
928 ps_root_dev_listening(struct dhcpcd_ctx * ctx)
929 {
930 
931 	if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_DEV_LISTENING, 0, NULL, 0)== -1)
932 		return -1;
933 	return (int)ps_root_readerror(ctx, NULL, 0);
934 }
935 #endif
936