1 /* SPDX-License-Identifier: BSD-2-Clause */
2 /*
3 * Privilege Separation for dhcpcd
4 * Copyright (c) 2006-2021 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 /*
30 * The current design is this:
31 * Spawn a priv process to carry out privileged actions and
32 * spawning unpriv process to initate network connections such as BPF
33 * or address specific listener.
34 * Spawn an unpriv process to send/receive common network data.
35 * Then drop all privs and start running.
36 * Every process aside from the privileged proxy is chrooted.
37 * All privsep processes ignore signals - only the manager process accepts them.
38 *
39 * dhcpcd will maintain the config file in the chroot, no need to handle
40 * this in a script or something.
41 */
42
43 #include <sys/resource.h>
44 #include <sys/socket.h>
45 #include <sys/stat.h>
46 #include <sys/types.h>
47 #include <sys/wait.h>
48
49 #ifdef AF_LINK
50 #include <net/if_dl.h>
51 #endif
52
53 #include <assert.h>
54 #include <errno.h>
55 #include <fcntl.h>
56 #include <grp.h>
57 #include <paths.h>
58 #include <pwd.h>
59 #include <stddef.h> /* For offsetof, struct padding debug */
60 #include <signal.h>
61 #include <stdlib.h>
62 #include <string.h>
63 #include <unistd.h>
64
65 #include "arp.h"
66 #include "common.h"
67 #include "control.h"
68 #include "dev.h"
69 #include "dhcp.h"
70 #include "dhcp6.h"
71 #include "eloop.h"
72 #include "ipv6nd.h"
73 #include "logerr.h"
74 #include "privsep.h"
75
76 #ifdef HAVE_CAPSICUM
77 #include <sys/capsicum.h>
78 #include <capsicum_helpers.h>
79 #endif
80 #ifdef HAVE_UTIL_H
81 #include <util.h>
82 #endif
83
84 /* CMSG_ALIGN is a Linux extension */
85 #ifndef CMSG_ALIGN
86 #define CMSG_ALIGN(n) (CMSG_SPACE((n)) - CMSG_SPACE(0))
87 #endif
88
89 /* Calculate number of padding bytes to achieve 'struct cmsghdr' alignment */
90 #define CALC_CMSG_PADLEN(has_cmsg, pos) \
91 ((has_cmsg) ? (socklen_t)(CMSG_ALIGN((pos)) - (pos)) : 0)
92
93 int
ps_init(struct dhcpcd_ctx * ctx)94 ps_init(struct dhcpcd_ctx *ctx)
95 {
96 struct passwd *pw;
97 struct stat st;
98
99 errno = 0;
100 if ((ctx->ps_user = pw = getpwnam(PRIVSEP_USER)) == NULL) {
101 ctx->options &= ~DHCPCD_PRIVSEP;
102 if (errno == 0) {
103 logerrx("no such user %s", PRIVSEP_USER);
104 /* Just incase logerrx caused an error... */
105 errno = 0;
106 } else
107 logerr("getpwnam");
108 return -1;
109 }
110
111 if (stat(pw->pw_dir, &st) == -1 || !S_ISDIR(st.st_mode)) {
112 ctx->options &= ~DHCPCD_PRIVSEP;
113 logerrx("refusing chroot: %s: %s",
114 PRIVSEP_USER, pw->pw_dir);
115 errno = 0;
116 return -1;
117 }
118
119 ctx->options |= DHCPCD_PRIVSEP;
120 return 0;
121 }
122
123 static int
ps_dropprivs(struct dhcpcd_ctx * ctx)124 ps_dropprivs(struct dhcpcd_ctx *ctx)
125 {
126 struct passwd *pw = ctx->ps_user;
127
128 if (ctx->options & DHCPCD_LAUNCHER)
129 logdebugx("chrooting as %s to %s", pw->pw_name, pw->pw_dir);
130 if (chroot(pw->pw_dir) == -1 &&
131 (errno != EPERM || ctx->options & DHCPCD_FORKED))
132 logerr("%s: chroot: %s", __func__, pw->pw_dir);
133 if (chdir("/") == -1)
134 logerr("%s: chdir: /", __func__);
135
136 if ((setgroups(1, &pw->pw_gid) == -1 ||
137 setgid(pw->pw_gid) == -1 ||
138 setuid(pw->pw_uid) == -1) &&
139 (errno != EPERM || ctx->options & DHCPCD_FORKED))
140 {
141 logerr("failed to drop privileges");
142 return -1;
143 }
144
145 struct rlimit rzero = { .rlim_cur = 0, .rlim_max = 0 };
146
147 if (ctx->ps_control_pid != getpid()) {
148 /* Prohibit new files, sockets, etc */
149 #if defined(__linux__) || defined(__sun) || defined(__OpenBSD__)
150 /*
151 * If poll(2) is called with nfds > RLIMIT_NOFILE
152 * then it returns EINVAL.
153 * This blows.
154 * Do the best we can and limit to what we need.
155 * An attacker could potentially close a file and
156 * open a new one still, but that cannot be helped.
157 */
158 unsigned long maxfd;
159 maxfd = (unsigned long)eloop_event_count(ctx->eloop);
160 if (IN_PRIVSEP_SE(ctx))
161 maxfd++; /* XXX why? */
162
163 struct rlimit rmaxfd = {
164 .rlim_cur = maxfd,
165 .rlim_max = maxfd
166 };
167 if (setrlimit(RLIMIT_NOFILE, &rmaxfd) == -1)
168 logerr("setrlimit RLIMIT_NOFILE");
169 #else
170 if (setrlimit(RLIMIT_NOFILE, &rzero) == -1)
171 logerr("setrlimit RLIMIT_NOFILE");
172 #endif
173 }
174
175 #define DHC_NOCHKIO (DHCPCD_STARTED | DHCPCD_DAEMONISE)
176 /* Prohibit writing to files.
177 * Obviously this won't work if we are using a logfile
178 * or redirecting stderr to a file. */
179 if ((ctx->options & DHC_NOCHKIO) == DHC_NOCHKIO ||
180 (ctx->logfile == NULL &&
181 (!ctx->stderr_valid || isatty(STDERR_FILENO) == 1)))
182 {
183 if (setrlimit(RLIMIT_FSIZE, &rzero) == -1)
184 logerr("setrlimit RLIMIT_FSIZE");
185 }
186
187 #ifdef RLIMIT_NPROC
188 /* Prohibit forks */
189 if (setrlimit(RLIMIT_NPROC, &rzero) == -1)
190 logerr("setrlimit RLIMIT_NPROC");
191 #endif
192
193 return 0;
194 }
195
196 static int
ps_setbuf0(int fd,int ctl,int minlen)197 ps_setbuf0(int fd, int ctl, int minlen)
198 {
199 int len;
200 socklen_t slen;
201
202 slen = sizeof(len);
203 if (getsockopt(fd, SOL_SOCKET, ctl, &len, &slen) == -1)
204 return -1;
205
206 #ifdef __linux__
207 len /= 2;
208 #endif
209 if (len >= minlen)
210 return 0;
211
212 return setsockopt(fd, SOL_SOCKET, ctl, &minlen, sizeof(minlen));
213 }
214
215 static int
ps_setbuf(int fd)216 ps_setbuf(int fd)
217 {
218 /* Ensure we can receive a fully sized privsep message.
219 * Double the send buffer. */
220 int minlen = (int)sizeof(struct ps_msg);
221
222 if (ps_setbuf0(fd, SO_RCVBUF, minlen) == -1 ||
223 ps_setbuf0(fd, SO_SNDBUF, minlen * 2) == -1)
224 {
225 logerr(__func__);
226 return -1;
227 }
228 return 0;
229 }
230
231 int
ps_setbuf_fdpair(int fd[])232 ps_setbuf_fdpair(int fd[])
233 {
234
235 if (ps_setbuf(fd[0]) == -1 || ps_setbuf(fd[1]) == -1)
236 return -1;
237 return 0;
238 }
239
240 #ifdef PRIVSEP_RIGHTS
241 int
ps_rights_limit_ioctl(int fd)242 ps_rights_limit_ioctl(int fd)
243 {
244 cap_rights_t rights;
245
246 cap_rights_init(&rights, CAP_IOCTL);
247 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
248 return -1;
249 return 0;
250 }
251
252 int
ps_rights_limit_fd_fctnl(int fd)253 ps_rights_limit_fd_fctnl(int fd)
254 {
255 cap_rights_t rights;
256
257 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT,
258 CAP_ACCEPT, CAP_FCNTL);
259 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
260 return -1;
261 return 0;
262 }
263
264 int
ps_rights_limit_fd(int fd)265 ps_rights_limit_fd(int fd)
266 {
267 cap_rights_t rights;
268
269 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT, CAP_SHUTDOWN);
270 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
271 return -1;
272 return 0;
273 }
274
275 int
ps_rights_limit_fd_sockopt(int fd)276 ps_rights_limit_fd_sockopt(int fd)
277 {
278 cap_rights_t rights;
279
280 cap_rights_init(&rights, CAP_READ, CAP_WRITE, CAP_EVENT,
281 CAP_GETSOCKOPT, CAP_SETSOCKOPT);
282 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
283 return -1;
284 return 0;
285 }
286
287 int
ps_rights_limit_fd_rdonly(int fd)288 ps_rights_limit_fd_rdonly(int fd)
289 {
290 cap_rights_t rights;
291
292 cap_rights_init(&rights, CAP_READ, CAP_EVENT);
293 if (cap_rights_limit(fd, &rights) == -1 && errno != ENOSYS)
294 return -1;
295 return 0;
296 }
297
298 int
ps_rights_limit_fdpair(int fd[])299 ps_rights_limit_fdpair(int fd[])
300 {
301
302 if (ps_rights_limit_fd(fd[0]) == -1 || ps_rights_limit_fd(fd[1]) == -1)
303 return -1;
304 return 0;
305 }
306
307 static int
ps_rights_limit_stdio(struct dhcpcd_ctx * ctx)308 ps_rights_limit_stdio(struct dhcpcd_ctx *ctx)
309 {
310 const int iebadf = CAPH_IGNORE_EBADF;
311 int error = 0;
312
313 if (ctx->stdin_valid &&
314 caph_limit_stream(STDIN_FILENO, CAPH_READ | iebadf) == -1)
315 error = -1;
316 if (ctx->stdout_valid &&
317 caph_limit_stream(STDOUT_FILENO, CAPH_WRITE | iebadf) == -1)
318 error = -1;
319 if (ctx->stderr_valid &&
320 caph_limit_stream(STDERR_FILENO, CAPH_WRITE | iebadf) == -1)
321 error = -1;
322
323 return error;
324 }
325 #endif
326
327 pid_t
ps_dostart(struct dhcpcd_ctx * ctx,pid_t * priv_pid,int * priv_fd,void (* recv_msg)(void *),void (* recv_unpriv_msg),void * recv_ctx,int (* callback)(void *),void (* signal_cb)(int,void *),unsigned int flags)328 ps_dostart(struct dhcpcd_ctx *ctx,
329 pid_t *priv_pid, int *priv_fd,
330 void (*recv_msg)(void *), void (*recv_unpriv_msg),
331 void *recv_ctx, int (*callback)(void *), void (*signal_cb)(int, void *),
332 unsigned int flags)
333 {
334 int fd[2];
335 pid_t pid;
336
337 if (xsocketpair(AF_UNIX, SOCK_DGRAM | SOCK_CXNB, 0, fd) == -1) {
338 logerr("%s: socketpair", __func__);
339 return -1;
340 }
341 if (ps_setbuf_fdpair(fd) == -1) {
342 logerr("%s: ps_setbuf_fdpair", __func__);
343 return -1;
344 }
345 #ifdef PRIVSEP_RIGHTS
346 if (ps_rights_limit_fdpair(fd) == -1) {
347 logerr("%s: ps_rights_limit_fdpair", __func__);
348 return -1;
349 }
350 #endif
351
352 switch (pid = fork()) {
353 case -1:
354 logerr("fork");
355 return -1;
356 case 0:
357 *priv_fd = fd[1];
358 close(fd[0]);
359 break;
360 default:
361 *priv_pid = pid;
362 *priv_fd = fd[0];
363 close(fd[1]);
364 if (recv_unpriv_msg == NULL)
365 ;
366 else if (eloop_event_add(ctx->eloop, *priv_fd,
367 recv_unpriv_msg, recv_ctx) == -1)
368 {
369 logerr("%s: eloop_event_add", __func__);
370 return -1;
371 }
372 return pid;
373 }
374
375 ctx->options |= DHCPCD_FORKED;
376 if (ctx->fork_fd != -1) {
377 close(ctx->fork_fd);
378 ctx->fork_fd = -1;
379 }
380 pidfile_clean();
381
382 eloop_clear(ctx->eloop);
383 eloop_signal_set_cb(ctx->eloop,
384 dhcpcd_signals, dhcpcd_signals_len, signal_cb, ctx);
385 /* ctx->sigset aready has the initial sigmask set in main() */
386 if (eloop_signal_mask(ctx->eloop, NULL) == -1) {
387 logerr("%s: eloop_signal_mask", __func__);
388 goto errexit;
389 }
390
391 /* We are not root */
392 if (priv_fd != &ctx->ps_root_fd) {
393 ps_freeprocesses(ctx, recv_ctx);
394 if (ctx->ps_root_fd != -1) {
395 close(ctx->ps_root_fd);
396 ctx->ps_root_fd = -1;
397 }
398
399 #ifdef PRIVSEP_RIGHTS
400 /* We cannot limit the root process in any way. */
401 if (ps_rights_limit_stdio(ctx) == -1) {
402 logerr("ps_rights_limit_stdio");
403 goto errexit;
404 }
405 #endif
406 }
407
408 if (priv_fd != &ctx->ps_inet_fd && ctx->ps_inet_fd != -1) {
409 close(ctx->ps_inet_fd);
410 ctx->ps_inet_fd = -1;
411 }
412
413 if (eloop_event_add(ctx->eloop, *priv_fd, recv_msg, recv_ctx) == -1)
414 {
415 logerr("%s: eloop_event_add", __func__);
416 goto errexit;
417 }
418
419 if (callback(recv_ctx) == -1)
420 goto errexit;
421
422 if (flags & PSF_DROPPRIVS)
423 ps_dropprivs(ctx);
424
425 return 0;
426
427 errexit:
428 /* Failure to start root or inet processes is fatal. */
429 if (priv_fd == &ctx->ps_root_fd || priv_fd == &ctx->ps_inet_fd)
430 (void)ps_sendcmd(ctx, *priv_fd, PS_STOP, 0, NULL, 0);
431 shutdown(*priv_fd, SHUT_RDWR);
432 *priv_fd = -1;
433 eloop_exit(ctx->eloop, EXIT_FAILURE);
434 return -1;
435 }
436
437 int
ps_dostop(struct dhcpcd_ctx * ctx,pid_t * pid,int * fd)438 ps_dostop(struct dhcpcd_ctx *ctx, pid_t *pid, int *fd)
439 {
440 int err = 0;
441
442 #ifdef PRIVSEP_DEBUG
443 logdebugx("%s: pid=%d fd=%d", __func__, *pid, *fd);
444 #endif
445
446 if (*fd != -1) {
447 eloop_event_delete(ctx->eloop, *fd);
448 if (ps_sendcmd(ctx, *fd, PS_STOP, 0, NULL, 0) == -1) {
449 logerr(__func__);
450 err = -1;
451 }
452 (void)shutdown(*fd, SHUT_RDWR);
453 close(*fd);
454 *fd = -1;
455 }
456
457 /* Don't wait for the process as it may not respond to the shutdown
458 * request. We'll reap the process on receipt of SIGCHLD. */
459 *pid = 0;
460 return err;
461 }
462
463 int
ps_start(struct dhcpcd_ctx * ctx)464 ps_start(struct dhcpcd_ctx *ctx)
465 {
466 pid_t pid;
467
468 TAILQ_INIT(&ctx->ps_processes);
469
470 switch (pid = ps_root_start(ctx)) {
471 case -1:
472 logerr("ps_root_start");
473 return -1;
474 case 0:
475 return 0;
476 default:
477 logdebugx("spawned privileged proxy on PID %d", pid);
478 }
479
480 /* No point in spawning the generic network listener if we're
481 * not going to use it. */
482 if (!ps_inet_canstart(ctx))
483 goto started_net;
484
485 switch (pid = ps_inet_start(ctx)) {
486 case -1:
487 return -1;
488 case 0:
489 return 0;
490 default:
491 logdebugx("spawned network proxy on PID %d", pid);
492 }
493
494 started_net:
495 if (!(ctx->options & DHCPCD_TEST)) {
496 switch (pid = ps_ctl_start(ctx)) {
497 case -1:
498 return -1;
499 case 0:
500 return 0;
501 default:
502 logdebugx("spawned controller proxy on PID %d", pid);
503 }
504 }
505
506 #ifdef ARC4RANDOM_H
507 /* Seed the random number generator early incase it needs /dev/urandom
508 * which won't be available in the chroot. */
509 arc4random();
510 #endif
511
512 return 1;
513 }
514
515 int
ps_entersandbox(const char * _pledge,const char ** sandbox)516 ps_entersandbox(const char *_pledge, const char **sandbox)
517 {
518
519 #if !defined(HAVE_PLEDGE)
520 UNUSED(_pledge);
521 #endif
522
523 #if defined(HAVE_CAPSICUM)
524 if (sandbox != NULL)
525 *sandbox = "capsicum";
526 return cap_enter();
527 #elif defined(HAVE_PLEDGE)
528 if (sandbox != NULL)
529 *sandbox = "pledge";
530 return pledge(_pledge, NULL);
531 #elif defined(HAVE_SECCOMP)
532 if (sandbox != NULL)
533 *sandbox = "seccomp";
534 return ps_seccomp_enter();
535 #else
536 if (sandbox != NULL)
537 *sandbox = "posix resource limited";
538 return 0;
539 #endif
540 }
541
542 int
ps_managersandbox(struct dhcpcd_ctx * ctx,const char * _pledge)543 ps_managersandbox(struct dhcpcd_ctx *ctx, const char *_pledge)
544 {
545 const char *sandbox = NULL;
546 bool forked;
547 int dropped;
548
549 forked = ctx->options & DHCPCD_FORKED;
550 ctx->options &= ~DHCPCD_FORKED;
551 dropped = ps_dropprivs(ctx);
552 if (forked)
553 ctx->options |= DHCPCD_FORKED;
554
555 /*
556 * If we don't have a root process, we cannot use syslog.
557 * If it cannot be opened before chrooting then syslog(3) will fail.
558 * openlog(3) does not return an error which doubly sucks.
559 */
560 if (ctx->ps_root_fd == -1) {
561 unsigned int logopts = loggetopts();
562
563 logopts &= ~LOGERR_LOG;
564 logsetopts(logopts);
565 }
566
567 if (dropped == -1) {
568 logerr("%s: ps_dropprivs", __func__);
569 return -1;
570 }
571
572 #ifdef PRIVSEP_RIGHTS
573 if ((ctx->pf_inet_fd != -1 &&
574 ps_rights_limit_ioctl(ctx->pf_inet_fd) == -1) ||
575 ps_rights_limit_stdio(ctx) == -1)
576 {
577 logerr("%s: cap_rights_limit", __func__);
578 return -1;
579 }
580 #endif
581
582 if (_pledge == NULL)
583 _pledge = "stdio";
584 if (ps_entersandbox(_pledge, &sandbox) == -1) {
585 if (errno == ENOSYS) {
586 if (sandbox != NULL)
587 logwarnx("sandbox unavailable: %s", sandbox);
588 return 0;
589 }
590 logerr("%s: %s", __func__, sandbox);
591 return -1;
592 } else if (ctx->options & DHCPCD_LAUNCHER ||
593 ((!(ctx->options & DHCPCD_DAEMONISE)) &&
594 ctx->options & DHCPCD_MANAGER))
595 logdebugx("sandbox: %s", sandbox);
596 return 0;
597 }
598
599 int
ps_stop(struct dhcpcd_ctx * ctx)600 ps_stop(struct dhcpcd_ctx *ctx)
601 {
602 int r, ret = 0;
603
604 if (!(ctx->options & DHCPCD_PRIVSEP) ||
605 ctx->options & DHCPCD_FORKED ||
606 ctx->eloop == NULL)
607 return 0;
608
609 r = ps_ctl_stop(ctx);
610 if (r != 0)
611 ret = r;
612
613 r = ps_inet_stop(ctx);
614 if (r != 0)
615 ret = r;
616
617 /* We've been chrooted, so we need to tell the
618 * privileged proxy to remove the pidfile. */
619 ps_root_unlink(ctx, ctx->pidfile);
620
621 r = ps_root_stop(ctx);
622 if (r != 0)
623 ret = r;
624
625 ctx->options &= ~DHCPCD_PRIVSEP;
626 return ret;
627 }
628
629 void
ps_freeprocess(struct ps_process * psp)630 ps_freeprocess(struct ps_process *psp)
631 {
632
633 TAILQ_REMOVE(&psp->psp_ctx->ps_processes, psp, next);
634 if (psp->psp_fd != -1) {
635 eloop_event_delete(psp->psp_ctx->eloop, psp->psp_fd);
636 close(psp->psp_fd);
637 }
638 if (psp->psp_work_fd != -1) {
639 eloop_event_delete(psp->psp_ctx->eloop, psp->psp_work_fd);
640 close(psp->psp_work_fd);
641 }
642 #ifdef INET
643 if (psp->psp_bpf != NULL)
644 bpf_close(psp->psp_bpf);
645 #endif
646 free(psp);
647 }
648
649 static void
ps_free(struct dhcpcd_ctx * ctx)650 ps_free(struct dhcpcd_ctx *ctx)
651 {
652 struct ps_process *psp;
653 bool stop = ctx->ps_root_pid == getpid();
654
655 while ((psp = TAILQ_FIRST(&ctx->ps_processes)) != NULL) {
656 if (stop)
657 ps_dostop(ctx, &psp->psp_pid, &psp->psp_fd);
658 ps_freeprocess(psp);
659 }
660 }
661
662 int
ps_unrollmsg(struct msghdr * msg,struct ps_msghdr * psm,const void * data,size_t len)663 ps_unrollmsg(struct msghdr *msg, struct ps_msghdr *psm,
664 const void *data, size_t len)
665 {
666 uint8_t *datap, *namep, *controlp;
667 socklen_t cmsg_padlen =
668 CALC_CMSG_PADLEN(psm->ps_controllen, psm->ps_namelen);
669
670 namep = UNCONST(data);
671 controlp = namep + psm->ps_namelen + cmsg_padlen;
672 datap = controlp + psm->ps_controllen;
673
674 if (psm->ps_namelen != 0) {
675 if (psm->ps_namelen > len) {
676 errno = EINVAL;
677 return -1;
678 }
679 msg->msg_name = namep;
680 len -= psm->ps_namelen;
681 } else
682 msg->msg_name = NULL;
683 msg->msg_namelen = psm->ps_namelen;
684
685 if (psm->ps_controllen != 0) {
686 if (psm->ps_controllen > len) {
687 errno = EINVAL;
688 return -1;
689 }
690 msg->msg_control = controlp;
691 len -= psm->ps_controllen + cmsg_padlen;
692 } else
693 msg->msg_control = NULL;
694 msg->msg_controllen = psm->ps_controllen;
695
696 if (len != 0) {
697 msg->msg_iovlen = 1;
698 msg->msg_iov[0].iov_base = datap;
699 msg->msg_iov[0].iov_len = len;
700 } else {
701 msg->msg_iovlen = 0;
702 msg->msg_iov[0].iov_base = NULL;
703 msg->msg_iov[0].iov_len = 0;
704 }
705 return 0;
706 }
707
708 ssize_t
ps_sendpsmmsg(struct dhcpcd_ctx * ctx,int fd,struct ps_msghdr * psm,const struct msghdr * msg)709 ps_sendpsmmsg(struct dhcpcd_ctx *ctx, int fd,
710 struct ps_msghdr *psm, const struct msghdr *msg)
711 {
712 long padding[1] = { 0 };
713 struct iovec iov[] = {
714 { .iov_base = UNCONST(psm), .iov_len = sizeof(*psm) },
715 { .iov_base = NULL, }, /* name */
716 { .iov_base = NULL, }, /* control padding */
717 { .iov_base = NULL, }, /* control */
718 { .iov_base = NULL, }, /* payload 1 */
719 { .iov_base = NULL, }, /* payload 2 */
720 { .iov_base = NULL, }, /* payload 3 */
721 };
722 int iovlen;
723 ssize_t len;
724
725 if (msg != NULL) {
726 struct iovec *iovp = &iov[1];
727 int i;
728 socklen_t cmsg_padlen;
729
730 psm->ps_namelen = msg->msg_namelen;
731 psm->ps_controllen = (socklen_t)msg->msg_controllen;
732
733 iovp->iov_base = msg->msg_name;
734 iovp->iov_len = msg->msg_namelen;
735 iovp++;
736
737 cmsg_padlen =
738 CALC_CMSG_PADLEN(msg->msg_controllen, msg->msg_namelen);
739 assert(cmsg_padlen <= sizeof(padding));
740 iovp->iov_len = cmsg_padlen;
741 iovp->iov_base = cmsg_padlen != 0 ? padding : NULL;
742 iovp++;
743
744 iovp->iov_base = msg->msg_control;
745 iovp->iov_len = msg->msg_controllen;
746 iovlen = 4;
747
748 for (i = 0; i < (int)msg->msg_iovlen; i++) {
749 if ((size_t)(iovlen + i) > __arraycount(iov)) {
750 errno = ENOBUFS;
751 return -1;
752 }
753 iovp++;
754 iovp->iov_base = msg->msg_iov[i].iov_base;
755 iovp->iov_len = msg->msg_iov[i].iov_len;
756 }
757 iovlen += i;
758 } else
759 iovlen = 1;
760
761 len = writev(fd, iov, iovlen);
762 if (len == -1) {
763 logerr(__func__);
764 if (ctx->options & DHCPCD_FORKED &&
765 !(ctx->options & DHCPCD_PRIVSEPROOT))
766 eloop_exit(ctx->eloop, EXIT_FAILURE);
767 }
768 return len;
769 }
770
771 ssize_t
ps_sendpsmdata(struct dhcpcd_ctx * ctx,int fd,struct ps_msghdr * psm,const void * data,size_t len)772 ps_sendpsmdata(struct dhcpcd_ctx *ctx, int fd,
773 struct ps_msghdr *psm, const void *data, size_t len)
774 {
775 struct iovec iov[] = {
776 { .iov_base = UNCONST(data), .iov_len = len },
777 };
778 struct msghdr msg = {
779 .msg_iov = iov, .msg_iovlen = 1,
780 };
781
782 return ps_sendpsmmsg(ctx, fd, psm, &msg);
783 }
784
785
786 ssize_t
ps_sendmsg(struct dhcpcd_ctx * ctx,int fd,uint16_t cmd,unsigned long flags,const struct msghdr * msg)787 ps_sendmsg(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
788 const struct msghdr *msg)
789 {
790 struct ps_msghdr psm = {
791 .ps_cmd = cmd,
792 .ps_flags = flags,
793 .ps_namelen = msg->msg_namelen,
794 .ps_controllen = (socklen_t)msg->msg_controllen,
795 };
796 size_t i;
797
798 for (i = 0; i < (size_t)msg->msg_iovlen; i++)
799 psm.ps_datalen += msg->msg_iov[i].iov_len;
800
801 #if 0 /* For debugging structure padding. */
802 logerrx("psa.family %lu %zu", offsetof(struct ps_addr, psa_family), sizeof(psm.ps_id.psi_addr.psa_family));
803 logerrx("psa.pad %lu %zu", offsetof(struct ps_addr, psa_pad), sizeof(psm.ps_id.psi_addr.psa_pad));
804 logerrx("psa.psa_u %lu %zu", offsetof(struct ps_addr, psa_u), sizeof(psm.ps_id.psi_addr.psa_u));
805 logerrx("psa %zu", sizeof(psm.ps_id.psi_addr));
806
807 logerrx("psi.addr %lu %zu", offsetof(struct ps_id, psi_addr), sizeof(psm.ps_id.psi_addr));
808 logerrx("psi.index %lu %zu", offsetof(struct ps_id, psi_ifindex), sizeof(psm.ps_id.psi_ifindex));
809 logerrx("psi.cmd %lu %zu", offsetof(struct ps_id, psi_cmd), sizeof(psm.ps_id.psi_cmd));
810 logerrx("psi.pad %lu %zu", offsetof(struct ps_id, psi_pad), sizeof(psm.ps_id.psi_pad));
811 logerrx("psi %zu", sizeof(struct ps_id));
812
813 logerrx("ps_cmd %lu", offsetof(struct ps_msghdr, ps_cmd));
814 logerrx("ps_pad %lu %zu", offsetof(struct ps_msghdr, ps_pad), sizeof(psm.ps_pad));
815 logerrx("ps_flags %lu %zu", offsetof(struct ps_msghdr, ps_flags), sizeof(psm.ps_flags));
816
817 logerrx("ps_id %lu %zu", offsetof(struct ps_msghdr, ps_id), sizeof(psm.ps_id));
818
819 logerrx("ps_namelen %lu %zu", offsetof(struct ps_msghdr, ps_namelen), sizeof(psm.ps_namelen));
820 logerrx("ps_controllen %lu %zu", offsetof(struct ps_msghdr, ps_controllen), sizeof(psm.ps_controllen));
821 logerrx("ps_pad2 %lu %zu", offsetof(struct ps_msghdr, ps_pad2), sizeof(psm.ps_pad2));
822 logerrx("ps_datalen %lu %zu", offsetof(struct ps_msghdr, ps_datalen), sizeof(psm.ps_datalen));
823 logerrx("psm %zu", sizeof(psm));
824 #endif
825
826 return ps_sendpsmmsg(ctx, fd, &psm, msg);
827 }
828
829 ssize_t
ps_sendcmd(struct dhcpcd_ctx * ctx,int fd,uint16_t cmd,unsigned long flags,const void * data,size_t len)830 ps_sendcmd(struct dhcpcd_ctx *ctx, int fd, uint16_t cmd, unsigned long flags,
831 const void *data, size_t len)
832 {
833 struct ps_msghdr psm = {
834 .ps_cmd = cmd,
835 .ps_flags = flags,
836 };
837 struct iovec iov[] = {
838 { .iov_base = UNCONST(data), .iov_len = len }
839 };
840 struct msghdr msg = {
841 .msg_iov = iov, .msg_iovlen = 1,
842 };
843
844 return ps_sendpsmmsg(ctx, fd, &psm, &msg);
845 }
846
847 static ssize_t
ps_sendcmdmsg(int fd,uint16_t cmd,const struct msghdr * msg)848 ps_sendcmdmsg(int fd, uint16_t cmd, const struct msghdr *msg)
849 {
850 struct ps_msghdr psm = { .ps_cmd = cmd };
851 uint8_t data[PS_BUFLEN], *p = data;
852 struct iovec iov[] = {
853 { .iov_base = &psm, .iov_len = sizeof(psm) },
854 { .iov_base = data, .iov_len = 0 },
855 };
856 size_t dl = sizeof(data);
857 socklen_t cmsg_padlen =
858 CALC_CMSG_PADLEN(msg->msg_controllen, msg->msg_namelen);
859
860 if (msg->msg_namelen != 0) {
861 if (msg->msg_namelen > dl)
862 goto nobufs;
863 psm.ps_namelen = msg->msg_namelen;
864 memcpy(p, msg->msg_name, msg->msg_namelen);
865 p += msg->msg_namelen;
866 dl -= msg->msg_namelen;
867 }
868
869 if (msg->msg_controllen != 0) {
870 if (msg->msg_controllen + cmsg_padlen > dl)
871 goto nobufs;
872 if (cmsg_padlen != 0) {
873 memset(p, 0, cmsg_padlen);
874 p += cmsg_padlen;
875 dl -= cmsg_padlen;
876 }
877 psm.ps_controllen = (socklen_t)msg->msg_controllen;
878 memcpy(p, msg->msg_control, msg->msg_controllen);
879 p += msg->msg_controllen;
880 dl -= msg->msg_controllen;
881 }
882
883 psm.ps_datalen = msg->msg_iov[0].iov_len;
884 if (psm.ps_datalen > dl)
885 goto nobufs;
886
887 iov[1].iov_len =
888 psm.ps_namelen + psm.ps_controllen + psm.ps_datalen + cmsg_padlen;
889 if (psm.ps_datalen != 0)
890 memcpy(p, msg->msg_iov[0].iov_base, psm.ps_datalen);
891 return writev(fd, iov, __arraycount(iov));
892
893 nobufs:
894 errno = ENOBUFS;
895 return -1;
896 }
897
898 ssize_t
ps_recvmsg(struct dhcpcd_ctx * ctx,int rfd,uint16_t cmd,int wfd)899 ps_recvmsg(struct dhcpcd_ctx *ctx, int rfd, uint16_t cmd, int wfd)
900 {
901 struct sockaddr_storage ss = { .ss_family = AF_UNSPEC };
902 uint8_t controlbuf[sizeof(struct sockaddr_storage)] = { 0 };
903 uint8_t databuf[64 * 1024];
904 struct iovec iov[] = {
905 { .iov_base = databuf, .iov_len = sizeof(databuf) }
906 };
907 struct msghdr msg = {
908 .msg_name = &ss, .msg_namelen = sizeof(ss),
909 .msg_control = controlbuf, .msg_controllen = sizeof(controlbuf),
910 .msg_iov = iov, .msg_iovlen = 1,
911 };
912
913 ssize_t len = recvmsg(rfd, &msg, 0);
914
915 if (len == -1)
916 logerr("%s: recvmsg", __func__);
917 if (len == -1 || len == 0) {
918 if (ctx->options & DHCPCD_FORKED &&
919 !(ctx->options & DHCPCD_PRIVSEPROOT))
920 eloop_exit(ctx->eloop,
921 len == 0 ? EXIT_SUCCESS : EXIT_FAILURE);
922 return len;
923 }
924
925 iov[0].iov_len = (size_t)len;
926 len = ps_sendcmdmsg(wfd, cmd, &msg);
927 if (len == -1) {
928 logerr("ps_sendcmdmsg");
929 if (ctx->options & DHCPCD_FORKED &&
930 !(ctx->options & DHCPCD_PRIVSEPROOT))
931 eloop_exit(ctx->eloop, EXIT_FAILURE);
932 }
933 return len;
934 }
935
936 ssize_t
ps_recvpsmsg(struct dhcpcd_ctx * ctx,int fd,ssize_t (* callback)(void *,struct ps_msghdr *,struct msghdr *),void * cbctx)937 ps_recvpsmsg(struct dhcpcd_ctx *ctx, int fd,
938 ssize_t (*callback)(void *, struct ps_msghdr *, struct msghdr *),
939 void *cbctx)
940 {
941 struct ps_msg psm;
942 ssize_t len;
943 size_t dlen;
944 struct iovec iov[1];
945 struct msghdr msg = { .msg_iov = iov, .msg_iovlen = 1 };
946 bool stop = false;
947
948 len = read(fd, &psm, sizeof(psm));
949 #ifdef PRIVSEP_DEBUG
950 logdebugx("%s: %zd", __func__, len);
951 #endif
952
953 if (len == -1 || len == 0)
954 stop = true;
955 else {
956 dlen = (size_t)len;
957 if (dlen < sizeof(psm.psm_hdr)) {
958 errno = EINVAL;
959 return -1;
960 }
961
962 if (psm.psm_hdr.ps_cmd == PS_STOP) {
963 stop = true;
964 len = 0;
965 }
966 }
967
968 if (stop) {
969 #ifdef PRIVSEP_DEBUG
970 logdebugx("process %d stopping", getpid());
971 #endif
972 ps_free(ctx);
973 #ifdef PLUGIN_DEV
974 dev_stop(ctx);
975 #endif
976 eloop_exit(ctx->eloop, len != -1 ? EXIT_SUCCESS : EXIT_FAILURE);
977 return len;
978 }
979 dlen -= sizeof(psm.psm_hdr);
980
981 if (ps_unrollmsg(&msg, &psm.psm_hdr, psm.psm_data, dlen) == -1)
982 return -1;
983
984 if (callback == NULL)
985 return 0;
986
987 errno = 0;
988 return callback(cbctx, &psm.psm_hdr, &msg);
989 }
990
991 struct ps_process *
ps_findprocess(struct dhcpcd_ctx * ctx,struct ps_id * psid)992 ps_findprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
993 {
994 struct ps_process *psp;
995
996 TAILQ_FOREACH(psp, &ctx->ps_processes, next) {
997 if (memcmp(&psp->psp_id, psid, sizeof(psp->psp_id)) == 0)
998 return psp;
999 }
1000 errno = ESRCH;
1001 return NULL;
1002 }
1003
1004 struct ps_process *
ps_newprocess(struct dhcpcd_ctx * ctx,struct ps_id * psid)1005 ps_newprocess(struct dhcpcd_ctx *ctx, struct ps_id *psid)
1006 {
1007 struct ps_process *psp;
1008
1009 psp = calloc(1, sizeof(*psp));
1010 if (psp == NULL)
1011 return NULL;
1012 psp->psp_ctx = ctx;
1013 memcpy(&psp->psp_id, psid, sizeof(psp->psp_id));
1014 psp->psp_work_fd = -1;
1015 TAILQ_INSERT_TAIL(&ctx->ps_processes, psp, next);
1016 return psp;
1017 }
1018
1019 void
ps_freeprocesses(struct dhcpcd_ctx * ctx,struct ps_process * notthis)1020 ps_freeprocesses(struct dhcpcd_ctx *ctx, struct ps_process *notthis)
1021 {
1022 struct ps_process *psp, *psn;
1023
1024 TAILQ_FOREACH_SAFE(psp, &ctx->ps_processes, next, psn) {
1025 if (psp == notthis)
1026 continue;
1027 ps_freeprocess(psp);
1028 }
1029 }
1030