xref: /dragonfly/contrib/dhcpcd/src/privsep-root.c (revision 6a6d63c5317abf314a78f8c8300ef73c2bc0c39e)
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
ps_root_readerrorcb(void * arg,unsigned short events)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(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;
106 
107 out:
108           eloop_exit(ctx->ps_eloop, exit_code);
109 }
110 
111 ssize_t
ps_root_readerror(struct dhcpcd_ctx * ctx,void * data,size_t len)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           int fd = PS_ROOT_FD(ctx);
119 
120           if (eloop_event_add(ctx->ps_eloop, fd, ELE_READ,
121               ps_root_readerrorcb, &psr_ctx) == -1)
122                     return -1;
123 
124           eloop_enter(ctx->ps_eloop);
125           eloop_start(ctx->ps_eloop, &ctx->sigset);
126           eloop_event_delete(ctx->ps_eloop, fd);
127 
128           errno = psr_ctx.psr_error.psr_errno;
129           return psr_ctx.psr_error.psr_result;
130 }
131 
132 #ifdef PRIVSEP_GETIFADDRS
133 static void
ps_root_mreaderrorcb(void * arg,unsigned short events)134 ps_root_mreaderrorcb(void *arg, unsigned short events)
135 {
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 },
142           };
143           ssize_t len;
144           int exit_code = EXIT_FAILURE;
145 
146           if (events != ELE_READ)
147                     logerrx("%s: unexpected event 0x%04x", __func__, events);
148 
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);
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(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;
172 
173 out:
174           eloop_exit(ctx->ps_eloop, exit_code);
175 }
176 
177 ssize_t
ps_root_mreaderror(struct dhcpcd_ctx * ctx,void ** data,size_t * len)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           int fd = PS_ROOT_FD(ctx);
184 
185           if (eloop_event_add(ctx->ps_eloop, fd, ELE_READ,
186               ps_root_mreaderrorcb, &psr_ctx) == -1)
187                     return -1;
188 
189           eloop_enter(ctx->ps_eloop);
190           eloop_start(ctx->ps_eloop, &ctx->sigset);
191           eloop_event_delete(ctx->ps_eloop, fd);
192 
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;
197 }
198 #endif
199 
200 static ssize_t
ps_root_writeerror(struct dhcpcd_ctx * ctx,ssize_t result,void * data,size_t len)201 ps_root_writeerror(struct dhcpcd_ctx *ctx, ssize_t result,
202     void *data, size_t len)
203 {
204           struct psr_error psr = {
205                     .psr_result = result,
206                     .psr_errno = errno,
207                     .psr_datalen = len,
208           };
209           struct iovec iov[] = {
210                     { .iov_base = &psr, .iov_len = sizeof(psr) },
211                     { .iov_base = data, .iov_len = len },
212           };
213           ssize_t err;
214           int fd = PS_ROOT_FD(ctx);
215 
216 #ifdef PRIVSEP_DEBUG
217           logdebugx("%s: result %zd errno %d", __func__, result, errno);
218 #endif
219 
220           err = writev(fd, iov, __arraycount(iov));
221 
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));
231           }
232 
233           return err;
234 }
235 
236 static ssize_t
ps_root_doioctl(unsigned long req,void * data,size_t len)237 ps_root_doioctl(unsigned long req, void *data, size_t len)
238 {
239           int s, err;
240 
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;
260           }
261 
262           s = xsocket(PF_INET, SOCK_DGRAM, 0);
263           if (s != -1)
264 #ifdef IOCTL_REQUEST_TYPE
265           {
266                     ioctl_request_t reqt;
267 
268                     memcpy(&reqt, &req, sizeof(reqt));
269                     err = ioctl(s, reqt, data, len);
270           }
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;
279 }
280 
281 static ssize_t
ps_root_run_script(struct dhcpcd_ctx * ctx,const void * data,size_t len)282 ps_root_run_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
283 {
284           const char *envbuf = data;
285           char * const argv[] = { ctx->script, NULL };
286           pid_t pid;
287           int status;
288 
289           if (len == 0)
290                     return 0;
291 
292           if (script_buftoenv(ctx, UNCONST(envbuf), len) == NULL)
293                     return -1;
294 
295           pid = script_exec(argv, ctx->script_env);
296           if (pid == -1)
297                     return -1;
298 
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;
305                     }
306           }
307           return status;
308 }
309 
310 static bool
ps_root_validpath(const struct dhcpcd_ctx * ctx,uint16_t cmd,const char * path)311 ps_root_validpath(const struct dhcpcd_ctx *ctx, uint16_t cmd, const char *path)
312 {
313 
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;
318 
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;
326           }
327           if (strncmp(DBDIR, path, strlen(DBDIR)) == 0)
328                     return true;
329           if (strncmp(RUNDIR, path, strlen(RUNDIR)) == 0)
330                     return true;
331 
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
338 
339           errno = EPERM;
340           return false;
341 }
342 
343 static ssize_t
ps_root_dowritefile(const struct dhcpcd_ctx * ctx,mode_t mode,void * data,size_t len)344 ps_root_dowritefile(const struct dhcpcd_ctx *ctx,
345     mode_t mode, void *data, size_t len)
346 {
347           char *file = data, *nc;
348 
349           nc = memchr(file, '\0', len);
350           if (nc == NULL) {
351                     errno = EINVAL;
352                     return -1;
353           }
354 
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));
359 }
360 
361 #ifdef AUTH
362 static ssize_t
ps_root_monordm(uint64_t * rdm,size_t len)363 ps_root_monordm(uint64_t *rdm, size_t len)
364 {
365 
366           if (len != sizeof(*rdm)) {
367                     errno = EINVAL;
368                     return -1;
369           }
370           return auth_get_rdm_monotonic(rdm);
371 }
372 #endif
373 
374 #ifdef PRIVSEP_GETIFADDRS
375 #define   IFA_NADDRS          4
376 static ssize_t
ps_root_dogetifaddrs(void ** rdata,size_t * rlen)377 ps_root_dogetifaddrs(void **rdata, size_t *rlen)
378 {
379           struct ifaddrs *ifaddrs, *ifa;
380           size_t len;
381           uint8_t *buf, *sap;
382           socklen_t salen;
383 
384           if (getifaddrs(&ifaddrs) == -1)
385                     return -1;
386           if (ifaddrs == NULL) {
387                     *rdata = NULL;
388                     *rlen = 0;
389                     return 0;
390           }
391 
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
409                     /*
410                      * On BSD we need to carry ifa_data so we can access
411                      * if_data->ifi_link_state
412                      */
413                     if (ifa->ifa_addr != NULL &&
414                         ifa->ifa_addr->sa_family == AF_LINK)
415                               len += ALIGN(sizeof(struct if_data));
416 #endif
417           }
418 
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;
426           }
427           *rdata = buf;
428           *rlen = len;
429 
430           for (ifa = ifaddrs; ifa != NULL; ifa = ifa->ifa_next) {
431                     memcpy(buf, ifa, sizeof(*ifa));
432                     buf += ALIGN(sizeof(*ifa));
433 
434                     strlcpy((char *)buf, ifa->ifa_name, IFNAMSIZ);
435                     buf += ALIGN(IFNAMSIZ);
436                     sap = buf;
437                     buf += ALIGN(sizeof(salen) * IFA_NADDRS);
438 
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);                              \
449                     }                                                           \
450                     sap += sizeof(salen);                                       \
451           } while (0 /*CONSTCOND */)
452 
453                     COPYINSA(ifa->ifa_addr);
454                     COPYINSA(ifa->ifa_netmask);
455                     COPYINSA(ifa->ifa_broadaddr);
456 
457 #ifdef BSD
458                     if (ifa->ifa_addr != NULL &&
459                         ifa->ifa_addr->sa_family == AF_LINK)
460                     {
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));
468           }
469 
470           freeifaddrs(ifaddrs);
471           return 0;
472 }
473 #endif
474 
475 static ssize_t
ps_root_recvmsgcb(void * arg,struct ps_msghdr * psm,struct msghdr * msg)476 ps_root_recvmsgcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
477 {
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;
488 
489           cmd = (uint16_t)(psm->ps_cmd & ~(PS_START | PS_STOP));
490           psp = ps_findprocess(ctx, &psm->ps_id);
491 
492 #ifdef PRIVSEP_DEBUG
493           logerrx("%s: IN cmd %x, psp %p", __func__, psm->ps_cmd, psp);
494 #endif
495 
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;
506                     }
507 
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);
513                     }
514                     return 0;
515           }
516 
517           if (psm->ps_cmd & PS_STOP && psp == NULL)
518                     return 0;
519 
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;
541           }
542 
543           assert(msg->msg_iovlen == 0 || msg->msg_iovlen == 1);
544 
545           /* Reset errno */
546           errno = 0;
547 
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;
554                     }
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);
564                     }
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;
571                     }
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;
578                     }
579                     err = readfile(data, buf, sizeof(buf));
580                     if (err != -1) {
581                               rdata = buf;
582                               rlen = (size_t)err;
583                     }
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);
594                     }
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;
605                     }
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;
630           }
631 
632           err = ps_root_writeerror(ctx, err, rlen != 0 ? rdata : 0, rlen);
633           if (free_rdata)
634                     free(rdata);
635           return err;
636 }
637 
638 /* Receive from state engine, do an action. */
639 static void
ps_root_recvmsg(void * arg,unsigned short events)640 ps_root_recvmsg(void *arg, unsigned short events)
641 {
642           struct ps_process *psp = arg;
643 
644           if (ps_recvpsmsg(psp->psp_ctx, psp->psp_fd, events,
645               ps_root_recvmsgcb, psp->psp_ctx) == -1)
646                     logerr(__func__);
647 }
648 
649 #ifdef PLUGIN_DEV
650 static int
ps_root_handleinterface(void * arg,int action,const char * ifname)651 ps_root_handleinterface(void *arg, int action, const char *ifname)
652 {
653           struct dhcpcd_ctx *ctx = arg;
654           unsigned long flag;
655 
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;
665           }
666 
667           return (int)ps_sendcmd(ctx, ctx->ps_data_fd, PS_DEV_IFCMD,
668               flag, ifname, strlen(ifname) + 1);
669 }
670 #endif
671 
672 static int
ps_root_startcb(struct ps_process * psp)673 ps_root_startcb(struct ps_process *psp)
674 {
675           struct dhcpcd_ctx *ctx = psp->psp_ctx;
676 
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;
685 
686           if (if_opensockets(ctx) == -1)
687                     logerr("%s: if_opensockets", __func__);
688 
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;
700 
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__);
708           }
709 #endif
710 #ifdef INET6
711           if (ctx->options & DHCPCD_IPV6) {
712                     int buflen = 1;
713 
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__);
720           }
721 #endif
722 #ifdef DHCP6
723           if (ctx->options & DHCPCD_IPV6) {
724                     int buflen = 1;
725 
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__);
732           }
733 #endif
734 
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
742 
743           return 0;
744 }
745 
746 void
ps_root_signalcb(int sig,void * arg)747 ps_root_signalcb(int sig, void *arg)
748 {
749           struct dhcpcd_ctx *ctx = arg;
750           int status;
751           pid_t pid;
752           const char *ifname, *name;
753           struct ps_process *psp;
754 
755           if (sig != SIGCHLD)
756                     return;
757 
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";
769                     }
770 
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);
785 
786                     if (psp != NULL)
787                               ps_freeprocess(psp);
788           }
789 
790           if (!(ctx->options & DHCPCD_EXITING))
791                     return;
792           if (!(ps_waitforprocs(ctx)))
793                     eloop_exit(ctx->ps_eloop, EXIT_SUCCESS);
794 }
795 
796 int (*handle_interface)(void *, int, const char *);
797 
798 #ifdef PLUGIN_DEV
799 static ssize_t
ps_root_devcb(struct dhcpcd_ctx * ctx,struct ps_msghdr * psm,struct msghdr * msg)800 ps_root_devcb(struct dhcpcd_ctx *ctx, struct ps_msghdr *psm, struct msghdr *msg)
801 {
802           int action;
803           struct iovec *iov = msg->msg_iov;
804 
805           if (msg->msg_iovlen != 1) {
806                     errno = EINVAL;
807                     return -1;
808           }
809 
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;
823           }
824 
825           return dhcpcd_handleinterface(ctx, action, iov->iov_base);
826 }
827 #endif
828 
829 static ssize_t
ps_root_dispatchcb(void * arg,struct ps_msghdr * psm,struct msghdr * msg)830 ps_root_dispatchcb(void *arg, struct ps_msghdr *psm, struct msghdr *msg)
831 {
832           struct dhcpcd_ctx *ctx = arg;
833           ssize_t err;
834 
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);
847           }
848           return err;
849 }
850 
851 static void
ps_root_dispatch(void * arg,unsigned short events)852 ps_root_dispatch(void *arg, unsigned short events)
853 {
854           struct dhcpcd_ctx *ctx = arg;
855 
856           if (ps_recvpsmsg(ctx, ctx->ps_data_fd, events,
857               ps_root_dispatchcb, ctx) == -1)
858                     logerr(__func__);
859 }
860 
861 static void
ps_root_log(void * arg,unsigned short events)862 ps_root_log(void *arg, unsigned short events)
863 {
864           struct dhcpcd_ctx *ctx = arg;
865 
866           if (events != ELE_READ)
867                     logerrx("%s: unexpected event 0x%04x", __func__, events);
868 
869           if (logreadfd(ctx->ps_log_root_fd) == -1)
870                     logerr(__func__);
871 }
872 
873 pid_t
ps_root_start(struct dhcpcd_ctx * ctx)874 ps_root_start(struct dhcpcd_ctx *ctx)
875 {
876           struct ps_id id = {
877                     .psi_ifindex = 0,
878                     .psi_cmd = PS_ROOT,
879           };
880           struct ps_process *psp;
881           int logfd[2], datafd[2];
882           pid_t pid;
883 
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
890 
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
899 
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);
904 
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;
916 
917           logsetfd(logfd[0]);
918           close(logfd[1]);
919 
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;
925 
926           return pid;
927 }
928 
929 void
ps_root_close(struct dhcpcd_ctx * ctx)930 ps_root_close(struct dhcpcd_ctx *ctx)
931 {
932 
933           if_closesockets(ctx);
934 
935 #ifdef INET
936           if (ctx->udp_wfd != -1) {
937                     close(ctx->udp_wfd);
938                     ctx->udp_wfd = -1;
939           }
940 #endif
941 #ifdef INET6
942           if (ctx->nd_fd != -1) {
943                     close(ctx->nd_fd);
944                     ctx->nd_fd = -1;
945           }
946 #endif
947 #ifdef DHCP6
948           if (ctx->dhcp6_wfd != -1) {
949                     close(ctx->dhcp6_wfd);
950                     ctx->dhcp6_wfd = -1;
951           }
952 #endif
953 }
954 
955 int
ps_root_stop(struct dhcpcd_ctx * ctx)956 ps_root_stop(struct dhcpcd_ctx *ctx)
957 {
958           struct ps_process *psp = ctx->ps_root;
959 
960           if (!(ctx->options & DHCPCD_PRIVSEP) ||
961               ctx->eloop == NULL)
962                     return 0;
963 
964           /* If we are the root process then remove the pidfile */
965           if (ctx->options & DHCPCD_PRIVSEPROOT &&
966               !(ctx->options & DHCPCD_TEST))
967           {
968                     if (unlink(ctx->pidfile) == -1)
969                               logerr("%s: unlink: %s", __func__, ctx->pidfile);
970           }
971 
972           /* Only the manager process gets past this point. */
973           if (ctx->options & DHCPCD_FORKED)
974                     return 0;
975 
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);
985 
986                     if (ps_stopprocess(psp) == -1)
987                               return -1;
988           } /* else the root process has already exited :( */
989 
990           return ps_stopwait(ctx);
991 }
992 
993 ssize_t
ps_root_stopprocesses(struct dhcpcd_ctx * ctx)994 ps_root_stopprocesses(struct dhcpcd_ctx *ctx)
995 {
996 
997           if (!(IN_PRIVSEP_SE(ctx)))
998                     return 0;
999 
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);
1004 }
1005 
1006 ssize_t
ps_root_script(struct dhcpcd_ctx * ctx,const void * data,size_t len)1007 ps_root_script(struct dhcpcd_ctx *ctx, const void *data, size_t len)
1008 {
1009 
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);
1014 }
1015 
1016 ssize_t
ps_root_ioctl(struct dhcpcd_ctx * ctx,ioctl_request_t req,void * data,size_t len)1017 ps_root_ioctl(struct dhcpcd_ctx *ctx, ioctl_request_t req, void *data,
1018     size_t len)
1019 {
1020           int fd = PS_ROOT_FD(ctx);
1021 #ifdef IOCTL_REQUEST_TYPE
1022           unsigned long ulreq = 0;
1023 
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);
1032 }
1033 
1034 ssize_t
ps_root_unlink(struct dhcpcd_ctx * ctx,const char * file)1035 ps_root_unlink(struct dhcpcd_ctx *ctx, const char *file)
1036 {
1037 
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);
1042 }
1043 
1044 ssize_t
ps_root_readfile(struct dhcpcd_ctx * ctx,const char * file,void * data,size_t len)1045 ps_root_readfile(struct dhcpcd_ctx *ctx, const char *file,
1046     void *data, size_t len)
1047 {
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);
1052 }
1053 
1054 ssize_t
ps_root_writefile(struct dhcpcd_ctx * ctx,const char * file,mode_t mode,const void * data,size_t len)1055 ps_root_writefile(struct dhcpcd_ctx *ctx, const char *file, mode_t mode,
1056     const void *data, size_t len)
1057 {
1058           char buf[PS_BUFLEN];
1059           size_t flen;
1060 
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;
1066           }
1067           memcpy(buf + flen, data, len);
1068 
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);
1073 }
1074 
1075 ssize_t
ps_root_filemtime(struct dhcpcd_ctx * ctx,const char * file,time_t * time)1076 ps_root_filemtime(struct dhcpcd_ctx *ctx, const char *file, time_t *time)
1077 {
1078 
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));
1083 }
1084 
1085 ssize_t
ps_root_logreopen(struct dhcpcd_ctx * ctx)1086 ps_root_logreopen(struct dhcpcd_ctx *ctx)
1087 {
1088 
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);
1093 }
1094 
1095 #ifdef PRIVSEP_GETIFADDRS
1096 int
ps_root_getifaddrs(struct dhcpcd_ctx * ctx,struct ifaddrs ** ifahead)1097 ps_root_getifaddrs(struct dhcpcd_ctx *ctx, struct ifaddrs **ifahead)
1098 {
1099           struct ifaddrs *ifa;
1100           void *buf = NULL;
1101           char *bp, *sap;
1102           socklen_t salen;
1103           size_t len;
1104           ssize_t err;
1105 
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);
1110 
1111           if (err == -1)
1112                     return -1;
1113 
1114           /* Should be impossible - lo0 will always exist. */
1115           if (len == 0) {
1116                     *ifahead = NULL;
1117                     return 0;
1118           }
1119 
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);
1133 
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);                                        \
1143                     }                                                                     \
1144                     sap += sizeof(salen);                                                 \
1145           } while (0 /* CONSTCOND */)
1146 
1147                     COPYOUTSA(ifa->ifa_addr);
1148                     COPYOUTSA(ifa->ifa_netmask);
1149                     COPYOUTSA(ifa->ifa_broadaddr);
1150 
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;
1160 
1161                     if (len != 0)
1162                               ifa->ifa_next = (struct ifaddrs *)(void *)bp;
1163                     else
1164                               ifa->ifa_next = NULL;
1165           }
1166           return 0;
1167 
1168 err:
1169           free(buf);
1170           *ifahead = NULL;
1171           errno = EINVAL;
1172           return -1;
1173 }
1174 #endif
1175 
1176 #if defined(__linux__) || defined(HAVE_PLEDGE)
1177 ssize_t
ps_root_ip6forwarding(struct dhcpcd_ctx * ctx,const char * ifname)1178 ps_root_ip6forwarding(struct dhcpcd_ctx *ctx, const char *ifname)
1179 {
1180 
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);
1185 }
1186 #endif
1187 
1188 #ifdef AUTH
1189 int
ps_root_getauthrdm(struct dhcpcd_ctx * ctx,uint64_t * rdm)1190 ps_root_getauthrdm(struct dhcpcd_ctx *ctx, uint64_t *rdm)
1191 {
1192 
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));
1197 }
1198 #endif
1199 
1200 #ifdef PLUGIN_DEV
1201 int
ps_root_dev_initialised(struct dhcpcd_ctx * ctx,const char * ifname)1202 ps_root_dev_initialised(struct dhcpcd_ctx *ctx, const char *ifname)
1203 {
1204 
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);
1209 }
1210 
1211 int
ps_root_dev_listening(struct dhcpcd_ctx * ctx)1212 ps_root_dev_listening(struct dhcpcd_ctx * ctx)
1213 {
1214 
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);
1219 }
1220 #endif
1221