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