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