Update to dhcpcd-9.2.0 with the following changes:
[dragonfly.git] / contrib / dhcpcd / src / privsep-root.c
blob1a438b67d354c5b5d045c9999460bc0b33d88d64
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
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.
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.
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>
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>
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"
59 __CTASSERT(sizeof(ioctl_request_t) <= sizeof(unsigned long));
61 struct psr_error
63 ssize_t psr_result;
64 int psr_errno;
65 char psr_pad[sizeof(ssize_t) - sizeof(int)];
66 size_t psr_datalen;
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;
76 static void
77 ps_root_readerrorcb(void *arg)
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 },
87 ssize_t len;
88 int exit_code = EXIT_FAILURE;
90 #define PSR_ERROR(e) \
91 do { \
92 psr_error->psr_result = -1; \
93 psr_error->psr_errno = (e); \
94 goto out; \
95 } while (0 /* CONSTCOND */)
97 len = readv(ctx->ps_root_fd, iov, __arraycount(iov));
98 if (len == -1)
99 PSR_ERROR(errno);
100 else if ((size_t)len < sizeof(*psr_error))
101 PSR_ERROR(EINVAL);
102 exit_code = EXIT_SUCCESS;
104 out:
105 eloop_exit(ctx->ps_eloop, exit_code);
108 ssize_t
109 ps_root_readerror(struct dhcpcd_ctx *ctx, void *data, size_t len)
111 struct psr_ctx psr_ctx = {
112 .psr_ctx = ctx,
113 .psr_data = data, .psr_datalen = len,
116 if (eloop_event_add(ctx->ps_eloop, ctx->ps_root_fd,
117 ps_root_readerrorcb, &psr_ctx) == -1)
118 return -1;
120 eloop_enter(ctx->ps_eloop);
121 eloop_start(ctx->ps_eloop, &ctx->sigset);
123 errno = psr_ctx.psr_error.psr_errno;
124 return psr_ctx.psr_error.psr_result;
127 #ifdef PRIVSEP_GETIFADDRS
128 static void
129 ps_root_mreaderrorcb(void *arg)
131 struct psr_ctx *psr_ctx = arg;
132 struct dhcpcd_ctx *ctx = psr_ctx->psr_ctx;
133 struct psr_error *psr_error = &psr_ctx->psr_error;
134 struct iovec iov[] = {
135 { .iov_base = psr_error, .iov_len = sizeof(*psr_error) },
136 { .iov_base = NULL, .iov_len = 0 },
138 ssize_t len;
139 int exit_code = EXIT_FAILURE;
141 len = recv(ctx->ps_root_fd, psr_error, sizeof(*psr_error), MSG_PEEK);
142 if (len == -1)
143 PSR_ERROR(errno);
144 else if ((size_t)len < sizeof(*psr_error))
145 PSR_ERROR(EINVAL);
147 if (psr_error->psr_datalen > SSIZE_MAX)
148 PSR_ERROR(ENOBUFS);
149 else if (psr_error->psr_datalen != 0) {
150 psr_ctx->psr_data = malloc(psr_error->psr_datalen);
151 if (psr_ctx->psr_data == NULL)
152 PSR_ERROR(errno);
153 psr_ctx->psr_datalen = psr_error->psr_datalen;
154 iov[1].iov_base = psr_ctx->psr_data;
155 iov[1].iov_len = psr_ctx->psr_datalen;
158 len = readv(ctx->ps_root_fd, iov, __arraycount(iov));
159 if (len == -1)
160 PSR_ERROR(errno);
161 else if ((size_t)len != sizeof(*psr_error) + psr_ctx->psr_datalen)
162 PSR_ERROR(EINVAL);
163 exit_code = EXIT_SUCCESS;
165 out:
166 eloop_exit(ctx->ps_eloop, exit_code);
169 ssize_t
170 ps_root_mreaderror(struct dhcpcd_ctx *ctx, void **data, size_t *len)
172 struct psr_ctx psr_ctx = {
173 .psr_ctx = ctx,
176 if (eloop_event_add(ctx->ps_eloop, ctx->ps_root_fd,
177 ps_root_mreaderrorcb, &psr_ctx) == -1)
178 return -1;
180 eloop_enter(ctx->ps_eloop);
181 eloop_start(ctx->ps_eloop, &ctx->sigset);
183 errno = psr_ctx.psr_error.psr_errno;
184 *data = psr_ctx.psr_data;
185 *len = psr_ctx.psr_datalen;
186 return psr_ctx.psr_error.psr_result;
188 #endif
190 static ssize_t
191 ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result,
192 void *data, size_t len)
194 struct psr_error psr = {
195 .psr_result = result,
196 .psr_errno = errno,
197 .psr_datalen = len,
199 struct iovec iov[] = {
200 { .iov_base = &psr, .iov_len = sizeof(psr) },
201 { .iov_base = data, .iov_len = len },
204 #ifdef PRIVSEP_DEBUG
205 logdebugx("%s: result %zd errno %d", __func__, result, errno);
206 #endif
208 return writev(ctx->ps_root_fd, iov, __arraycount(iov));
211 static ssize_t
212 ps_root_doioctl(unsigned long req, void *data, size_t len)
214 int s, err;
216 /* Only allow these ioctls */
217 switch(req) {
218 #ifdef SIOCAIFADDR
219 case SIOCAIFADDR: /* FALLTHROUGH */
220 case SIOCDIFADDR: /* FALLTHROUGH */
221 #endif
222 #ifdef SIOCSIFHWADDR
223 case SIOCSIFHWADDR: /* FALLTHROUGH */
224 #endif
225 #ifdef SIOCGIFPRIORITY
226 case SIOCGIFPRIORITY: /* FALLTHROUGH */
227 #endif
228 case SIOCSIFFLAGS: /* FALLTHROUGH */
229 case SIOCGIFMTU: /* FALLTHROUGH */
230 case SIOCSIFMTU:
231 break;
232 default:
233 errno = EPERM;
234 return -1;
237 s = socket(PF_INET, SOCK_DGRAM, 0);
238 if (s != -1)
239 #ifdef IOCTL_REQUEST_TYPE
241 ioctl_request_t reqt;
243 memcpy(&reqt, &req, sizeof(reqt));
244 err = ioctl(s, reqt, data, len);
246 #else
247 err = ioctl(s, req, data, len);
248 #endif
249 else
250 err = -1;
251 if (s != -1)
252 close(s);
253 return err;
256 static ssize_t
257 ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
259 const char *envbuf = data;
260 char * const argv[] = { ctx->script, NULL };
261 pid_t pid;
262 int status;
264 if (len == 0)
265 return 0;
267 if (script_buftoenv(ctx, UNCONST(envbuf), len) == NULL)
268 return -1;
270 pid = script_exec(argv, ctx->script_env);
271 if (pid == -1)
272 return -1;
273 /* Wait for the script to finish */
274 while (waitpid(pid, &status, 0) == -1) {
275 if (errno != EINTR) {
276 logerr(__func__);
277 status = 0;
278 break;
281 return status;
284 static bool
285 ps_root_validpath(const struct dhcpcd_ctx *ctx, uint16_t cmd, const char *path)
288 /* Avoid a previous directory attack to avoid /proc/../
289 * dhcpcd should never use a path with double dots. */
290 if (strstr(path, "..") != NULL)
291 return false;
293 if (cmd == PS_READFILE) {
294 #ifdef EMBEDDED_CONFIG
295 if (strcmp(ctx->cffile, EMBEDDED_CONFIG) == 0)
296 return true;
297 #endif
298 if (strcmp(ctx->cffile, path) == 0)
299 return true;
301 if (strncmp(DBDIR, path, strlen(DBDIR)) == 0)
302 return true;
303 if (strncmp(RUNDIR, path, strlen(RUNDIR)) == 0)
304 return true;
306 #ifdef __linux__
307 if (strncmp("/proc/net/", path, strlen("/proc/net/")) == 0 ||
308 strncmp("/proc/sys/net/", path, strlen("/proc/sys/net/")) == 0 ||
309 strncmp("/sys/class/net/", path, strlen("/sys/class/net/")) == 0)
310 return true;
311 #endif
313 errno = EPERM;
314 return false;
317 static ssize_t
318 ps_root_dowritefile(const struct dhcpcd_ctx *ctx,
319 mode_t mode, void *data, size_t len)
321 char *file = data, *nc;
323 nc = memchr(file, '\0', len);
324 if (nc == NULL) {
325 errno = EINVAL;
326 return -1;
329 if (!ps_root_validpath(ctx, PS_WRITEFILE, file))
330 return -1;
331 nc++;
332 return writefile(file, mode, nc, len - (size_t)(nc - file));
335 #ifdef AUTH
336 static ssize_t
337 ps_root_monordm(uint64_t *rdm, size_t len)
340 if (len != sizeof(*rdm)) {
341 errno = EINVAL;
342 return -1;
344 return auth_get_rdm_monotonic(rdm);
346 #endif
348 #ifdef PRIVSEP_GETIFADDRS
349 #define IFA_NADDRS 3
350 static ssize_t
351 ps_root_dogetifaddrs(void **rdata, size_t *rlen)
353 struct ifaddrs *ifaddrs, *ifa, *ifa_next;
354 size_t len;
355 uint8_t *buf, *sap;
356 socklen_t salen;
357 void *ifa_data;
359 if (getifaddrs(&ifaddrs) == -1)
360 return -1;
361 if (ifaddrs == NULL) {
362 *rdata = NULL;
363 *rlen = 0;
364 return 0;
367 /* Work out the buffer length required.
368 * Ensure everything is aligned correctly, which does
369 * create a larger buffer than what is needed to send,
370 * but makes creating the same structure in the client
371 * much easier. */
372 len = 0;
373 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
374 len += ALIGN(sizeof(*ifa));
375 len += ALIGN(IFNAMSIZ);
376 len += ALIGN(sizeof(salen) * IFA_NADDRS);
377 if (ifa->ifa_addr != NULL)
378 len += ALIGN(sa_len(ifa->ifa_addr));
379 if (ifa->ifa_netmask != NULL)
380 len += ALIGN(sa_len(ifa->ifa_netmask));
381 if (ifa->ifa_broadaddr != NULL)
382 len += ALIGN(sa_len(ifa->ifa_broadaddr));
385 /* Use calloc to set everything to zero.
386 * This satisfies memory sanitizers because don't write
387 * where we don't need to. */
388 buf = calloc(1, len);
389 if (buf == NULL) {
390 freeifaddrs(ifaddrs);
391 return -1;
393 *rdata = buf;
394 *rlen = len;
396 for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
397 /* Don't carry ifa_data or ifa_next. */
398 ifa_data = ifa->ifa_data;
399 ifa_next = ifa->ifa_next;
400 ifa->ifa_data = NULL;
401 ifa->ifa_next = NULL;
402 memcpy(buf, ifa, sizeof(*ifa));
403 buf += ALIGN(sizeof(*ifa));
404 ifa->ifa_data = ifa_data;
405 ifa->ifa_next = ifa_next;
407 strlcpy((char *)buf, ifa->ifa_name, IFNAMSIZ);
408 buf += ALIGN(IFNAMSIZ);
409 sap = buf;
410 buf += ALIGN(sizeof(salen) * IFA_NADDRS);
412 #define COPYINSA(addr) \
413 do { \
414 salen = sa_len((addr)); \
415 if (salen != 0) { \
416 memcpy(sap, &salen, sizeof(salen)); \
417 memcpy(buf, (addr), salen); \
418 buf += ALIGN(salen); \
420 sap += sizeof(salen); \
421 } while (0 /*CONSTCOND */)
423 if (ifa->ifa_addr != NULL)
424 COPYINSA(ifa->ifa_addr);
425 if (ifa->ifa_netmask != NULL)
426 COPYINSA(ifa->ifa_netmask);
427 if (ifa->ifa_broadaddr != NULL)
428 COPYINSA(ifa->ifa_broadaddr);
431 freeifaddrs(ifaddrs);
432 return 0;
434 #endif
436 static ssize_t
437 ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
439 struct dhcpcd_ctx *ctx = arg;
440 uint16_t cmd;
441 struct ps_process *psp;
442 struct iovec *iov = msg->msg_iov;
443 void *data = iov->iov_base, *rdata = NULL;
444 size_t len = iov->iov_len, rlen = 0;
445 uint8_t buf[PS_BUFLEN];
446 time_t mtime;
447 ssize_t err;
448 bool free_rdata = false;
450 cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
451 psp = ps_findprocess(ctx, &psm->ps_id);
453 #ifdef PRIVSEP_DEBUG
454 logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
455 #endif
457 if (psp != NULL) {
458 if (psm->ps_cmd & PS_STOP) {
459 int ret = ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
461 ps_freeprocess(psp);
462 return ret;
463 } else if (psm->ps_cmd & PS_START) {
464 /* Process has already started .... */
465 return 0;
468 err = ps_sendpsmmsg(ctx, psp->psp_fd, psm, msg);
469 if (err == -1) {
470 logerr("%s: failed to send message to pid %d",
471 __func__, psp->psp_pid);
472 shutdown(psp->psp_fd, SHUT_RDWR);
473 close(psp->psp_fd);
474 psp->psp_fd = -1;
475 ps_freeprocess(psp);
477 return 0;
480 if (psm->ps_cmd & PS_STOP && psp == NULL)
481 return 0;
483 switch (cmd) {
484 #ifdef INET
485 #ifdef ARP
486 case PS_BPF_ARP: /* FALLTHROUGH */
487 #endif
488 case PS_BPF_BOOTP:
489 return ps_bpf_cmd(ctx, psm, msg);
490 #endif
491 #ifdef INET
492 case PS_BOOTP:
493 return ps_inet_cmd(ctx, psm, msg);
494 #endif
495 #ifdef INET6
496 #ifdef DHCP6
497 case PS_DHCP6: /* FALLTHROUGH */
498 #endif
499 case PS_ND:
500 return ps_inet_cmd(ctx, psm, msg);
501 #endif
502 default:
503 break;
506 assert(msg->msg_iovlen == 0 || msg->msg_iovlen == 1);
508 /* Reset errno */
509 errno = 0;
511 switch (psm->ps_cmd) {
512 case PS_IOCTL:
513 err = ps_root_doioctl(psm->ps_flags, data, len);
514 if (err != -1) {
515 rdata = data;
516 rlen = len;
518 break;
519 case PS_SCRIPT:
520 err = ps_root_run_script(ctx, data, len);
521 break;
522 case PS_UNLINK:
523 if (!ps_root_validpath(ctx, psm->ps_cmd, data)) {
524 err = -1;
525 break;
527 err = unlink(data);
528 break;
529 case PS_READFILE:
530 if (!ps_root_validpath(ctx, psm->ps_cmd, data)) {
531 err = -1;
532 break;
534 err = readfile(data, buf, sizeof(buf));
535 if (err != -1) {
536 rdata = buf;
537 rlen = (size_t)err;
539 break;
540 case PS_WRITEFILE:
541 err = ps_root_dowritefile(ctx, (mode_t)psm->ps_flags,
542 data, len);
543 break;
544 case PS_FILEMTIME:
545 err = filemtime(data, &mtime);
546 if (err != -1) {
547 rdata = &mtime;
548 rlen = sizeof(mtime);
550 break;
551 #ifdef AUTH
552 case PS_AUTH_MONORDM:
553 err = ps_root_monordm(data, len);
554 if (err != -1) {
555 rdata = data;
556 rlen = len;
558 break;
559 #endif
560 #ifdef PRIVSEP_GETIFADDRS
561 case PS_GETIFADDRS:
562 err = ps_root_dogetifaddrs(&rdata, &rlen);
563 free_rdata = true;
564 break;
565 #endif
566 #if defined(INET6) && (defined(__linux__) || defined(HAVE_PLEDGE))
567 case PS_IP6FORWARDING:
568 err = ip6_forwarding(data);
569 break;
570 #endif
571 #ifdef PLUGIN_DEV
572 case PS_DEV_INITTED:
573 err = dev_initialized(ctx, data);
574 break;
575 case PS_DEV_LISTENING:
576 err = dev_listening(ctx);
577 break;
578 #endif
579 default:
580 err = ps_root_os(psm, msg, &rdata, &rlen);
581 break;
584 err = ps_root_writeerror(ctx, err, rlen != 0 ? rdata : 0, rlen);
585 if (free_rdata)
586 free(rdata);
587 return err;
590 /* Receive from state engine, do an action. */
591 static void
592 ps_root_recvmsg(void *arg)
594 struct dhcpcd_ctx *ctx = arg;
596 if (ps_recvpsmsg(ctx, ctx->ps_root_fd, ps_root_recvmsgcb, ctx) == -1)
597 logerr(__func__);
600 #ifdef PLUGIN_DEV
601 static int
602 ps_root_handleinterface(void *arg, int action, const char *ifname)
604 struct dhcpcd_ctx *ctx = arg;
605 unsigned long flag;
607 if (action == 1)
608 flag = PS_DEV_IFADDED;
609 else if (action == -1)
610 flag = PS_DEV_IFREMOVED;
611 else if (action == 0)
612 flag = PS_DEV_IFUPDATED;
613 else {
614 errno = EINVAL;
615 return -1;
618 return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD, flag,
619 ifname, strlen(ifname) + 1);
621 #endif
623 static int
624 ps_root_startcb(void *arg)
626 struct dhcpcd_ctx *ctx = arg;
628 if (ctx->options & DHCPCD_MASTER)
629 setproctitle("[privileged actioneer]");
630 else
631 setproctitle("[privileged actioneer] %s%s%s",
632 ctx->ifv[0],
633 ctx->options & DHCPCD_IPV4 ? " [ip4]" : "",
634 ctx->options & DHCPCD_IPV6 ? " [ip6]" : "");
635 ctx->ps_root_pid = getpid();
636 ctx->options |= DHCPCD_PRIVSEPROOT;
638 /* Open network sockets for sending.
639 * This is a small bit wasteful for non sandboxed OS's
640 * but makes life very easy for unicasting DHCPv6 in non master
641 * mode as we no longer care about address selection.
642 * We can't call shutdown SHUT_RD on the socket because it's
643 * not connectd. All we can do is try and set a zero sized
644 * receive buffer and just let it overflow.
645 * Reading from it just to drain it is a waste of CPU time. */
646 #ifdef INET
647 if (ctx->options & DHCPCD_IPV4) {
648 int buflen = 1;
650 ctx->udp_wfd = xsocket(PF_INET,
651 SOCK_RAW | SOCK_CXNB, IPPROTO_UDP);
652 if (ctx->udp_wfd == -1)
653 logerr("%s: dhcp_openraw", __func__);
654 else if (setsockopt(ctx->udp_wfd, SOL_SOCKET, SO_RCVBUF,
655 &buflen, sizeof(buflen)) == -1)
656 logerr("%s: setsockopt SO_RCVBUF DHCP", __func__);
658 #endif
659 #ifdef INET6
660 if (ctx->options & DHCPCD_IPV6) {
661 int buflen = 1;
663 ctx->nd_fd = ipv6nd_open(false);
664 if (ctx->nd_fd == -1)
665 logerr("%s: ipv6nd_open", __func__);
666 else if (setsockopt(ctx->nd_fd, SOL_SOCKET, SO_RCVBUF,
667 &buflen, sizeof(buflen)) == -1)
668 logerr("%s: setsockopt SO_RCVBUF ND", __func__);
670 #endif
671 #ifdef DHCP6
672 if (ctx->options & DHCPCD_IPV6) {
673 int buflen = 1;
675 ctx->dhcp6_wfd = dhcp6_openraw();
676 if (ctx->dhcp6_wfd == -1)
677 logerr("%s: dhcp6_openraw", __func__);
678 else if (setsockopt(ctx->dhcp6_wfd, SOL_SOCKET, SO_RCVBUF,
679 &buflen, sizeof(buflen)) == -1)
680 logerr("%s: setsockopt SO_RCVBUF DHCP6", __func__);
682 #endif
684 #ifdef PLUGIN_DEV
685 /* Start any dev listening plugin which may want to
686 * change the interface name provided by the kernel */
687 if ((ctx->options & (DHCPCD_MASTER | DHCPCD_DEV)) ==
688 (DHCPCD_MASTER | DHCPCD_DEV))
689 dev_start(ctx, ps_root_handleinterface);
690 #endif
692 return 0;
695 static void
696 ps_root_signalcb(int sig, __unused void *arg)
699 if (sig == SIGCHLD) {
700 while (waitpid(-1, NULL, WNOHANG) > 0)
702 return;
706 int (*handle_interface)(void *, int, const char *);
708 #ifdef PLUGIN_DEV
709 static ssize_t
710 ps_root_devcb(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
712 int action;
713 struct iovec *iov = msg->msg_iov;
715 if (msg->msg_iovlen != 1) {
716 errno = EINVAL;
717 return -1;
720 switch(psm->ps_flags) {
721 case PS_DEV_IFADDED:
722 action = 1;
723 break;
724 case PS_DEV_IFREMOVED:
725 action = -1;
726 break;
727 case PS_DEV_IFUPDATED:
728 action = 0;
729 break;
730 default:
731 errno = EINVAL;
732 return -1;
735 return dhcpcd_handleinterface(ctx, action, iov->iov_base);
737 #endif
739 static ssize_t
740 ps_root_dispatchcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
742 struct dhcpcd_ctx *ctx = arg;
743 ssize_t err;
745 switch(psm->ps_cmd) {
746 #ifdef PLUGIN_DEV
747 case PS_DEV_IFCMD:
748 err = ps_root_devcb(ctx, psm, msg);
749 break;
750 #endif
751 default:
752 #ifdef INET
753 err = ps_bpf_dispatch(ctx, psm, msg);
754 if (err == -1 && errno == ENOTSUP)
755 #endif
756 err = ps_inet_dispatch(ctx, psm, msg);
758 return err;
761 static void
762 ps_root_dispatch(void *arg)
764 struct dhcpcd_ctx *ctx = arg;
766 if (ps_recvpsmsg(ctx, ctx->ps_data_fd, ps_root_dispatchcb, ctx) == -1)
767 logerr(__func__);
770 pid_t
771 ps_root_start(struct dhcpcd_ctx *ctx)
773 int fd[2];
774 pid_t pid;
776 if (socketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1)
777 return -1;
778 if (ps_setbuf_fdpair(fd) == -1)
779 return -1;
780 #ifdef PRIVSEP_RIGHTS
781 if (ps_rights_limit_fdpair(fd) == -1)
782 return -1;
783 #endif
785 pid = ps_dostart(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd,
786 ps_root_recvmsg, NULL, ctx,
787 ps_root_startcb, ps_root_signalcb, 0);
789 if (pid == 0) {
790 ctx->ps_data_fd = fd[1];
791 close(fd[0]);
792 return 0;
793 } else if (pid == -1)
794 return -1;
796 ctx->ps_data_fd = fd[0];
797 close(fd[1]);
798 if (eloop_event_add(ctx->eloop, ctx->ps_data_fd,
799 ps_root_dispatch, ctx) == -1)
800 return -1;
802 if ((ctx->ps_eloop = eloop_new()) == NULL)
803 return -1;
805 eloop_signal_set_cb(ctx->ps_eloop,
806 dhcpcd_signals, dhcpcd_signals_len,
807 ps_root_signalcb, ctx);
809 return pid;
813 ps_root_stop(struct dhcpcd_ctx *ctx)
816 return ps_dostop(ctx, &ctx->ps_root_pid, &ctx->ps_root_fd);
819 ssize_t
820 ps_root_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
823 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_SCRIPT, 0, data, len) == -1)
824 return -1;
825 return ps_root_readerror(ctx, NULL, 0);
828 ssize_t
829 ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,
830 size_t len)
832 #ifdef IOCTL_REQUEST_TYPE
833 unsigned long ulreq = 0;
835 memcpy(&ulreq, &req, sizeof(req));
836 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, ulreq, data, len) == -1)
837 return -1;
838 #else
839 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IOCTL, req, data, len) == -1)
840 return -1;
841 #endif
842 return ps_root_readerror(ctx, data, len);
845 ssize_t
846 ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file)
849 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_UNLINK, 0,
850 file, strlen(file) + 1) == -1)
851 return -1;
852 return ps_root_readerror(ctx, NULL, 0);
855 ssize_t
856 ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file,
857 void *data, size_t len)
859 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_READFILE, 0,
860 file, strlen(file) + 1) == -1)
861 return -1;
862 return ps_root_readerror(ctx, data, len);
865 ssize_t
866 ps_root_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
867 const void *data, size_t len)
869 char buf[PS_BUFLEN];
870 size_t flen;
872 flen = strlcpy(buf, file, sizeof(buf));
873 flen += 1;
874 if (flen > sizeof(buf) || flen + len > sizeof(buf)) {
875 errno = ENOBUFS;
876 return -1;
878 memcpy(buf + flen, data, len);
880 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_WRITEFILE, mode,
881 buf, flen + len) == -1)
882 return -1;
883 return ps_root_readerror(ctx, NULL, 0);
886 ssize_t
887 ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
890 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_FILEMTIME, 0,
891 file, strlen(file) + 1) == -1)
892 return -1;
893 return ps_root_readerror(ctx, time, sizeof(*time));
896 #ifdef PRIVSEP_GETIFADDRS
898 ps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead)
900 struct ifaddrs *ifa;
901 void *buf = NULL;
902 char *bp, *sap;
903 socklen_t salen;
904 size_t len;
905 ssize_t err;
907 if (ps_sendcmd(ctx, ctx->ps_root_fd,
908 PS_GETIFADDRS, 0, NULL, 0) == -1)
909 return -1;
910 err = ps_root_mreaderror(ctx, &buf, &len);
912 if (err == -1)
913 return -1;
915 /* Should be impossible - lo0 will always exist. */
916 if (len == 0) {
917 *ifahead = NULL;
918 return 0;
921 bp = buf;
922 *ifahead = (struct ifaddrs *)(void *)bp;
923 for (ifa = *ifahead; ifa != NULL; ifa = ifa->ifa_next) {
924 if (len < ALIGN(sizeof(*ifa)) +
925 ALIGN(IFNAMSIZ) + ALIGN(sizeof(salen) * IFA_NADDRS))
926 goto err;
927 bp += ALIGN(sizeof(*ifa));
928 ifa->ifa_name = bp;
929 bp += ALIGN(IFNAMSIZ);
930 sap = bp;
931 bp += ALIGN(sizeof(salen) * IFA_NADDRS);
932 len -= ALIGN(sizeof(*ifa)) +
933 ALIGN(IFNAMSIZ) + ALIGN(sizeof(salen) * IFA_NADDRS);
935 #define COPYOUTSA(addr) \
936 do { \
937 memcpy(&salen, sap, sizeof(salen)); \
938 if (len < salen) \
939 goto err; \
940 if (salen != 0) { \
941 (addr) = (struct sockaddr *)bp; \
942 bp += ALIGN(salen); \
943 len -= ALIGN(salen); \
945 sap += sizeof(salen); \
946 } while (0 /* CONSTCOND */)
948 COPYOUTSA(ifa->ifa_addr);
949 COPYOUTSA(ifa->ifa_netmask);
950 COPYOUTSA(ifa->ifa_broadaddr);
951 if (len != 0)
952 ifa->ifa_next = (struct ifaddrs *)(void *)bp;
953 else
954 ifa->ifa_next = NULL;
956 return 0;
958 err:
959 free(buf);
960 *ifahead = NULL;
961 errno = EINVAL;
962 return -1;
964 #endif
966 #if defined(__linux__) || defined(HAVE_PLEDGE)
967 ssize_t
968 ps_root_ip6forwarding(struct dhcpcd_ctx *ctx, const char *ifname)
971 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_IP6FORWARDING, 0,
972 ifname, ifname != NULL ? strlen(ifname) + 1 : 0) == -1)
973 return -1;
974 return ps_root_readerror(ctx, NULL, 0);
976 #endif
978 #ifdef AUTH
980 ps_root_getauthrdm(struct dhcpcd_ctx *ctx, uint64_t *rdm)
983 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_AUTH_MONORDM, 0,
984 rdm, sizeof(*rdm))== -1)
985 return -1;
986 return (int)ps_root_readerror(ctx, rdm, sizeof(*rdm));
988 #endif
990 #ifdef PLUGIN_DEV
992 ps_root_dev_initialized(struct dhcpcd_ctx *ctx, const char *ifname)
995 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_DEV_INITTED, 0,
996 ifname, strlen(ifname) + 1)== -1)
997 return -1;
998 return (int)ps_root_readerror(ctx, NULL, 0);
1002 ps_root_dev_listening(struct dhcpcd_ctx * ctx)
1005 if (ps_sendcmd(ctx, ctx->ps_root_fd, PS_DEV_LISTENING, 0, NULL, 0)== -1)
1006 return -1;
1007 return (int)ps_root_readerror(ctx, NULL, 0);
1009 #endif