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