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