]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/priv.c
bf9fb550fb0dd634ce43e044056f3a8ea1f08bda
[thirdparty/lldpd.git] / src / daemon / priv.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 *
5 * Permission to use, copy, modify, and/or distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
8 *
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16 */
17
18 /* This file contains code for privilege separation. When an error arises in
19 * monitor (which is running as root), it just stops instead of trying to
20 * recover. This module also contains proxies to privileged operations. In this
21 * case, error can be non fatal. */
22
23 #include "lldpd.h"
24 #include "trace.h"
25
26 #include <stdio.h>
27 #include <unistd.h>
28 #include <signal.h>
29 #include <errno.h>
30 #include <sys/wait.h>
31 #include <sys/stat.h>
32 #include <sys/socket.h>
33 #include <sys/un.h>
34 #include <fcntl.h>
35 #include <grp.h>
36 #include <sys/utsname.h>
37 #include <sys/ioctl.h>
38 #include <netinet/if_ether.h>
39
40 #ifdef HAVE_LINUX_CAPABILITIES
41 #include <sys/capability.h>
42 #include <sys/prctl.h>
43 #endif
44
45 #if defined HOST_OS_FREEBSD || HOST_OS_OSX || HOST_OS_DRAGONFLY
46 # include <net/if_dl.h>
47 #endif
48 #if defined HOST_OS_SOLARIS
49 # include <sys/sockio.h>
50 #endif
51
52 /* Use resolv.h */
53 #ifdef HAVE_SYS_TYPES_H
54 # include <sys/types.h>
55 #endif
56 #ifdef HAVE_NETINET_IN_H
57 # include <netinet/in.h>
58 #endif
59 #ifdef HAVE_ARPA_NAMESER_H
60 # include <arpa/nameser.h> /* DNS HEADER struct */
61 #endif
62 #ifdef HAVE_NETDB_H
63 # include <netdb.h>
64 #endif
65 #ifdef HAVE_RESOLV_H
66 # include <resolv.h>
67 #endif
68
69 /* Bionic has res_init() but it's not in any header */
70 #if defined HAVE_RES_INIT && defined __BIONIC__
71 int res_init (void);
72 #endif
73
74 #ifdef ENABLE_PRIVSEP
75 static int monitored = -1; /* Child */
76 #endif
77
78 /* Proxies */
79 static void
80 priv_ping()
81 {
82 int rc;
83 enum priv_cmd cmd = PRIV_PING;
84 must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd));
85 priv_wait();
86 must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int));
87 log_debug("privsep", "monitor ready");
88 }
89
90 /* Proxy for ctl_cleanup */
91 void
92 priv_ctl_cleanup(const char *ctlname)
93 {
94 int rc, len = strlen(ctlname);
95 enum priv_cmd cmd = PRIV_DELETE_CTL_SOCKET;
96 must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd));
97 must_write(PRIV_UNPRIVILEGED, &len, sizeof(int));
98 must_write(PRIV_UNPRIVILEGED, ctlname, len);
99 priv_wait();
100 must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int));
101 }
102
103 /* Proxy for gethostname */
104 char *
105 priv_gethostname()
106 {
107 static char *buf = NULL;
108 int rc;
109 enum priv_cmd cmd = PRIV_GET_HOSTNAME;
110 must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd));
111 priv_wait();
112 must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int));
113 if ((buf = (char*)realloc(buf, rc+1)) == NULL)
114 fatal("privsep", NULL);
115 must_read(PRIV_UNPRIVILEGED, buf, rc);
116 buf[rc] = '\0';
117 return buf;
118 }
119
120
121 int
122 priv_iface_init(int index, char *iface)
123 {
124 int rc;
125 char dev[IFNAMSIZ] = {};
126 enum priv_cmd cmd = PRIV_IFACE_INIT;
127 must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd));
128 must_write(PRIV_UNPRIVILEGED, &index, sizeof(int));
129 strlcpy(dev, iface, IFNAMSIZ);
130 must_write(PRIV_UNPRIVILEGED, dev, IFNAMSIZ);
131 priv_wait();
132 must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int));
133 if (rc != 0) return -1;
134 return receive_fd(PRIV_UNPRIVILEGED);
135 }
136
137 int
138 priv_iface_multicast(const char *name, const u_int8_t *mac, int add)
139 {
140 int rc;
141 enum priv_cmd cmd = PRIV_IFACE_MULTICAST;
142 must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd));
143 must_write(PRIV_UNPRIVILEGED, name, IFNAMSIZ);
144 must_write(PRIV_UNPRIVILEGED, mac, ETHER_ADDR_LEN);
145 must_write(PRIV_UNPRIVILEGED, &add, sizeof(int));
146 priv_wait();
147 must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int));
148 return rc;
149 }
150
151 int
152 priv_iface_description(const char *name, const char *description)
153 {
154 int rc, len = strlen(description);
155 enum priv_cmd cmd = PRIV_IFACE_DESCRIPTION;
156 must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd));
157 must_write(PRIV_UNPRIVILEGED, name, IFNAMSIZ);
158 must_write(PRIV_UNPRIVILEGED, &len, sizeof(int));
159 must_write(PRIV_UNPRIVILEGED, description, len);
160 priv_wait();
161 must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int));
162 return rc;
163 }
164
165 /* Proxy to set interface in promiscuous mode */
166 int
167 priv_iface_promisc(const char *ifname)
168 {
169 int rc;
170 enum priv_cmd cmd = PRIV_IFACE_PROMISC;
171 must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd));
172 must_write(PRIV_UNPRIVILEGED, ifname, IFNAMSIZ);
173 priv_wait();
174 must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int));
175 return rc;
176 }
177
178 int
179 priv_snmp_socket(struct sockaddr_un *addr)
180 {
181 int rc;
182 enum priv_cmd cmd = PRIV_SNMP_SOCKET;
183 must_write(PRIV_UNPRIVILEGED, &cmd, sizeof(enum priv_cmd));
184 must_write(PRIV_UNPRIVILEGED, addr, sizeof(struct sockaddr_un));
185 priv_wait();
186 must_read(PRIV_UNPRIVILEGED, &rc, sizeof(int));
187 if (rc < 0)
188 return rc;
189 return receive_fd(PRIV_UNPRIVILEGED);
190 }
191
192 static void
193 asroot_ping()
194 {
195 int rc = 1;
196 must_write(PRIV_PRIVILEGED, &rc, sizeof(int));
197 }
198
199 static void
200 asroot_ctl_cleanup()
201 {
202 int len;
203 char *ctlname;
204 int rc = 0;
205
206 must_read(PRIV_PRIVILEGED, &len, sizeof(int));
207 if ((ctlname = (char*)malloc(len+1)) == NULL)
208 fatal("ctlname", NULL);
209
210 must_read(PRIV_PRIVILEGED, ctlname, len);
211 ctlname[len] = 0;
212
213 ctl_cleanup(ctlname);
214 free(ctlname);
215
216 /* Ack */
217 must_write(PRIV_PRIVILEGED, &rc, sizeof(int));
218 }
219
220 static void
221 asroot_gethostname()
222 {
223 struct utsname un;
224 struct addrinfo hints = {
225 .ai_flags = AI_CANONNAME
226 };
227 struct addrinfo *res;
228 int len;
229 if (uname(&un) < 0)
230 fatal("privsep", "failed to get system information");
231 if (getaddrinfo(un.nodename, NULL, &hints, &res) != 0) {
232 log_info("privsep", "unable to get system name");
233 #ifdef HAVE_RES_INIT
234 res_init();
235 #endif
236 len = strlen(un.nodename);
237 must_write(PRIV_PRIVILEGED, &len, sizeof(int));
238 must_write(PRIV_PRIVILEGED, un.nodename, len);
239 } else {
240 len = strlen(res->ai_canonname);
241 must_write(PRIV_PRIVILEGED, &len, sizeof(int));
242 must_write(PRIV_PRIVILEGED, res->ai_canonname, len);
243 freeaddrinfo(res);
244 }
245 }
246
247 static void
248 asroot_iface_init()
249 {
250 int rc = -1, fd = -1;
251 int ifindex;
252 char name[IFNAMSIZ];
253 must_read(PRIV_PRIVILEGED, &ifindex, sizeof(ifindex));
254 must_read(PRIV_PRIVILEGED, &name, sizeof(name));
255 name[sizeof(name) - 1] = '\0';
256
257 TRACE(LLDPD_PRIV_INTERFACE_INIT(name));
258 rc = asroot_iface_init_os(ifindex, name, &fd);
259 must_write(PRIV_PRIVILEGED, &rc, sizeof(rc));
260 if (rc == 0 && fd >=0) send_fd(PRIV_PRIVILEGED, fd);
261 if (fd >= 0) close(fd);
262 }
263
264 static void
265 asroot_iface_multicast()
266 {
267 int sock = -1, add, rc = 0;
268 struct ifreq ifr = { .ifr_name = {} };
269 must_read(PRIV_PRIVILEGED, ifr.ifr_name, IFNAMSIZ);
270 #if defined HOST_OS_LINUX
271 must_read(PRIV_PRIVILEGED, ifr.ifr_hwaddr.sa_data, ETHER_ADDR_LEN);
272 #elif defined HOST_OS_FREEBSD || defined HOST_OS_OSX || defined HOST_OS_DRAGONFLY
273 /* Black magic from mtest.c */
274 struct sockaddr_dl *dlp = ALIGNED_CAST(struct sockaddr_dl *, &ifr.ifr_addr);
275 dlp->sdl_len = sizeof(struct sockaddr_dl);
276 dlp->sdl_family = AF_LINK;
277 dlp->sdl_index = 0;
278 dlp->sdl_nlen = 0;
279 dlp->sdl_alen = ETHER_ADDR_LEN;
280 dlp->sdl_slen = 0;
281 must_read(PRIV_PRIVILEGED, LLADDR(dlp), ETHER_ADDR_LEN);
282 #elif defined HOST_OS_OPENBSD || defined HOST_OS_NETBSD || defined HOST_OS_SOLARIS
283 struct sockaddr *sap = (struct sockaddr *)&ifr.ifr_addr;
284 #if ! defined HOST_OS_SOLARIS
285 sap->sa_len = sizeof(struct sockaddr);
286 #endif
287 sap->sa_family = AF_UNSPEC;
288 must_read(PRIV_PRIVILEGED, sap->sa_data, ETHER_ADDR_LEN);
289 #else
290 #error Unsupported OS
291 #endif
292
293 must_read(PRIV_PRIVILEGED, &add, sizeof(int));
294 if (((sock = socket(AF_INET, SOCK_DGRAM, 0)) == -1) ||
295 ((ioctl(sock, (add)?SIOCADDMULTI:SIOCDELMULTI,
296 &ifr) < 0) && (errno != EADDRINUSE)))
297 rc = errno;
298
299 if (sock != -1) close(sock);
300 must_write(PRIV_PRIVILEGED, &rc, sizeof(rc));
301 }
302
303 static void
304 asroot_iface_description()
305 {
306 char name[IFNAMSIZ];
307 char *description;
308 int len, rc;
309 must_read(PRIV_PRIVILEGED, &name, sizeof(name));
310 name[sizeof(name) - 1] = '\0';
311 must_read(PRIV_PRIVILEGED, &len, sizeof(int));
312 if ((description = (char*)malloc(len+1)) == NULL)
313 fatal("description", NULL);
314
315 must_read(PRIV_PRIVILEGED, description, len);
316 description[len] = 0;
317 TRACE(LLDPD_PRIV_INTERFACE_DESCRIPTION(name, description));
318 rc = asroot_iface_description_os(name, description);
319 must_write(PRIV_PRIVILEGED, &rc, sizeof(rc));
320 free(description);
321 }
322
323 static void
324 asroot_iface_promisc()
325 {
326 char name[IFNAMSIZ];
327 int rc;
328 must_read(PRIV_PRIVILEGED, &name, sizeof(name));
329 name[sizeof(name) - 1] = '\0';
330 rc = asroot_iface_promisc_os(name);
331 must_write(PRIV_PRIVILEGED, &rc, sizeof(rc));
332 }
333
334 static void
335 asroot_snmp_socket()
336 {
337 int sock, rc;
338 static struct sockaddr_un *addr = NULL;
339 struct sockaddr_un bogus;
340
341 if (!addr) {
342 addr = (struct sockaddr_un *)malloc(sizeof(struct sockaddr_un));
343 must_read(PRIV_PRIVILEGED, addr, sizeof(struct sockaddr_un));
344 } else
345 /* We have already been asked to connect to a socket. We will
346 * connect to the same socket. */
347 must_read(PRIV_PRIVILEGED, &bogus, sizeof(struct sockaddr_un));
348 if (addr->sun_family != AF_UNIX)
349 fatal("privsep", "someone is trying to trick me");
350 addr->sun_path[sizeof(addr->sun_path)-1] = '\0';
351
352 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
353 log_warn("privsep", "cannot open socket");
354 must_write(PRIV_PRIVILEGED, &sock, sizeof(int));
355 return;
356 }
357 if ((rc = connect(sock, (struct sockaddr *) addr,
358 sizeof(struct sockaddr_un))) != 0) {
359 log_info("privsep", "cannot connect to %s: %s",
360 addr->sun_path, strerror(errno));
361 close(sock);
362 rc = -1;
363 must_write(PRIV_PRIVILEGED, &rc, sizeof(int));
364 return;
365 }
366
367 int flags;
368 if ((flags = fcntl(sock, F_GETFL, NULL)) < 0 ||
369 fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
370 log_warn("privsep", "cannot set sock %s to non-block : %s",
371 addr->sun_path, strerror(errno));
372
373 close(sock);
374 rc = -1;
375 must_write(PRIV_PRIVILEGED, &rc, sizeof(int));
376 return;
377 }
378
379 must_write(PRIV_PRIVILEGED, &rc, sizeof(int));
380 send_fd(PRIV_PRIVILEGED, sock);
381 close(sock);
382 }
383
384 struct dispatch_actions {
385 enum priv_cmd msg;
386 void(*function)(void);
387 };
388
389 static struct dispatch_actions actions[] = {
390 {PRIV_PING, asroot_ping},
391 {PRIV_DELETE_CTL_SOCKET, asroot_ctl_cleanup},
392 {PRIV_GET_HOSTNAME, asroot_gethostname},
393 #ifdef HOST_OS_LINUX
394 {PRIV_OPEN, asroot_open},
395 #endif
396 {PRIV_IFACE_INIT, asroot_iface_init},
397 {PRIV_IFACE_MULTICAST, asroot_iface_multicast},
398 {PRIV_IFACE_DESCRIPTION, asroot_iface_description},
399 {PRIV_IFACE_PROMISC, asroot_iface_promisc},
400 {PRIV_SNMP_SOCKET, asroot_snmp_socket},
401 {-1, NULL}
402 };
403
404 /* Main loop, run as root */
405 static void
406 priv_loop(int privileged, int once)
407 {
408 enum priv_cmd cmd;
409 struct dispatch_actions *a;
410
411 #ifdef ENABLE_PRIVSEP
412 setproctitle("monitor.");
413 #ifdef USE_SECCOMP
414 if (priv_seccomp_init(privileged, monitored) != 0)
415 fatal("privsep", "cannot continue without seccomp setup");
416 #endif
417 #endif
418 while (!may_read(PRIV_PRIVILEGED, &cmd, sizeof(enum priv_cmd))) {
419 log_debug("privsep", "received command %d", cmd);
420 for (a = actions; a->function != NULL; a++) {
421 if (cmd == a->msg) {
422 a->function();
423 break;
424 }
425 }
426 if (a->function == NULL)
427 fatalx("privsep", "bogus message received");
428 if (once) break;
429 }
430 }
431
432 /* This function is a NOOP when privilege separation is enabled. In
433 * the other case, it should be called when we wait an action from the
434 * privileged side. */
435 void
436 priv_wait() {
437 #ifndef ENABLE_PRIVSEP
438 /* We have no remote process on the other side. Let's emulate it. */
439 priv_loop(0, 1);
440 #endif
441 }
442
443
444 #ifdef ENABLE_PRIVSEP
445 static void
446 priv_exit_rc_status(int rc, int status) {
447 switch (rc) {
448 case 0:
449 /* kill child */
450 kill(monitored, SIGTERM);
451 /* we will receive a sigchld in the future */
452 return;
453 case -1:
454 /* child doesn't exist anymore, we consider this is an error to
455 * be here */
456 _exit(1);
457 break;
458 default:
459 /* Monitored child has terminated */
460 /* Mimic the exit state of the child */
461 if (WIFEXITED(status)) {
462 /* Normal exit */
463 _exit(WEXITSTATUS(status));
464 }
465 if (WIFSIGNALED(status)) {
466 /* Terminated with signal */
467 signal(WTERMSIG(status), SIG_DFL);
468 raise(WTERMSIG(status));
469 _exit(1); /* We consider that not being killed is an error. */
470 }
471 /* Other cases, consider this as an error. */
472 _exit(1);
473 break;
474 }
475 }
476
477 static void
478 priv_exit()
479 {
480 int status;
481 int rc;
482 rc = waitpid(monitored, &status, WNOHANG);
483 priv_exit_rc_status(rc, status);
484 }
485
486 /* If priv parent gets a TERM or HUP, pass it through to child instead */
487 static void
488 sig_pass_to_chld(int sig)
489 {
490 int oerrno = errno;
491 if (monitored != -1)
492 kill(monitored, sig);
493 errno = oerrno;
494 }
495
496 /* If priv parent gets a SIGCHLD, it will exit if this is the monitored
497 * process. Other processes (including lldpcli)) are just reaped without
498 * consequences. */
499 static void
500 sig_chld(int sig)
501 {
502 int status;
503 int rc = waitpid(monitored, &status, WNOHANG);
504 if (rc == 0) {
505 while ((rc = waitpid(-1, &status, WNOHANG)) > 0) {
506 if (rc == monitored) priv_exit_rc_status(rc, status);
507 }
508 return;
509 }
510 priv_exit_rc_status(rc, status);
511 }
512
513 /* Create a directory recursively. */
514 static int mkdir_p(const char *pathname, mode_t mode)
515 {
516 char path[PATH_MAX+1], current[PATH_MAX+1];
517 char *tok;
518
519 if (strlcpy(path, pathname, sizeof(path)) >= sizeof(path)) {
520 errno = ENAMETOOLONG;
521 return -1;
522 }
523
524 /* Use strtok which will provides non-empty tokens only. */
525 if (path[0] == '/') current[0] = '/';
526 tok = strtok(path, "/");
527 while (tok) {
528 strcat(current, tok);
529 if (mkdir(current, mode) != 0 && errno != EEXIST)
530 return -1;
531 strcat(current, "/");
532 tok = strtok(NULL, "/");
533 }
534
535 errno = 0;
536 return 0;
537 }
538
539 /* Initialization */
540 #define LOCALTIME "/etc/localtime"
541 static void
542 priv_setup_chroot(const char *chrootdir)
543 {
544 /* Create chroot if it does not exist */
545 if (mkdir_p(chrootdir, 0755) == -1) {
546 fatal("privsep", "unable to create chroot directory");
547 }
548
549 /* Check if /etc/localtime exists in chroot or outside chroot */
550 char path[1024];
551 int source = -1, destination = -1;
552 if (snprintf(path, sizeof(path),
553 "%s" LOCALTIME, chrootdir) >= sizeof(path))
554 return;
555 if ((source = open(LOCALTIME, O_RDONLY)) == -1) {
556 if (errno == ENOENT)
557 return;
558 log_warn("privsep", "cannot read " LOCALTIME);
559 return;
560 }
561
562 /* Prepare copy of /etc/localtime */
563 path[strlen(chrootdir) + 4] = '\0';
564 if (mkdir(path, 0755) == -1) {
565 if (errno != EEXIST) {
566 log_warn("privsep", "unable to create %s directory",
567 path);
568 close(source);
569 return;
570 }
571 }
572 path[strlen(chrootdir) + 4] = '/';
573
574 /* Do copy */
575 char buffer[1024];
576 ssize_t n;
577 mode_t old = umask(S_IWGRP | S_IWOTH);
578 if ((destination = open(path,
579 O_WRONLY | O_CREAT | O_TRUNC | O_EXCL, 0666)) == -1) {
580 if (errno != EEXIST)
581 log_warn("privsep", "cannot create %s", path);
582 close(source);
583 umask(old);
584 return;
585 }
586 umask(old);
587 while ((n = read(source, buffer, sizeof(buffer))) > 0) {
588 ssize_t nw, left = n;
589 char *p = buffer;
590 while (left > 0) {
591 if ((nw = write(destination, p, left)) == -1) {
592 if (errno == EINTR) continue;
593 log_warn("privsep", "cannot write to %s", path);
594 close(source);
595 close(destination);
596 unlink(path);
597 return;
598 }
599 left -= nw;
600 p += nw;
601 }
602 }
603 if (n == -1) {
604 log_warn("privsep", "cannot read " LOCALTIME);
605 unlink(path);
606 } else {
607 log_info("privsep", LOCALTIME " copied to chroot");
608 }
609 close(source);
610 close(destination);
611 }
612 #else /* !ENABLE_PRIVSEP */
613
614 /* Reap any children. It should only be lldpcli since there is not monitored
615 * process. */
616 static void
617 sig_chld(int sig)
618 {
619 int status = 0;
620 while (waitpid(-1, &status, WNOHANG) > 0);
621 }
622
623 #endif
624
625 void
626 priv_drop(uid_t uid, gid_t gid)
627 {
628 gid_t gidset[1];
629 gidset[0] = gid;
630 log_debug("privsep", "dropping privileges");
631 #ifdef HAVE_SETRESGID
632 if (setresgid(gid, gid, gid) == -1)
633 fatal("privsep", "setresgid() failed");
634 #else
635 if (setregid(gid, gid) == -1)
636 fatal("privsep", "setregid() failed");
637 #endif
638 if (setgroups(1, gidset) == -1)
639 fatal("privsep", "setgroups() failed");
640 #ifdef HAVE_SETRESUID
641 if (setresuid(uid, uid, uid) == -1)
642 fatal("privsep", "setresuid() failed");
643 #else
644 if (setreuid(uid, uid) == -1)
645 fatal("privsep", "setreuid() failed");
646 #endif
647 }
648
649 void
650 priv_caps(uid_t uid, gid_t gid)
651 {
652 #ifdef HAVE_LINUX_CAPABILITIES
653 cap_t caps;
654 const char *caps_strings[2] = {
655 "cap_dac_override,cap_net_raw,cap_net_admin,cap_setuid,cap_setgid=pe",
656 "cap_dac_override,cap_net_raw,cap_net_admin=pe"
657 };
658 log_debug("privsep", "getting CAP_NET_RAW/ADMIN and CAP_DAC_OVERRIDE privilege");
659 if (!(caps = cap_from_text(caps_strings[0])))
660 fatal("privsep", "unable to convert caps");
661 if (cap_set_proc(caps) == -1) {
662 log_warn("privsep", "unable to drop privileges, monitor running as root");
663 cap_free(caps);
664 return;
665 }
666 cap_free(caps);
667
668 if (prctl(PR_SET_KEEPCAPS, 1L, 0L, 0L, 0L) == -1)
669 fatal("privsep", "cannot keep capabilities");
670 priv_drop(uid, gid);
671
672 log_debug("privsep", "dropping extra capabilities");
673 if (!(caps = cap_from_text(caps_strings[1])))
674 fatal("privsep", "unable to convert caps");
675 if (cap_set_proc(caps) == -1)
676 fatal("privsep", "unable to drop extra privileges");
677 cap_free(caps);
678 #else
679 log_info("privsep", "no libcap support, running monitor as root");
680 #endif
681 }
682
683 void
684 priv_init(const char *chrootdir, int ctl, uid_t uid, gid_t gid)
685 {
686
687 int pair[2];
688
689 /* Create socket pair */
690 if (socketpair(AF_UNIX, SOCK_STREAM, PF_UNSPEC, pair) < 0) {
691 fatal("privsep",
692 "unable to create socket pair for privilege separation");
693 }
694
695 priv_unprivileged_fd(pair[0]);
696 priv_privileged_fd(pair[1]);
697
698 #ifdef ENABLE_PRIVSEP
699 /* Spawn off monitor */
700 if ((monitored = fork()) < 0)
701 fatal("privsep", "unable to fork monitor");
702 switch (monitored) {
703 case 0:
704 /* We are in the children, drop privileges */
705 if (RUNNING_ON_VALGRIND)
706 log_warnx("privsep", "running on valgrind, keep privileges");
707 else {
708 priv_setup_chroot(chrootdir);
709 if (chroot(chrootdir) == -1)
710 fatal("privsep", "unable to chroot");
711 if (chdir("/") != 0)
712 fatal("privsep", "unable to chdir");
713 priv_drop(uid, gid);
714 }
715 close(pair[1]);
716 priv_ping();
717 break;
718 default:
719 /* We are in the monitor */
720 if (ctl != -1) close(ctl);
721 close(pair[0]);
722 if (atexit(priv_exit) != 0)
723 fatal("privsep", "unable to set exit function");
724
725 priv_caps(uid, gid);
726
727 /* Install signal handlers */
728 const struct sigaction pass_to_child = {
729 .sa_handler = sig_pass_to_chld,
730 .sa_flags = SA_RESTART
731 };
732 sigaction(SIGALRM, &pass_to_child, NULL);
733 sigaction(SIGTERM, &pass_to_child, NULL);
734 sigaction(SIGHUP, &pass_to_child, NULL);
735 sigaction(SIGINT, &pass_to_child, NULL);
736 sigaction(SIGQUIT, &pass_to_child, NULL);
737 const struct sigaction child = {
738 .sa_handler = sig_chld,
739 .sa_flags = SA_RESTART
740 };
741 sigaction(SIGCHLD, &child, NULL);
742 sig_chld(0); /* Reap already dead children */
743 priv_loop(pair[1], 0);
744 exit(0);
745 }
746 #else
747 const struct sigaction child = {
748 .sa_handler = sig_chld,
749 .sa_flags = SA_RESTART
750 };
751 sigaction(SIGCHLD, &child, NULL);
752 sig_chld(0); /* Reap already dead children */
753 log_warnx("priv", "no privilege separation available");
754 priv_ping();
755 #endif
756 }