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