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