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