]>
git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/priv.c
bf9fb550fb0dd634ce43e044056f3a8ea1f08bda
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
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.
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.
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. */
32 #include <sys/socket.h>
36 #include <sys/utsname.h>
37 #include <sys/ioctl.h>
38 #include <netinet/if_ether.h>
40 #ifdef HAVE_LINUX_CAPABILITIES
41 #include <sys/capability.h>
42 #include <sys/prctl.h>
45 #if defined HOST_OS_FREEBSD || HOST_OS_OSX || HOST_OS_DRAGONFLY
46 # include <net/if_dl.h>
48 #if defined HOST_OS_SOLARIS
49 # include <sys/sockio.h>
53 #ifdef HAVE_SYS_TYPES_H
54 # include <sys/types.h>
56 #ifdef HAVE_NETINET_IN_H
57 # include <netinet/in.h>
59 #ifdef HAVE_ARPA_NAMESER_H
60 # include <arpa/nameser.h> /* DNS HEADER struct */
69 /* Bionic has res_init() but it's not in any header */
70 #if defined HAVE_RES_INIT && defined __BIONIC__
75 static int monitored
= -1; /* Child */
83 enum priv_cmd cmd
= PRIV_PING
;
84 must_write(PRIV_UNPRIVILEGED
, &cmd
, sizeof(enum priv_cmd
));
86 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
87 log_debug("privsep", "monitor ready");
90 /* Proxy for ctl_cleanup */
92 priv_ctl_cleanup(const char *ctlname
)
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
);
100 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
103 /* Proxy for gethostname */
107 static char *buf
= NULL
;
109 enum priv_cmd cmd
= PRIV_GET_HOSTNAME
;
110 must_write(PRIV_UNPRIVILEGED
, &cmd
, sizeof(enum priv_cmd
));
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
);
122 priv_iface_init(int index
, char *iface
)
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
);
132 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
133 if (rc
!= 0) return -1;
134 return receive_fd(PRIV_UNPRIVILEGED
);
138 priv_iface_multicast(const char *name
, const u_int8_t
*mac
, int add
)
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));
147 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
152 priv_iface_description(const char *name
, const char *description
)
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
);
161 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
165 /* Proxy to set interface in promiscuous mode */
167 priv_iface_promisc(const char *ifname
)
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
);
174 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
179 priv_snmp_socket(struct sockaddr_un
*addr
)
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
));
186 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
189 return receive_fd(PRIV_UNPRIVILEGED
);
196 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(int));
206 must_read(PRIV_PRIVILEGED
, &len
, sizeof(int));
207 if ((ctlname
= (char*)malloc(len
+1)) == NULL
)
208 fatal("ctlname", NULL
);
210 must_read(PRIV_PRIVILEGED
, ctlname
, len
);
213 ctl_cleanup(ctlname
);
217 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(int));
224 struct addrinfo hints
= {
225 .ai_flags
= AI_CANONNAME
227 struct addrinfo
*res
;
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");
236 len
= strlen(un
.nodename
);
237 must_write(PRIV_PRIVILEGED
, &len
, sizeof(int));
238 must_write(PRIV_PRIVILEGED
, un
.nodename
, len
);
240 len
= strlen(res
->ai_canonname
);
241 must_write(PRIV_PRIVILEGED
, &len
, sizeof(int));
242 must_write(PRIV_PRIVILEGED
, res
->ai_canonname
, len
);
250 int rc
= -1, fd
= -1;
253 must_read(PRIV_PRIVILEGED
, &ifindex
, sizeof(ifindex
));
254 must_read(PRIV_PRIVILEGED
, &name
, sizeof(name
));
255 name
[sizeof(name
) - 1] = '\0';
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
);
265 asroot_iface_multicast()
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
;
279 dlp
->sdl_alen
= ETHER_ADDR_LEN
;
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
);
287 sap
->sa_family
= AF_UNSPEC
;
288 must_read(PRIV_PRIVILEGED
, sap
->sa_data
, ETHER_ADDR_LEN
);
290 #error Unsupported OS
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
)))
299 if (sock
!= -1) close(sock
);
300 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(rc
));
304 asroot_iface_description()
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
);
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
));
324 asroot_iface_promisc()
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
));
338 static struct sockaddr_un
*addr
= NULL
;
339 struct sockaddr_un bogus
;
342 addr
= (struct sockaddr_un
*)malloc(sizeof(struct sockaddr_un
));
343 must_read(PRIV_PRIVILEGED
, addr
, sizeof(struct sockaddr_un
));
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';
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));
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
));
363 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(int));
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
));
375 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(int));
379 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(int));
380 send_fd(PRIV_PRIVILEGED
, sock
);
384 struct dispatch_actions
{
386 void(*function
)(void);
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
},
394 {PRIV_OPEN
, asroot_open
},
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
},
404 /* Main loop, run as root */
406 priv_loop(int privileged
, int once
)
409 struct dispatch_actions
*a
;
411 #ifdef ENABLE_PRIVSEP
412 setproctitle("monitor.");
414 if (priv_seccomp_init(privileged
, monitored
) != 0)
415 fatal("privsep", "cannot continue without seccomp setup");
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
++) {
426 if (a
->function
== NULL
)
427 fatalx("privsep", "bogus message received");
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. */
437 #ifndef ENABLE_PRIVSEP
438 /* We have no remote process on the other side. Let's emulate it. */
444 #ifdef ENABLE_PRIVSEP
446 priv_exit_rc_status(int rc
, int status
) {
450 kill(monitored
, SIGTERM
);
451 /* we will receive a sigchld in the future */
454 /* child doesn't exist anymore, we consider this is an error to
459 /* Monitored child has terminated */
460 /* Mimic the exit state of the child */
461 if (WIFEXITED(status
)) {
463 _exit(WEXITSTATUS(status
));
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. */
471 /* Other cases, consider this as an error. */
482 rc
= waitpid(monitored
, &status
, WNOHANG
);
483 priv_exit_rc_status(rc
, status
);
486 /* If priv parent gets a TERM or HUP, pass it through to child instead */
488 sig_pass_to_chld(int sig
)
492 kill(monitored
, sig
);
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
503 int rc
= waitpid(monitored
, &status
, WNOHANG
);
505 while ((rc
= waitpid(-1, &status
, WNOHANG
)) > 0) {
506 if (rc
== monitored
) priv_exit_rc_status(rc
, status
);
510 priv_exit_rc_status(rc
, status
);
513 /* Create a directory recursively. */
514 static int mkdir_p(const char *pathname
, mode_t mode
)
516 char path
[PATH_MAX
+1], current
[PATH_MAX
+1];
519 if (strlcpy(path
, pathname
, sizeof(path
)) >= sizeof(path
)) {
520 errno
= ENAMETOOLONG
;
524 /* Use strtok which will provides non-empty tokens only. */
525 if (path
[0] == '/') current
[0] = '/';
526 tok
= strtok(path
, "/");
528 strcat(current
, tok
);
529 if (mkdir(current
, mode
) != 0 && errno
!= EEXIST
)
531 strcat(current
, "/");
532 tok
= strtok(NULL
, "/");
540 #define LOCALTIME "/etc/localtime"
542 priv_setup_chroot(const char *chrootdir
)
544 /* Create chroot if it does not exist */
545 if (mkdir_p(chrootdir
, 0755) == -1) {
546 fatal("privsep", "unable to create chroot directory");
549 /* Check if /etc/localtime exists in chroot or outside chroot */
551 int source
= -1, destination
= -1;
552 if (snprintf(path
, sizeof(path
),
553 "%s" LOCALTIME
, chrootdir
) >= sizeof(path
))
555 if ((source
= open(LOCALTIME
, O_RDONLY
)) == -1) {
558 log_warn("privsep", "cannot read " LOCALTIME
);
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",
572 path
[strlen(chrootdir
) + 4] = '/';
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) {
581 log_warn("privsep", "cannot create %s", path
);
587 while ((n
= read(source
, buffer
, sizeof(buffer
))) > 0) {
588 ssize_t nw
, left
= n
;
591 if ((nw
= write(destination
, p
, left
)) == -1) {
592 if (errno
== EINTR
) continue;
593 log_warn("privsep", "cannot write to %s", path
);
604 log_warn("privsep", "cannot read " LOCALTIME
);
607 log_info("privsep", LOCALTIME
" copied to chroot");
612 #else /* !ENABLE_PRIVSEP */
614 /* Reap any children. It should only be lldpcli since there is not monitored
620 while (waitpid(-1, &status
, WNOHANG
) > 0);
626 priv_drop(uid_t uid
, gid_t gid
)
630 log_debug("privsep", "dropping privileges");
631 #ifdef HAVE_SETRESGID
632 if (setresgid(gid
, gid
, gid
) == -1)
633 fatal("privsep", "setresgid() failed");
635 if (setregid(gid
, gid
) == -1)
636 fatal("privsep", "setregid() failed");
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");
644 if (setreuid(uid
, uid
) == -1)
645 fatal("privsep", "setreuid() failed");
650 priv_caps(uid_t uid
, gid_t gid
)
652 #ifdef HAVE_LINUX_CAPABILITIES
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"
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");
668 if (prctl(PR_SET_KEEPCAPS
, 1L, 0L, 0L, 0L) == -1)
669 fatal("privsep", "cannot keep capabilities");
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");
679 log_info("privsep", "no libcap support, running monitor as root");
684 priv_init(const char *chrootdir
, int ctl
, uid_t uid
, gid_t gid
)
689 /* Create socket pair */
690 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, pair
) < 0) {
692 "unable to create socket pair for privilege separation");
695 priv_unprivileged_fd(pair
[0]);
696 priv_privileged_fd(pair
[1]);
698 #ifdef ENABLE_PRIVSEP
699 /* Spawn off monitor */
700 if ((monitored
= fork()) < 0)
701 fatal("privsep", "unable to fork monitor");
704 /* We are in the children, drop privileges */
705 if (RUNNING_ON_VALGRIND
)
706 log_warnx("privsep", "running on valgrind, keep privileges");
708 priv_setup_chroot(chrootdir
);
709 if (chroot(chrootdir
) == -1)
710 fatal("privsep", "unable to chroot");
712 fatal("privsep", "unable to chdir");
719 /* We are in the monitor */
720 if (ctl
!= -1) close(ctl
);
722 if (atexit(priv_exit
) != 0)
723 fatal("privsep", "unable to set exit function");
727 /* Install signal handlers */
728 const struct sigaction pass_to_child
= {
729 .sa_handler
= sig_pass_to_chld
,
730 .sa_flags
= SA_RESTART
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
741 sigaction(SIGCHLD
, &child
, NULL
);
742 sig_chld(0); /* Reap already dead children */
743 priv_loop(pair
[1], 0);
747 const struct sigaction child
= {
748 .sa_handler
= sig_chld
,
749 .sa_flags
= SA_RESTART
751 sigaction(SIGCHLD
, &child
, NULL
);
752 sig_chld(0); /* Reap already dead children */
753 log_warnx("priv", "no privilege separation available");