xref: /dragonfly/contrib/dhcpcd/src/privsep.c (revision 5ca0a96d)
1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3  * Privilege Separation for dhcpcd
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 /*
30  * The current design is this:
31  * Spawn a priv process to carry out privileged actions and
32  * spawning unpriv process to initate network connections such as BPF
33  * or address specific listener.
34  * Spawn an unpriv process to send/receive common network data.
35  * Then drop all privs and start running.
36  * Every process aside from the privileged actioneer is chrooted.
37  * All privsep processes ignore signals - only the master process accepts them.
38  *
39  * dhcpcd will maintain the config file in the chroot, no need to handle
40  * this in a script or something.
41  */
42 
43 #include <sys/resource.h>
44 #include <sys/socket.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include <sys/wait.h>
48 
49 #ifdef AF_LINK
50 #include <net/if_dl.h>
51 #endif
52 
53 #include <assert.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <grp.h>
57 #include <paths.h>
58 #include <pwd.h>
59 #include <stddef.h>	/* For offsetof, struct padding debug */
60 #include <signal.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <unistd.h>
64 
65 #include "arp.h"
66 #include "common.h"
67 #include "control.h"
68 #include "dev.h"
69 #include "dhcp.h"
70 #include "dhcp6.h"
71 #include "eloop.h"
72 #include "ipv6nd.h"
73 #include "logerr.h"
74 #include "privsep.h"
75 
76 #ifdef HAVE_CAPSICUM
77 #include <sys/capsicum.h>
78 #include <capsicum_helpers.h>
79 #endif
80 #ifdef HAVE_UTIL_H
81 #include <util.h>
82 #endif
83 
84 int
85 ps_init(struct dhcpcd_ctx *ctx)
86 {
87 	struct passwd *pw;
88 	struct stat st;
89 
90 	errno = 0;
91 	if ((ctx->ps_user = pw = getpwnam(PRIVSEP_USER)) == NULL) {
92 		ctx->options &= ~DHCPCD_PRIVSEP;
93 		if (errno == 0) {
94 			logerrx("no such user %s", PRIVSEP_USER);
95 			/* Just incase logerrx caused an error... */
96 			errno = 0;
97 		} else
98 			logerr("getpwnam");
99 		return -1;
100 	}
101 
102 	if (stat(pw->pw_dir, &st) == -1 || !S_ISDIR(st.st_mode)) {
103 		ctx->options &= ~DHCPCD_PRIVSEP;
104 		logerrx("refusing chroot: %s: %s",
105 		    PRIVSEP_USER, pw->pw_dir);
106 		errno = 0;
107 		return -1;
108 	}
109 
110 	ctx->options |= DHCPCD_PRIVSEP;
111 	return 0;
112 }
113 
114 static int
115 ps_dropprivs(struct dhcpcd_ctx *ctx)
116 {
117 	struct passwd *pw = ctx->ps_user;
118 
119 	if (ctx->options & DHCPCD_LAUNCHER)
120 		logdebugx("chrooting as %s to %s", pw->pw_name, pw->pw_dir);
121 	if (chroot(pw->pw_dir) == -1 &&
122 	    (errno != EPERM || ctx->options & DHCPCD_FORKED))
123 		logerr("%s: chroot: %s", __func__, pw->pw_dir);
124 	if (chdir("/") == -1)
125 		logerr("%s: chdir: /", __func__);
126 
127 	if ((setgroups(1, &pw->pw_gid) == -1 ||
128 	     setgid(pw->pw_gid) == -1 ||
129 	     setuid(pw->pw_uid) == -1) &&
130 	     (errno != EPERM || ctx->options & DHCPCD_FORKED))
131 	{
132 		logerr("failed to drop privileges");
133 		return -1;
134 	}
135 
136 	struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 };
137 
138 	if (ctx->ps_control_pid != getpid()) {
139 		/* Prohibit new files, sockets, etc */
140 #if defined(__linux__) || defined(__sun) || defined(__OpenBSD__)
141 		/*
142 		 * If poll(2) is called with nfds > RLIMIT_NOFILE
143 		 * then it returns EINVAL.
144 		 * This blows.
145 		 * Do the best we can and limit to what we need.
146 		 * An attacker could potentially close a file and
147 		 * open a new one still, but that cannot be helped.
148 		 */
149 		unsigned long maxfd;
150 		maxfd = (unsigned long)eloop_event_count(ctx->eloop);
151 		if (IN_PRIVSEP_SE(ctx))
152 			maxfd++; /* XXX why? */
153 
154 		struct rlimit rmaxfd = {
155 		    .rlim_cur = maxfd,
156 		    .rlim_max = maxfd
157 		};
158 		if (setrlimit(RLIMIT_NOFILE, &rmaxfd) == -1)
159 			logerr("setrlimit RLIMIT_NOFILE");
160 #else
161 		if (setrlimit(RLIMIT_NOFILE, &rzero) == -1)
162 			logerr("setrlimit RLIMIT_NOFILE");
163 #endif
164 	}
165 
166 #define DHC_NOCHKIO	(DHCPCD_STARTED | DHCPCD_DAEMONISE)
167 	/* Prohibit writing to files.
168 	 * Obviously this won't work if we are using a logfile
169 	 * or redirecting stderr to a file. */
170 	if ((ctx->options & DHC_NOCHKIO) == DHC_NOCHKIO ||
171 	    (ctx->logfile == NULL &&
172 	    (!ctx->stderr_valid || isatty(STDERR_FILENO) == 1)))
173 	{
174 		if (setrlimit(RLIMIT_FSIZE, &rzero) == -1)
175 			logerr("setrlimit RLIMIT_FSIZE");
176 	}
177 
178 #ifdef RLIMIT_NPROC
179 	/* Prohibit forks */
180 	if (setrlimit(RLIMIT_NPROC, &rzero) == -1)
181 		logerr("setrlimit RLIMIT_NPROC");
182 #endif
183 
184 	return 0;
185 }
186 
187 static int
188 ps_setbuf0(int fd, int ctl, int minlen)
189 {
190 	int len;
191 	socklen_t slen;
192 
193 	slen = sizeof(len);
194 	if (getsockopt(fd, SOL_SOCKET, ctl, &len, &slen) == -1)
195 		return -1;
196 
197 #ifdef __linux__
198 	len /= 2;
199 #endif
200 	if (len >= minlen)
201 		return 0;
202 
203 	return setsockopt(fd, SOL_SOCKET, ctl, &minlen, sizeof(minlen));
204 }
205 
206 static int
207 ps_setbuf(int fd)
208 {
209 	/* Ensure we can receive a fully sized privsep message.
210 	 * Double the send buffer. */
211 	int minlen = (int)sizeof(struct ps_msg);
212 
213 	if (ps_setbuf0(fd, SO_RCVBUF, minlen) == -1 ||
214 	    ps_setbuf0(fd, SO_SNDBUF, minlen * 2) == -1)
215 	{
216 		logerr(__func__);
217 		return -1;
218 	}
219 	return 0;
220 }
221 
222 int
223 ps_setbuf_fdpair(int fd[])
224 {
225 
226 	if (ps_setbuf(fd[0]) == -1 || ps_setbuf(fd[1]) == -1)
227 		return -1;
228 	return 0;
229 }
230 
231 #ifdef PRIVSEP_RIGHTS
232 int
233 ps_rights_limit_ioctl(int fd)
234 {
235 	cap_rights_t rights;
236 
237 	cap_rights_init(&rights, CAP_IOCTL);
238 	if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
239 		return -1;
240 	return 0;
241 }
242 
243 int
244 ps_rights_limit_fd_fctnl(int fd)
245 {
246 	cap_rights_t rights;
247 
248 	cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT,
249 	    CAP_ACCEPT, CAP_FCNTL);
250 	if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
251 		return -1;
252 	return 0;
253 }
254 
255 int
256 ps_rights_limit_fd(int fd)
257 {
258 	cap_rights_t rights;
259 
260 	cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN);
261 	if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
262 		return -1;
263 	return 0;
264 }
265 
266 int
267 ps_rights_limit_fd_sockopt(int fd)
268 {
269 	cap_rights_t rights;
270 
271 	cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT,
272 	    CAP_GETSOCKOPT, CAP_SETSOCKOPT);
273 	if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
274 		return -1;
275 	return 0;
276 }
277 
278 int
279 ps_rights_limit_fd_rdonly(int fd)
280 {
281 	cap_rights_t rights;
282 
283 	cap_rights_init(&rights, CAP_READ, CAP_EVENT);
284 	if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
285 		return -1;
286 	return 0;
287 }
288 
289 int
290 ps_rights_limit_fdpair(int fd[])
291 {
292 
293 	if (ps_rights_limit_fd(fd[0]) == -1 || ps_rights_limit_fd(fd[1]) == -1)
294 		return -1;
295 	return 0;
296 }
297 
298 static int
299 ps_rights_limit_stdio(struct dhcpcd_ctx *ctx)
300 {
301 	const int iebadf = CAPH_IGNORE_EBADF;
302 	int error = 0;
303 
304 	if (ctx->stdin_valid &&
305 	    caph_limit_stream(STDIN_FILENO, CAPH_READ | iebadf) == -1)
306 		error = -1;
307 	if (ctx->stdout_valid &&
308 	    caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | iebadf) == -1)
309 		error = -1;
310 	if (ctx->stderr_valid &&
311 	    caph_limit_stream(STDERR_FILENO, CAPH_WRITE | iebadf) == -1)
312 		error = -1;
313 
314 	return error;
315 }
316 #endif
317 
318 pid_t
319 ps_dostart(struct dhcpcd_ctx *ctx,
320     pid_t *priv_pid, int *priv_fd,
321     void (*recv_msg)(void *), void (*recv_unpriv_msg),
322     void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *),
323     unsigned int flags)
324 {
325 	int fd[2];
326 	pid_t pid;
327 
328 	if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1) {
329 		logerr("%s: socketpair", __func__);
330 		return -1;
331 	}
332 	if (ps_setbuf_fdpair(fd) == -1) {
333 		logerr("%s: ps_setbuf_fdpair", __func__);
334 		return -1;
335 	}
336 #ifdef PRIVSEP_RIGHTS
337 	if (ps_rights_limit_fdpair(fd) == -1) {
338 		logerr("%s: ps_rights_limit_fdpair", __func__);
339 		return -1;
340 	}
341 #endif
342 
343 	switch (pid = fork()) {
344 	case -1:
345 		logerr("fork");
346 		return -1;
347 	case 0:
348 		*priv_fd = fd[1];
349 		close(fd[0]);
350 		break;
351 	default:
352 		*priv_pid = pid;
353 		*priv_fd = fd[0];
354 		close(fd[1]);
355 		if (recv_unpriv_msg == NULL)
356 			;
357 		else if (eloop_event_add(ctx->eloop, *priv_fd,
358 		    recv_unpriv_msg, recv_ctx) == -1)
359 		{
360 			logerr("%s: eloop_event_add", __func__);
361 			return -1;
362 		}
363 		return pid;
364 	}
365 
366 	ctx->options |= DHCPCD_FORKED;
367 	if (ctx->fork_fd != -1) {
368 		close(ctx->fork_fd);
369 		ctx->fork_fd = -1;
370 	}
371 	pidfile_clean();
372 	eloop_clear(ctx->eloop);
373 
374 	/* We are not root */
375 	if (priv_fd != &ctx->ps_root_fd) {
376 		ps_freeprocesses(ctx, recv_ctx);
377 		if (ctx->ps_root_fd != -1) {
378 			close(ctx->ps_root_fd);
379 			ctx->ps_root_fd = -1;
380 		}
381 
382 #ifdef PRIVSEP_RIGHTS
383 		/* We cannot limit the root process in any way. */
384 		if (ps_rights_limit_stdio(ctx) == -1) {
385 			logerr("ps_rights_limit_stdio");
386 			goto errexit;
387 		}
388 #endif
389 	}
390 
391 	if (priv_fd != &ctx->ps_inet_fd && ctx->ps_inet_fd != -1) {
392 		close(ctx->ps_inet_fd);
393 		ctx->ps_inet_fd = -1;
394 	}
395 
396 	eloop_signal_set_cb(ctx->eloop,
397 	    dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx);
398 
399 	/* ctx->sigset aready has the initial sigmask set in main() */
400 	if (eloop_signal_mask(ctx->eloop, NULL) == -1) {
401 		logerr("%s: eloop_signal_mask", __func__);
402 		goto errexit;
403 	}
404 
405 	if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1)
406 	{
407 		logerr("%s: eloop_event_add", __func__);
408 		goto errexit;
409 	}
410 
411 	if (callback(recv_ctx) == -1)
412 		goto errexit;
413 
414 	if (flags & PSF_DROPPRIVS)
415 		ps_dropprivs(ctx);
416 
417 	return 0;
418 
419 errexit:
420 	/* Failure to start root or inet processes is fatal. */
421 	if (priv_fd == &ctx->ps_root_fd || priv_fd == &ctx->ps_inet_fd)
422 		(void)ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0);
423 	shutdown(*priv_fd, SHUT_RDWR);
424 	*priv_fd = -1;
425 	eloop_exit(ctx->eloop, EXIT_FAILURE);
426 	return -1;
427 }
428 
429 int
430 ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd)
431 {
432 	int err = 0;
433 
434 #ifdef PRIVSEP_DEBUG
435 	logdebugx("%s: pid=%d fd=%d", __func__, *pid, *fd);
436 #endif
437 
438 	if (*fd != -1) {
439 		eloop_event_delete(ctx->eloop, *fd);
440 		if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1) {
441 			logerr(__func__);
442 			err = -1;
443 		}
444 		(void)shutdown(*fd, SHUT_RDWR);
445 		close(*fd);
446 		*fd = -1;
447 	}
448 
449 	/* Don't wait for the process as it may not respond to the shutdown
450 	 * request. We'll reap the process on receipt of SIGCHLD. */
451 	*pid = 0;
452 	return err;
453 }
454 
455 int
456 ps_start(struct dhcpcd_ctx *ctx)
457 {
458 	pid_t pid;
459 
460 	TAILQ_INIT(&ctx->ps_processes);
461 
462 	switch (pid = ps_root_start(ctx)) {
463 	case -1:
464 		logerr("ps_root_start");
465 		return -1;
466 	case 0:
467 		return 0;
468 	default:
469 		logdebugx("spawned privileged actioneer on PID %d", pid);
470 	}
471 
472 	/* No point in spawning the generic network listener if we're
473 	 * not going to use it. */
474 	if (!ps_inet_canstart(ctx))
475 		goto started_net;
476 
477 	switch (pid = ps_inet_start(ctx)) {
478 	case -1:
479 		return -1;
480 	case 0:
481 		return 0;
482 	default:
483 		logdebugx("spawned network proxy on PID %d", pid);
484 	}
485 
486 started_net:
487 	if (!(ctx->options & DHCPCD_TEST)) {
488 		switch (pid = ps_ctl_start(ctx)) {
489 		case -1:
490 			return -1;
491 		case 0:
492 			return 0;
493 		default:
494 			logdebugx("spawned controller proxy on PID %d", pid);
495 		}
496 	}
497 
498 #ifdef ARC4RANDOM_H
499 	/* Seed the random number generator early incase it needs /dev/urandom
500 	 * which won't be available in the chroot. */
501 	arc4random();
502 #endif
503 
504 	return 1;
505 }
506 
507 int
508 ps_entersandbox(const char *_pledge, const char **sandbox)
509 {
510 
511 #if !defined(HAVE_PLEDGE)
512 	UNUSED(_pledge);
513 #endif
514 
515 #if defined(HAVE_CAPSICUM)
516 	if (sandbox != NULL)
517 		*sandbox = "capsicum";
518 	return cap_enter();
519 #elif defined(HAVE_PLEDGE)
520 	if (sandbox != NULL)
521 		*sandbox = "pledge";
522 	return pledge(_pledge, NULL);
523 #elif defined(HAVE_SECCOMP)
524 	if (sandbox != NULL)
525 		*sandbox = "seccomp";
526 	return ps_seccomp_enter();
527 #else
528 	if (sandbox != NULL)
529 		*sandbox = "posix resource limited";
530 	return 0;
531 #endif
532 }
533 
534 int
535 ps_mastersandbox(struct dhcpcd_ctx *ctx, const char *_pledge)
536 {
537 	const char *sandbox = NULL;
538 	bool forked;
539 	int dropped;
540 
541 	forked = ctx->options & DHCPCD_FORKED;
542 	ctx->options &= ~DHCPCD_FORKED;
543 	dropped = ps_dropprivs(ctx);
544 	if (forked)
545 		ctx->options |= DHCPCD_FORKED;
546 
547 	/*
548 	 * If we don't have a root process, we cannot use syslog.
549 	 * If it cannot be opened before chrooting then syslog(3) will fail.
550 	 * openlog(3) does not return an error which doubly sucks.
551 	 */
552 	if (ctx->ps_root_fd == -1) {
553 		unsigned int logopts = loggetopts();
554 
555 		logopts &= ~LOGERR_LOG;
556 		logsetopts(logopts);
557 	}
558 
559 	if (dropped == -1) {
560 		logerr("%s: ps_dropprivs", __func__);
561 		return -1;
562 	}
563 
564 #ifdef PRIVSEP_RIGHTS
565 	if ((ctx->pf_inet_fd != -1 &&
566 	    ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1) ||
567 	     ps_rights_limit_stdio(ctx) == -1)
568 	{
569 		logerr("%s: cap_rights_limit", __func__);
570 		return -1;
571 	}
572 #endif
573 
574 	if (_pledge == NULL)
575 		_pledge = "stdio";
576 	if (ps_entersandbox(_pledge, &sandbox) == -1) {
577 		if (errno == ENOSYS) {
578 			if (sandbox != NULL)
579 				logwarnx("sandbox unavailable: %s", sandbox);
580 			return 0;
581 		}
582 		logerr("%s: %s", __func__, sandbox);
583 		return -1;
584 	} else if (ctx->options & DHCPCD_LAUNCHER ||
585 		  ((!(ctx->options & DHCPCD_DAEMONISE)) &&
586 		   ctx->options & DHCPCD_MASTER))
587 		logdebugx("sandbox: %s", sandbox);
588 	return 0;
589 }
590 
591 int
592 ps_stop(struct dhcpcd_ctx *ctx)
593 {
594 	int r, ret = 0;
595 
596 	if (!(ctx->options & DHCPCD_PRIVSEP) ||
597 	    ctx->options & DHCPCD_FORKED ||
598 	    ctx->eloop == NULL)
599 		return 0;
600 
601 	r = ps_ctl_stop(ctx);
602 	if (r != 0)
603 		ret = r;
604 
605 	r = ps_inet_stop(ctx);
606 	if (r != 0)
607 		ret = r;
608 
609 	/* We've been chrooted, so we need to tell the
610 	 * privileged actioneer to remove the pidfile. */
611 	ps_root_unlink(ctx, ctx->pidfile);
612 
613 	r = ps_root_stop(ctx);
614 	if (r != 0)
615 		ret = r;
616 
617 	ctx->options &= ~DHCPCD_PRIVSEP;
618 	return ret;
619 }
620 
621 void
622 ps_freeprocess(struct ps_process *psp)
623 {
624 
625 	TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next);
626 	if (psp->psp_fd != -1) {
627 		eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd);
628 		close(psp->psp_fd);
629 	}
630 	if (psp->psp_work_fd != -1) {
631 		eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd);
632 		close(psp->psp_work_fd);
633 	}
634 #ifdef INET
635 	if (psp->psp_bpf != NULL)
636 		bpf_close(psp->psp_bpf);
637 #endif
638 	free(psp);
639 }
640 
641 static void
642 ps_free(struct dhcpcd_ctx *ctx)
643 {
644 	struct ps_process *psp;
645 	bool stop = ctx->ps_root_pid == getpid();
646 
647 	while ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) {
648 		if (stop)
649 			ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
650 		ps_freeprocess(psp);
651 	}
652 }
653 
654 int
655 ps_unrollmsg(struct msghdr *msg, struct ps_msghdr *psm,
656     const void *data, size_t len)
657 {
658 	uint8_t *datap, *namep, *controlp;
659 
660 	namep = UNCONST(data);
661 	controlp = namep + psm->ps_namelen;
662 	datap = controlp + psm->ps_controllen;
663 
664 	if (psm->ps_namelen != 0) {
665 		if (psm->ps_namelen > len) {
666 			errno = EINVAL;
667 			return -1;
668 		}
669 		msg->msg_name = namep;
670 		len -= psm->ps_namelen;
671 	} else
672 		msg->msg_name = NULL;
673 	msg->msg_namelen = psm->ps_namelen;
674 
675 	if (psm->ps_controllen != 0) {
676 		if (psm->ps_controllen > len) {
677 			errno = EINVAL;
678 			return -1;
679 		}
680 		msg->msg_control = controlp;
681 		len -= psm->ps_controllen;
682 	} else
683 		msg->msg_control = NULL;
684 	msg->msg_controllen = psm->ps_controllen;
685 
686 	if (len != 0) {
687 		msg->msg_iovlen = 1;
688 		msg->msg_iov[0].iov_base = datap;
689 		msg->msg_iov[0].iov_len = len;
690 	} else {
691 		msg->msg_iovlen = 0;
692 		msg->msg_iov[0].iov_base = NULL;
693 		msg->msg_iov[0].iov_len = 0;
694 	}
695 	return 0;
696 }
697 
698 ssize_t
699 ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd,
700     struct ps_msghdr *psm, const struct msghdr *msg)
701 {
702 	struct iovec iov[] = {
703 		{ .iov_base = UNCONST(psm), .iov_len = sizeof(*psm) },
704 		{ .iov_base = NULL, },	/* name */
705 		{ .iov_base = NULL, },	/* control */
706 		{ .iov_base = NULL, },	/* payload 1 */
707 		{ .iov_base = NULL, },	/* payload 2 */
708 		{ .iov_base = NULL, },	/* payload 3 */
709 	};
710 	int iovlen;
711 	ssize_t len;
712 
713 	if (msg != NULL) {
714 		struct iovec *iovp = &iov[1];
715 		int i;
716 
717 		psm->ps_namelen = msg->msg_namelen;
718 		psm->ps_controllen = (socklen_t)msg->msg_controllen;
719 
720 		iovp->iov_base = msg->msg_name;
721 		iovp->iov_len = msg->msg_namelen;
722 		iovp++;
723 		iovp->iov_base = msg->msg_control;
724 		iovp->iov_len = msg->msg_controllen;
725 		iovlen = 3;
726 
727 		for (i = 0; i < (int)msg->msg_iovlen; i++) {
728 			if ((size_t)(iovlen + i) > __arraycount(iov)) {
729 				errno =	ENOBUFS;
730 				return -1;
731 			}
732 			iovp++;
733 			iovp->iov_base = msg->msg_iov[i].iov_base;
734 			iovp->iov_len = msg->msg_iov[i].iov_len;
735 		}
736 		iovlen += i;
737 	} else
738 		iovlen = 1;
739 
740 	len = writev(fd, iov, iovlen);
741 	if (len == -1) {
742 		logerr(__func__);
743 		if (ctx->options & DHCPCD_FORKED &&
744 		    !(ctx->options & DHCPCD_PRIVSEPROOT))
745 			eloop_exit(ctx->eloop, EXIT_FAILURE);
746 	}
747 	return len;
748 }
749 
750 ssize_t
751 ps_sendpsmdata(struct dhcpcd_ctx *ctx, int fd,
752     struct ps_msghdr *psm, const void *data, size_t len)
753 {
754 	struct iovec iov[] = {
755 		{ .iov_base = UNCONST(data), .iov_len = len },
756 	};
757 	struct msghdr msg = {
758 		.msg_iov = iov, .msg_iovlen = 1,
759 	};
760 
761 	return ps_sendpsmmsg(ctx, fd, psm, &msg);
762 }
763 
764 
765 ssize_t
766 ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
767     const struct msghdr *msg)
768 {
769 	struct ps_msghdr psm = {
770 		.ps_cmd = cmd,
771 		.ps_flags = flags,
772 		.ps_namelen = msg->msg_namelen,
773 		.ps_controllen = (socklen_t)msg->msg_controllen,
774 	};
775 	size_t i;
776 
777 	for (i = 0; i < (size_t)msg->msg_iovlen; i++)
778 		psm.ps_datalen += msg->msg_iov[i].iov_len;
779 
780 #if 0	/* For debugging structure padding. */
781 	logerrx("psa.family %lu %zu", offsetof(struct ps_addr, psa_family), sizeof(psm.ps_id.psi_addr.psa_family));
782 	logerrx("psa.pad %lu %zu", offsetof(struct ps_addr, psa_pad), sizeof(psm.ps_id.psi_addr.psa_pad));
783 	logerrx("psa.psa_u %lu %zu", offsetof(struct ps_addr, psa_u), sizeof(psm.ps_id.psi_addr.psa_u));
784 	logerrx("psa %zu", sizeof(psm.ps_id.psi_addr));
785 
786 	logerrx("psi.addr %lu %zu", offsetof(struct ps_id, psi_addr), sizeof(psm.ps_id.psi_addr));
787 	logerrx("psi.index %lu %zu", offsetof(struct ps_id, psi_ifindex), sizeof(psm.ps_id.psi_ifindex));
788 	logerrx("psi.cmd %lu %zu", offsetof(struct ps_id, psi_cmd), sizeof(psm.ps_id.psi_cmd));
789 	logerrx("psi.pad %lu %zu", offsetof(struct ps_id, psi_pad), sizeof(psm.ps_id.psi_pad));
790 	logerrx("psi %zu", sizeof(struct ps_id));
791 
792 	logerrx("ps_cmd %lu", offsetof(struct ps_msghdr, ps_cmd));
793 	logerrx("ps_pad %lu %zu", offsetof(struct ps_msghdr, ps_pad), sizeof(psm.ps_pad));
794 	logerrx("ps_flags %lu %zu", offsetof(struct ps_msghdr, ps_flags), sizeof(psm.ps_flags));
795 
796 	logerrx("ps_id %lu %zu", offsetof(struct ps_msghdr, ps_id), sizeof(psm.ps_id));
797 
798 	logerrx("ps_namelen %lu %zu", offsetof(struct ps_msghdr, ps_namelen), sizeof(psm.ps_namelen));
799 	logerrx("ps_controllen %lu %zu", offsetof(struct ps_msghdr, ps_controllen), sizeof(psm.ps_controllen));
800 	logerrx("ps_pad2 %lu %zu", offsetof(struct ps_msghdr, ps_pad2), sizeof(psm.ps_pad2));
801 	logerrx("ps_datalen %lu %zu", offsetof(struct ps_msghdr, ps_datalen), sizeof(psm.ps_datalen));
802 	logerrx("psm %zu", sizeof(psm));
803 #endif
804 
805 	return ps_sendpsmmsg(ctx, fd, &psm, msg);
806 }
807 
808 ssize_t
809 ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
810     const void *data, size_t len)
811 {
812 	struct ps_msghdr psm = {
813 		.ps_cmd = cmd,
814 		.ps_flags = flags,
815 	};
816 	struct iovec iov[] = {
817 		{ .iov_base = UNCONST(data), .iov_len = len }
818 	};
819 	struct msghdr msg = {
820 		.msg_iov = iov, .msg_iovlen = 1,
821 	};
822 
823 	return ps_sendpsmmsg(ctx, fd, &psm, &msg);
824 }
825 
826 static ssize_t
827 ps_sendcmdmsg(int fd, uint16_t cmd, const struct msghdr *msg)
828 {
829 	struct ps_msghdr psm = { .ps_cmd = cmd };
830 	uint8_t data[PS_BUFLEN], *p = data;
831 	struct iovec iov[] = {
832 		{ .iov_base = &psm, .iov_len = sizeof(psm) },
833 		{ .iov_base = data, .iov_len = 0 },
834 	};
835 	size_t dl = sizeof(data);
836 
837 	if (msg->msg_namelen != 0) {
838 		if (msg->msg_namelen > dl)
839 			goto nobufs;
840 		psm.ps_namelen = msg->msg_namelen;
841 		memcpy(p, msg->msg_name, msg->msg_namelen);
842 		p += msg->msg_namelen;
843 		dl -= msg->msg_namelen;
844 	}
845 
846 	if (msg->msg_controllen != 0) {
847 		if (msg->msg_controllen > dl)
848 			goto nobufs;
849 		psm.ps_controllen = (socklen_t)msg->msg_controllen;
850 		memcpy(p, msg->msg_control, msg->msg_controllen);
851 		p += msg->msg_controllen;
852 		dl -= msg->msg_controllen;
853 	}
854 
855 	psm.ps_datalen = msg->msg_iov[0].iov_len;
856 	if (psm.ps_datalen > dl)
857 		goto nobufs;
858 
859 	iov[1].iov_len = psm.ps_namelen + psm.ps_controllen + psm.ps_datalen;
860 	if (psm.ps_datalen != 0)
861 		memcpy(p, msg->msg_iov[0].iov_base, psm.ps_datalen);
862 	return writev(fd, iov, __arraycount(iov));
863 
864 nobufs:
865 	errno = ENOBUFS;
866 	return -1;
867 }
868 
869 ssize_t
870 ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
871 {
872 	struct sockaddr_storage ss = { .ss_family = AF_UNSPEC };
873 	uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 };
874 	uint8_t databuf[64 * 1024];
875 	struct iovec iov[] = {
876 	    { .iov_base = databuf, .iov_len = sizeof(databuf) }
877 	};
878 	struct msghdr msg = {
879 		.msg_name = &ss, .msg_namelen = sizeof(ss),
880 		.msg_control = controlbuf, .msg_controllen = sizeof(controlbuf),
881 		.msg_iov = iov, .msg_iovlen = 1,
882 	};
883 
884 	ssize_t len = recvmsg(rfd, &msg, 0);
885 
886 	if (len == -1)
887 		logerr("%s: recvmsg", __func__);
888 	if (len == -1 || len == 0) {
889 		if (ctx->options & DHCPCD_FORKED &&
890 		    !(ctx->options & DHCPCD_PRIVSEPROOT))
891 			eloop_exit(ctx->eloop,
892 			    len == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
893 		return len;
894 	}
895 
896 	iov[0].iov_len = (size_t)len;
897 	len = ps_sendcmdmsg(wfd, cmd, &msg);
898 	if (len == -1) {
899 		logerr("ps_sendcmdmsg");
900 		if (ctx->options & DHCPCD_FORKED &&
901 		    !(ctx->options & DHCPCD_PRIVSEPROOT))
902 			eloop_exit(ctx->eloop, EXIT_FAILURE);
903 	}
904 	return len;
905 }
906 
907 ssize_t
908 ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd,
909     ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *),
910     void *cbctx)
911 {
912 	struct ps_msg psm;
913 	ssize_t len;
914 	size_t dlen;
915 	struct iovec iov[1];
916 	struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 };
917 	bool stop = false;
918 
919 	len = read(fd, &psm, sizeof(psm));
920 #ifdef PRIVSEP_DEBUG
921 	logdebugx("%s: %zd", __func__, len);
922 #endif
923 
924 	if (len == -1 || len == 0)
925 		stop = true;
926 	else {
927 		dlen = (size_t)len;
928 		if (dlen < sizeof(psm.psm_hdr)) {
929 			errno = EINVAL;
930 			return -1;
931 		}
932 
933 		if (psm.psm_hdr.ps_cmd == PS_STOP) {
934 			stop = true;
935 			len = 0;
936 		}
937 	}
938 
939 	if (stop) {
940 #ifdef PRIVSEP_DEBUG
941 		logdebugx("process %d stopping", getpid());
942 #endif
943 		ps_free(ctx);
944 #ifdef PLUGIN_DEV
945 		dev_stop(ctx);
946 #endif
947 		eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);
948 		return len;
949 	}
950 	dlen -= sizeof(psm.psm_hdr);
951 
952 	if (ps_unrollmsg(&msg, &psm.psm_hdr, psm.psm_data, dlen) == -1)
953 		return -1;
954 
955 	if (callback == NULL)
956 		return 0;
957 
958 	errno = 0;
959 	return callback(cbctx, &psm.psm_hdr, &msg);
960 }
961 
962 struct ps_process *
963 ps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
964 {
965 	struct ps_process *psp;
966 
967 	TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
968 		if (memcmp(&psp->psp_id, psid, sizeof(psp->psp_id)) == 0)
969 			return psp;
970 	}
971 	errno = ESRCH;
972 	return NULL;
973 }
974 
975 struct ps_process *
976 ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
977 {
978 	struct ps_process *psp;
979 
980 	psp = calloc(1, sizeof(*psp));
981 	if (psp == NULL)
982 		return NULL;
983 	psp->psp_ctx = ctx;
984 	memcpy(&psp->psp_id, psid, sizeof(psp->psp_id));
985 	psp->psp_work_fd = -1;
986 	TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next);
987 	return psp;
988 }
989 
990 void
991 ps_freeprocesses(struct dhcpcd_ctx *ctx, struct ps_process *notthis)
992 {
993 	struct ps_process *psp, *psn;
994 
995 	TAILQ_FOREACH_SAFE(psp, &ctx->ps_processes, next, psn) {
996 		if (psp == notthis)
997 			continue;
998 		ps_freeprocess(psp);
999 	}
1000 }
1001