xref: /dragonfly/contrib/dhcpcd/src/privsep.c (revision a31d3627)
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_FORKED))
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 	/* Prohibit writing to files.
167 	 * Obviously this won't work if we are using a logfile
168 	 * or redirecting stderr to a file. */
169 	if (ctx->logfile == NULL) {
170 		if (setrlimit(RLIMIT_FSIZE, &rzero) == -1)
171 			logerr("setrlimit RLIMIT_FSIZE");
172 	}
173 
174 #ifdef RLIMIT_NPROC
175 	/* Prohibit forks */
176 	if (setrlimit(RLIMIT_NPROC, &rzero) == -1)
177 		logerr("setrlimit RLIMIT_NPROC");
178 #endif
179 
180 	return 0;
181 }
182 
183 static int
184 ps_setbuf0(int fd, int ctl, int minlen)
185 {
186 	int len;
187 	socklen_t slen;
188 
189 	slen = sizeof(len);
190 	if (getsockopt(fd, SOL_SOCKET, ctl, &len, &slen) == -1)
191 		return -1;
192 
193 #ifdef __linux__
194 	len /= 2;
195 #endif
196 	if (len >= minlen)
197 		return 0;
198 
199 	return setsockopt(fd, SOL_SOCKET, ctl, &minlen, sizeof(minlen));
200 }
201 
202 static int
203 ps_setbuf(int fd)
204 {
205 	/* Ensure we can receive a fully sized privsep message.
206 	 * Double the send buffer. */
207 	int minlen = (int)sizeof(struct ps_msg);
208 
209 	if (ps_setbuf0(fd, SO_RCVBUF, minlen) == -1 ||
210 	    ps_setbuf0(fd, SO_SNDBUF, minlen * 2) == -1)
211 	{
212 		logerr(__func__);
213 		return -1;
214 	}
215 	return 0;
216 }
217 
218 int
219 ps_setbuf_fdpair(int fd[])
220 {
221 
222 	if (ps_setbuf(fd[0]) == -1 || ps_setbuf(fd[1]) == -1)
223 		return -1;
224 	return 0;
225 }
226 
227 #ifdef PRIVSEP_RIGHTS
228 int
229 ps_rights_limit_ioctl(int fd)
230 {
231 	cap_rights_t rights;
232 
233 	cap_rights_init(&rights, CAP_IOCTL);
234 	if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
235 		return -1;
236 	return 0;
237 }
238 
239 int
240 ps_rights_limit_fd_fctnl(int fd)
241 {
242 	cap_rights_t rights;
243 
244 	cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT,
245 	    CAP_ACCEPT, CAP_FCNTL);
246 	if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
247 		return -1;
248 	return 0;
249 }
250 
251 int
252 ps_rights_limit_fd(int fd)
253 {
254 	cap_rights_t rights;
255 
256 	cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN);
257 	if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
258 		return -1;
259 	return 0;
260 }
261 
262 int
263 ps_rights_limit_fd_sockopt(int fd)
264 {
265 	cap_rights_t rights;
266 
267 	cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT,
268 	    CAP_GETSOCKOPT, CAP_SETSOCKOPT);
269 	if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
270 		return -1;
271 	return 0;
272 }
273 
274 int
275 ps_rights_limit_fd_rdonly(int fd)
276 {
277 	cap_rights_t rights;
278 
279 	cap_rights_init(&rights, CAP_READ, CAP_EVENT);
280 	if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
281 		return -1;
282 	return 0;
283 }
284 
285 int
286 ps_rights_limit_fdpair(int fd[])
287 {
288 
289 	if (ps_rights_limit_fd(fd[0]) == -1 || ps_rights_limit_fd(fd[1]) == -1)
290 		return -1;
291 	return 0;
292 }
293 
294 static int
295 ps_rights_limit_stdio(struct dhcpcd_ctx *ctx)
296 {
297 	const int iebadf = CAPH_IGNORE_EBADF;
298 	int error = 0;
299 
300 	if (ctx->stdin_valid &&
301 	    caph_limit_stream(STDIN_FILENO, CAPH_READ | iebadf) == -1)
302 		error = -1;
303 	if (ctx->stdout_valid &&
304 	    caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | iebadf) == -1)
305 		error = -1;
306 	if (ctx->stderr_valid &&
307 	    caph_limit_stream(STDERR_FILENO, CAPH_WRITE | iebadf) == -1)
308 		error = -1;
309 
310 	return error;
311 }
312 #endif
313 
314 pid_t
315 ps_dostart(struct dhcpcd_ctx *ctx,
316     pid_t *priv_pid, int *priv_fd,
317     void (*recv_msg)(void *), void (*recv_unpriv_msg),
318     void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *),
319     unsigned int flags)
320 {
321 	int fd[2];
322 	pid_t pid;
323 
324 	if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1) {
325 		logerr("%s: socketpair", __func__);
326 		return -1;
327 	}
328 	if (ps_setbuf_fdpair(fd) == -1) {
329 		logerr("%s: ps_setbuf_fdpair", __func__);
330 		return -1;
331 	}
332 #ifdef PRIVSEP_RIGHTS
333 	if (ps_rights_limit_fdpair(fd) == -1) {
334 		logerr("%s: ps_rights_limit_fdpair", __func__);
335 		return -1;
336 	}
337 #endif
338 
339 	switch (pid = fork()) {
340 	case -1:
341 		logerr("fork");
342 		return -1;
343 	case 0:
344 		*priv_fd = fd[1];
345 		close(fd[0]);
346 		break;
347 	default:
348 		*priv_pid = pid;
349 		*priv_fd = fd[0];
350 		close(fd[1]);
351 		if (recv_unpriv_msg == NULL)
352 			;
353 		else if (eloop_event_add(ctx->eloop, *priv_fd,
354 		    recv_unpriv_msg, recv_ctx) == -1)
355 		{
356 			logerr("%s: eloop_event_add", __func__);
357 			return -1;
358 		}
359 		return pid;
360 	}
361 
362 	ctx->options |= DHCPCD_UNPRIV | DHCPCD_FORKED;
363 	if (ctx->fork_fd != -1) {
364 		close(ctx->fork_fd);
365 		ctx->fork_fd = -1;
366 	}
367 	pidfile_clean();
368 	eloop_clear(ctx->eloop);
369 
370 	/* We are not root */
371 	if (priv_fd != &ctx->ps_root_fd) {
372 		ps_freeprocesses(ctx, recv_ctx);
373 		if (ctx->ps_root_fd != -1) {
374 			close(ctx->ps_root_fd);
375 			ctx->ps_root_fd = -1;
376 		}
377 
378 #ifdef PRIVSEP_RIGHTS
379 		/* We cannot limit the root process in any way. */
380 		if (ps_rights_limit_stdio(ctx) == -1) {
381 			logerr("ps_rights_limit_stdio");
382 			goto errexit;
383 		}
384 #endif
385 	}
386 
387 	if (priv_fd != &ctx->ps_inet_fd && ctx->ps_inet_fd != -1) {
388 		close(ctx->ps_inet_fd);
389 		ctx->ps_inet_fd = -1;
390 	}
391 
392 	eloop_signal_set_cb(ctx->eloop,
393 	    dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx);
394 
395 	/* ctx->sigset aready has the initial sigmask set in main() */
396 	if (eloop_signal_mask(ctx->eloop, NULL) == -1) {
397 		logerr("%s: eloop_signal_mask", __func__);
398 		goto errexit;
399 	}
400 
401 	if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1)
402 	{
403 		logerr("%s: eloop_event_add", __func__);
404 		goto errexit;
405 	}
406 
407 	if (callback(recv_ctx) == -1)
408 		goto errexit;
409 
410 	if (flags & PSF_DROPPRIVS)
411 		ps_dropprivs(ctx);
412 
413 	return 0;
414 
415 errexit:
416 	/* Failure to start root or inet processes is fatal. */
417 	if (priv_fd == &ctx->ps_root_fd || priv_fd == &ctx->ps_inet_fd)
418 		(void)ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0);
419 	shutdown(*priv_fd, SHUT_RDWR);
420 	*priv_fd = -1;
421 	eloop_exit(ctx->eloop, EXIT_FAILURE);
422 	return -1;
423 }
424 
425 int
426 ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd)
427 {
428 	int err = 0;
429 
430 #ifdef PRIVSEP_DEBUG
431 	logdebugx("%s: pid=%d fd=%d", __func__, *pid, *fd);
432 #endif
433 
434 	if (*fd != -1) {
435 		eloop_event_delete(ctx->eloop, *fd);
436 		if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1) {
437 			logerr(__func__);
438 			err = -1;
439 		}
440 		(void)shutdown(*fd, SHUT_RDWR);
441 		close(*fd);
442 		*fd = -1;
443 	}
444 
445 	/* Don't wait for the process as it may not respond to the shutdown
446 	 * request. We'll reap the process on receipt of SIGCHLD. */
447 	*pid = 0;
448 	return err;
449 }
450 
451 int
452 ps_start(struct dhcpcd_ctx *ctx)
453 {
454 	pid_t pid;
455 
456 	TAILQ_INIT(&ctx->ps_processes);
457 
458 	switch (pid = ps_root_start(ctx)) {
459 	case -1:
460 		logerr("ps_root_start");
461 		return -1;
462 	case 0:
463 		return 0;
464 	default:
465 		logdebugx("spawned privileged actioneer on PID %d", pid);
466 	}
467 
468 	/* No point in spawning the generic network listener if we're
469 	 * not going to use it. */
470 	if (!(ctx->options & (DHCPCD_MASTER | DHCPCD_IPV6)))
471 		goto started_net;
472 
473 	switch (pid = ps_inet_start(ctx)) {
474 	case -1:
475 		if (errno == ENXIO)
476 			return 0;
477 		return -1;
478 	case 0:
479 		return 0;
480 	default:
481 		logdebugx("spawned network proxy on PID %d", pid);
482 	}
483 
484 started_net:
485 	if (!(ctx->options & DHCPCD_TEST)) {
486 		switch (pid = ps_ctl_start(ctx)) {
487 		case -1:
488 			return -1;
489 		case 0:
490 			return 0;
491 		default:
492 			logdebugx("spawned controller proxy on PID %d", pid);
493 		}
494 	}
495 
496 #ifdef ARC4RANDOM_H
497 	/* Seed the random number generator early incase it needs /dev/urandom
498 	 * which won't be available in the chroot. */
499 	arc4random();
500 #endif
501 
502 	return 1;
503 }
504 
505 int
506 ps_entersandbox(const char *_pledge, const char **sandbox)
507 {
508 
509 #if !defined(HAVE_PLEDGE)
510 	UNUSED(_pledge);
511 #endif
512 
513 #if defined(HAVE_CAPSICUM)
514 	if (sandbox != NULL)
515 		*sandbox = "capsicum";
516 	return cap_enter();
517 #elif defined(HAVE_PLEDGE)
518 	if (sandbox != NULL)
519 		*sandbox = "pledge";
520 	return pledge(_pledge, NULL);
521 #elif defined(HAVE_SECCOMP)
522 	if (sandbox != NULL)
523 		*sandbox = "seccomp";
524 	return ps_seccomp_enter();
525 #else
526 	if (sandbox != NULL)
527 		*sandbox = "posix resource limited";
528 	return 0;
529 #endif
530 }
531 
532 int
533 ps_mastersandbox(struct dhcpcd_ctx *ctx, const char *_pledge)
534 {
535 	const char *sandbox = NULL;
536 	bool forked;
537 	int dropped;
538 
539 	forked = ctx->options & DHCPCD_FORKED;
540 	ctx->options &= ~DHCPCD_FORKED;
541 	dropped = ps_dropprivs(ctx);
542 	if (forked)
543 		ctx->options |= DHCPCD_FORKED;
544 	if (dropped == -1) {
545 		logerr("%s: ps_dropprivs", __func__);
546 		return -1;
547 	}
548 
549 #ifdef PRIVSEP_RIGHTS
550 	if ((ctx->pf_inet_fd != -1 &&
551 	    ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1) ||
552 	     ps_rights_limit_stdio(ctx) == -1)
553 	{
554 		logerr("%s: cap_rights_limit", __func__);
555 		return -1;
556 	}
557 #endif
558 
559 	if (_pledge == NULL)
560 		_pledge = "stdio";
561 	if (ps_entersandbox(_pledge, &sandbox) == -1) {
562 		if (errno == ENOSYS) {
563 			if (sandbox != NULL)
564 				logwarnx("sandbox unavailable: %s", sandbox);
565 			return 0;
566 		}
567 		logerr("%s: %s", __func__, sandbox);
568 		return -1;
569 	} else if (!forked)
570 		logdebugx("sandbox: %s", sandbox);
571 	return 0;
572 }
573 
574 int
575 ps_stop(struct dhcpcd_ctx *ctx)
576 {
577 	int r, ret = 0;
578 
579 	if (!(ctx->options & DHCPCD_PRIVSEP) ||
580 	    ctx->options & DHCPCD_FORKED ||
581 	    ctx->eloop == NULL)
582 		return 0;
583 
584 	r = ps_ctl_stop(ctx);
585 	if (r != 0)
586 		ret = r;
587 
588 	r = ps_inet_stop(ctx);
589 	if (r != 0)
590 		ret = r;
591 
592 	/* We've been chrooted, so we need to tell the
593 	 * privileged actioneer to remove the pidfile. */
594 	ps_root_unlink(ctx, ctx->pidfile);
595 
596 	r = ps_root_stop(ctx);
597 	if (r != 0)
598 		ret = r;
599 
600 	ctx->options &= ~DHCPCD_PRIVSEP;
601 	return ret;
602 }
603 
604 void
605 ps_freeprocess(struct ps_process *psp)
606 {
607 
608 	TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next);
609 	if (psp->psp_fd != -1) {
610 		eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd);
611 		close(psp->psp_fd);
612 	}
613 	if (psp->psp_work_fd != -1) {
614 		eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd);
615 		close(psp->psp_work_fd);
616 	}
617 #ifdef INET
618 	if (psp->psp_bpf != NULL)
619 		bpf_close(psp->psp_bpf);
620 #endif
621 	free(psp);
622 }
623 
624 static void
625 ps_free(struct dhcpcd_ctx *ctx)
626 {
627 	struct ps_process *psp;
628 	bool stop = ctx->ps_root_pid == getpid();
629 
630 	while ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) {
631 		if (stop)
632 			ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
633 		ps_freeprocess(psp);
634 	}
635 }
636 
637 int
638 ps_unrollmsg(struct msghdr *msg, struct ps_msghdr *psm,
639     const void *data, size_t len)
640 {
641 	uint8_t *datap, *namep, *controlp;
642 
643 	namep = UNCONST(data);
644 	controlp = namep + psm->ps_namelen;
645 	datap = controlp + psm->ps_controllen;
646 
647 	if (psm->ps_namelen != 0) {
648 		if (psm->ps_namelen > len) {
649 			errno = EINVAL;
650 			return -1;
651 		}
652 		msg->msg_name = namep;
653 		len -= psm->ps_namelen;
654 	} else
655 		msg->msg_name = NULL;
656 	msg->msg_namelen = psm->ps_namelen;
657 
658 	if (psm->ps_controllen != 0) {
659 		if (psm->ps_controllen > len) {
660 			errno = EINVAL;
661 			return -1;
662 		}
663 		msg->msg_control = controlp;
664 		len -= psm->ps_controllen;
665 	} else
666 		msg->msg_control = NULL;
667 	msg->msg_controllen = psm->ps_controllen;
668 
669 	if (len != 0) {
670 		msg->msg_iovlen = 1;
671 		msg->msg_iov[0].iov_base = datap;
672 		msg->msg_iov[0].iov_len = len;
673 	} else {
674 		msg->msg_iovlen = 0;
675 		msg->msg_iov[0].iov_base = NULL;
676 		msg->msg_iov[0].iov_len = 0;
677 	}
678 	return 0;
679 }
680 
681 ssize_t
682 ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd,
683     struct ps_msghdr *psm, const struct msghdr *msg)
684 {
685 	struct iovec iov[] = {
686 		{ .iov_base = UNCONST(psm), .iov_len = sizeof(*psm) },
687 		{ .iov_base = NULL, },	/* name */
688 		{ .iov_base = NULL, },	/* control */
689 		{ .iov_base = NULL, },	/* payload 1 */
690 		{ .iov_base = NULL, },	/* payload 2 */
691 		{ .iov_base = NULL, },	/* payload 3 */
692 	};
693 	int iovlen;
694 	ssize_t len;
695 
696 	if (msg != NULL) {
697 		struct iovec *iovp = &iov[1];
698 		int i;
699 
700 		psm->ps_namelen = msg->msg_namelen;
701 		psm->ps_controllen = (socklen_t)msg->msg_controllen;
702 
703 		iovp->iov_base = msg->msg_name;
704 		iovp->iov_len = msg->msg_namelen;
705 		iovp++;
706 		iovp->iov_base = msg->msg_control;
707 		iovp->iov_len = msg->msg_controllen;
708 		iovlen = 3;
709 
710 		for (i = 0; i < (int)msg->msg_iovlen; i++) {
711 			if ((size_t)(iovlen + i) > __arraycount(iov)) {
712 				errno =	ENOBUFS;
713 				return -1;
714 			}
715 			iovp++;
716 			iovp->iov_base = msg->msg_iov[i].iov_base;
717 			iovp->iov_len = msg->msg_iov[i].iov_len;
718 		}
719 		iovlen += i;
720 	} else
721 		iovlen = 1;
722 
723 	len = writev(fd, iov, iovlen);
724 	if (len == -1) {
725 		logerr(__func__);
726 		if (ctx->options & DHCPCD_FORKED &&
727 		    !(ctx->options & DHCPCD_PRIVSEPROOT))
728 			eloop_exit(ctx->eloop, EXIT_FAILURE);
729 	}
730 	return len;
731 }
732 
733 ssize_t
734 ps_sendpsmdata(struct dhcpcd_ctx *ctx, int fd,
735     struct ps_msghdr *psm, const void *data, size_t len)
736 {
737 	struct iovec iov[] = {
738 		{ .iov_base = UNCONST(data), .iov_len = len },
739 	};
740 	struct msghdr msg = {
741 		.msg_iov = iov, .msg_iovlen = 1,
742 	};
743 
744 	return ps_sendpsmmsg(ctx, fd, psm, &msg);
745 }
746 
747 
748 ssize_t
749 ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
750     const struct msghdr *msg)
751 {
752 	struct ps_msghdr psm = {
753 		.ps_cmd = cmd,
754 		.ps_flags = flags,
755 		.ps_namelen = msg->msg_namelen,
756 		.ps_controllen = (socklen_t)msg->msg_controllen,
757 	};
758 	size_t i;
759 
760 	for (i = 0; i < (size_t)msg->msg_iovlen; i++)
761 		psm.ps_datalen += msg->msg_iov[i].iov_len;
762 
763 #if 0	/* For debugging structure padding. */
764 	logerrx("psa.family %lu %zu", offsetof(struct ps_addr, psa_family), sizeof(psm.ps_id.psi_addr.psa_family));
765 	logerrx("psa.pad %lu %zu", offsetof(struct ps_addr, psa_pad), sizeof(psm.ps_id.psi_addr.psa_pad));
766 	logerrx("psa.psa_u %lu %zu", offsetof(struct ps_addr, psa_u), sizeof(psm.ps_id.psi_addr.psa_u));
767 	logerrx("psa %zu", sizeof(psm.ps_id.psi_addr));
768 
769 	logerrx("psi.addr %lu %zu", offsetof(struct ps_id, psi_addr), sizeof(psm.ps_id.psi_addr));
770 	logerrx("psi.index %lu %zu", offsetof(struct ps_id, psi_ifindex), sizeof(psm.ps_id.psi_ifindex));
771 	logerrx("psi.cmd %lu %zu", offsetof(struct ps_id, psi_cmd), sizeof(psm.ps_id.psi_cmd));
772 	logerrx("psi.pad %lu %zu", offsetof(struct ps_id, psi_pad), sizeof(psm.ps_id.psi_pad));
773 	logerrx("psi %zu", sizeof(struct ps_id));
774 
775 	logerrx("ps_cmd %lu", offsetof(struct ps_msghdr, ps_cmd));
776 	logerrx("ps_pad %lu %zu", offsetof(struct ps_msghdr, ps_pad), sizeof(psm.ps_pad));
777 	logerrx("ps_flags %lu %zu", offsetof(struct ps_msghdr, ps_flags), sizeof(psm.ps_flags));
778 
779 	logerrx("ps_id %lu %zu", offsetof(struct ps_msghdr, ps_id), sizeof(psm.ps_id));
780 
781 	logerrx("ps_namelen %lu %zu", offsetof(struct ps_msghdr, ps_namelen), sizeof(psm.ps_namelen));
782 	logerrx("ps_controllen %lu %zu", offsetof(struct ps_msghdr, ps_controllen), sizeof(psm.ps_controllen));
783 	logerrx("ps_pad2 %lu %zu", offsetof(struct ps_msghdr, ps_pad2), sizeof(psm.ps_pad2));
784 	logerrx("ps_datalen %lu %zu", offsetof(struct ps_msghdr, ps_datalen), sizeof(psm.ps_datalen));
785 	logerrx("psm %zu", sizeof(psm));
786 #endif
787 
788 	return ps_sendpsmmsg(ctx, fd, &psm, msg);
789 }
790 
791 ssize_t
792 ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
793     const void *data, size_t len)
794 {
795 	struct ps_msghdr psm = {
796 		.ps_cmd = cmd,
797 		.ps_flags = flags,
798 	};
799 	struct iovec iov[] = {
800 		{ .iov_base = UNCONST(data), .iov_len = len }
801 	};
802 	struct msghdr msg = {
803 		.msg_iov = iov, .msg_iovlen = 1,
804 	};
805 
806 	return ps_sendpsmmsg(ctx, fd, &psm, &msg);
807 }
808 
809 static ssize_t
810 ps_sendcmdmsg(int fd, uint16_t cmd, const struct msghdr *msg)
811 {
812 	struct ps_msghdr psm = { .ps_cmd = cmd };
813 	uint8_t data[PS_BUFLEN], *p = data;
814 	struct iovec iov[] = {
815 		{ .iov_base = &psm, .iov_len = sizeof(psm) },
816 		{ .iov_base = data, .iov_len = 0 },
817 	};
818 	size_t dl = sizeof(data);
819 
820 	if (msg->msg_namelen != 0) {
821 		if (msg->msg_namelen > dl)
822 			goto nobufs;
823 		psm.ps_namelen = msg->msg_namelen;
824 		memcpy(p, msg->msg_name, msg->msg_namelen);
825 		p += msg->msg_namelen;
826 		dl -= msg->msg_namelen;
827 	}
828 
829 	if (msg->msg_controllen != 0) {
830 		if (msg->msg_controllen > dl)
831 			goto nobufs;
832 		psm.ps_controllen = (socklen_t)msg->msg_controllen;
833 		memcpy(p, msg->msg_control, msg->msg_controllen);
834 		p += msg->msg_controllen;
835 		dl -= msg->msg_controllen;
836 	}
837 
838 	psm.ps_datalen = msg->msg_iov[0].iov_len;
839 	if (psm.ps_datalen > dl)
840 		goto nobufs;
841 
842 	iov[1].iov_len = psm.ps_namelen + psm.ps_controllen + psm.ps_datalen;
843 	if (psm.ps_datalen != 0)
844 		memcpy(p, msg->msg_iov[0].iov_base, psm.ps_datalen);
845 	return writev(fd, iov, __arraycount(iov));
846 
847 nobufs:
848 	errno = ENOBUFS;
849 	return -1;
850 }
851 
852 ssize_t
853 ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
854 {
855 	struct sockaddr_storage ss = { .ss_family = AF_UNSPEC };
856 	uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 };
857 	uint8_t databuf[64 * 1024];
858 	struct iovec iov[] = {
859 	    { .iov_base = databuf, .iov_len = sizeof(databuf) }
860 	};
861 	struct msghdr msg = {
862 		.msg_name = &ss, .msg_namelen = sizeof(ss),
863 		.msg_control = controlbuf, .msg_controllen = sizeof(controlbuf),
864 		.msg_iov = iov, .msg_iovlen = 1,
865 	};
866 
867 	ssize_t len = recvmsg(rfd, &msg, 0);
868 
869 	if (len == -1)
870 		logerr("%s: recvmsg", __func__);
871 	if (len == -1 || len == 0) {
872 		if (ctx->options & DHCPCD_FORKED &&
873 		    !(ctx->options & DHCPCD_PRIVSEPROOT))
874 			eloop_exit(ctx->eloop,
875 			    len == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
876 		return len;
877 	}
878 
879 	iov[0].iov_len = (size_t)len;
880 	len = ps_sendcmdmsg(wfd, cmd, &msg);
881 	if (len == -1) {
882 		logerr("ps_sendcmdmsg");
883 		if (ctx->options & DHCPCD_FORKED &&
884 		    !(ctx->options & DHCPCD_PRIVSEPROOT))
885 			eloop_exit(ctx->eloop, EXIT_FAILURE);
886 	}
887 	return len;
888 }
889 
890 ssize_t
891 ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd,
892     ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *),
893     void *cbctx)
894 {
895 	struct ps_msg psm;
896 	ssize_t len;
897 	size_t dlen;
898 	struct iovec iov[1];
899 	struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 };
900 	bool stop = false;
901 
902 	len = read(fd, &psm, sizeof(psm));
903 #ifdef PRIVSEP_DEBUG
904 	logdebugx("%s: %zd", __func__, len);
905 #endif
906 
907 	if (len == -1 || len == 0)
908 		stop = true;
909 	else {
910 		dlen = (size_t)len;
911 		if (dlen < sizeof(psm.psm_hdr)) {
912 			errno = EINVAL;
913 			return -1;
914 		}
915 
916 		if (psm.psm_hdr.ps_cmd == PS_STOP) {
917 			stop = true;
918 			len = 0;
919 		}
920 	}
921 
922 	if (stop) {
923 #ifdef PRIVSEP_DEBUG
924 		logdebugx("process %d stopping", getpid());
925 #endif
926 		ps_free(ctx);
927 #ifdef PLUGIN_DEV
928 		dev_stop(ctx);
929 #endif
930 		eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);
931 		return len;
932 	}
933 	dlen -= sizeof(psm.psm_hdr);
934 
935 	if (ps_unrollmsg(&msg, &psm.psm_hdr, psm.psm_data, dlen) == -1)
936 		return -1;
937 
938 	if (callback == NULL)
939 		return 0;
940 
941 	errno = 0;
942 	return callback(cbctx, &psm.psm_hdr, &msg);
943 }
944 
945 struct ps_process *
946 ps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
947 {
948 	struct ps_process *psp;
949 
950 	TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
951 		if (memcmp(&psp->psp_id, psid, sizeof(psp->psp_id)) == 0)
952 			return psp;
953 	}
954 	errno = ESRCH;
955 	return NULL;
956 }
957 
958 struct ps_process *
959 ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
960 {
961 	struct ps_process *psp;
962 
963 	psp = calloc(1, sizeof(*psp));
964 	if (psp == NULL)
965 		return NULL;
966 	psp->psp_ctx = ctx;
967 	memcpy(&psp->psp_id, psid, sizeof(psp->psp_id));
968 	psp->psp_work_fd = -1;
969 	TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next);
970 	return psp;
971 }
972 
973 void
974 ps_freeprocesses(struct dhcpcd_ctx *ctx, struct ps_process *notthis)
975 {
976 	struct ps_process *psp, *psn;
977 
978 	TAILQ_FOREACH_SAFE(psp, &ctx->ps_processes, next, psn) {
979 		if (psp == notthis)
980 			continue;
981 		ps_freeprocess(psp);
982 	}
983 }
984