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