]>
git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/priv.c
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. */
33 #include <sys/socket.h>
37 #include <sys/utsname.h>
38 #include <sys/ioctl.h>
39 #include <netinet/if_ether.h>
41 #ifdef HAVE_LINUX_CAPABILITIES
42 #include <sys/capability.h>
43 #include <sys/prctl.h>
46 #if defined HOST_OS_FREEBSD || HOST_OS_OSX || HOST_OS_DRAGONFLY
47 # include <net/if_dl.h>
49 #if defined HOST_OS_SOLARIS
50 # include <sys/sockio.h>
54 #ifdef HAVE_SYS_TYPES_H
55 # include <sys/types.h>
57 #ifdef HAVE_NETINET_IN_H
58 # include <netinet/in.h>
60 #ifdef HAVE_ARPA_NAMESER_H
61 # include <arpa/nameser.h> /* DNS HEADER struct */
70 /* Bionic has res_init() but it's not in any header */
71 #if defined HAVE_RES_INIT && defined __BIONIC__
76 static int monitored
= -1; /* Child */
84 enum priv_cmd cmd
= PRIV_PING
;
85 must_write(PRIV_UNPRIVILEGED
, &cmd
, sizeof(enum priv_cmd
));
87 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
88 log_debug("privsep", "monitor ready");
91 /* Proxy for ctl_cleanup */
93 priv_ctl_cleanup(const char *ctlname
)
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
);
101 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
104 /* Proxy for gethostname */
108 static char *buf
= NULL
;
110 enum priv_cmd cmd
= PRIV_GET_HOSTNAME
;
111 must_write(PRIV_UNPRIVILEGED
, &cmd
, sizeof(enum priv_cmd
));
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
);
123 priv_iface_init(int index
, char *iface
, int proto
)
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));
134 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
135 if (rc
!= 0) return -1;
136 return receive_fd(PRIV_UNPRIVILEGED
);
140 priv_iface_multicast(const char *name
, const u_int8_t
*mac
, int add
)
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));
149 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
154 priv_iface_description(const char *name
, const char *description
)
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
);
163 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
167 /* Proxy to set interface in promiscuous mode */
169 priv_iface_promisc(const char *ifname
)
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
);
176 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
181 priv_snmp_socket(struct sockaddr_un
*addr
)
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
));
188 must_read(PRIV_UNPRIVILEGED
, &rc
, sizeof(int));
191 return receive_fd(PRIV_UNPRIVILEGED
);
198 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(int));
208 must_read(PRIV_PRIVILEGED
, &len
, sizeof(int));
209 if ((ctlname
= (char*)malloc(len
+1)) == NULL
)
210 fatal("ctlname", NULL
);
212 must_read(PRIV_PRIVILEGED
, ctlname
, len
);
215 ctl_cleanup(ctlname
);
219 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(int));
226 struct addrinfo hints
= {
227 .ai_flags
= AI_CANONNAME
229 struct addrinfo
*res
;
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");
238 len
= strlen(un
.nodename
);
239 must_write(PRIV_PRIVILEGED
, &len
, sizeof(int));
240 must_write(PRIV_PRIVILEGED
, un
.nodename
, len
);
242 len
= strlen(res
->ai_canonname
);
243 must_write(PRIV_PRIVILEGED
, &len
, sizeof(int));
244 must_write(PRIV_PRIVILEGED
, res
->ai_canonname
, len
);
252 int rc
= -1, fd
= -1;
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
))
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
);
269 asroot_iface_multicast()
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
;
283 dlp
->sdl_alen
= ETHER_ADDR_LEN
;
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
);
291 sap
->sa_family
= AF_UNSPEC
;
292 must_read(PRIV_PRIVILEGED
, sap
->sa_data
, ETHER_ADDR_LEN
);
294 #error Unsupported OS
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
)))
303 if (sock
!= -1) close(sock
);
304 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(rc
));
308 asroot_iface_description()
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
);
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
));
328 asroot_iface_promisc()
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
));
342 static struct sockaddr_un
*addr
= NULL
;
343 struct sockaddr_un bogus
;
346 addr
= (struct sockaddr_un
*)malloc(sizeof(struct sockaddr_un
));
347 must_read(PRIV_PRIVILEGED
, addr
, sizeof(struct sockaddr_un
));
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';
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));
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
));
367 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(int));
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
));
379 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(int));
383 must_write(PRIV_PRIVILEGED
, &rc
, sizeof(int));
384 send_fd(PRIV_PRIVILEGED
, sock
);
388 struct dispatch_actions
{
390 void(*function
)(void);
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
},
398 {PRIV_OPEN
, asroot_open
},
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
},
408 /* Main loop, run as root */
410 priv_loop(int privileged
, int once
)
413 struct dispatch_actions
*a
;
415 #ifdef ENABLE_PRIVSEP
416 setproctitle("monitor.");
418 if (priv_seccomp_init(privileged
, monitored
) != 0)
419 fatal("privsep", "cannot continue without seccomp setup");
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
++) {
430 if (a
->function
== NULL
)
431 fatalx("privsep", "bogus message received");
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. */
441 #ifndef ENABLE_PRIVSEP
442 /* We have no remote process on the other side. Let's emulate it. */
448 #ifdef ENABLE_PRIVSEP
450 priv_exit_rc_status(int rc
, int status
) {
454 kill(monitored
, SIGTERM
);
455 /* we will receive a sigchld in the future */
458 /* child doesn't exist anymore, we consider this is an error to
463 /* Monitored child has terminated */
464 /* Mimic the exit state of the child */
465 if (WIFEXITED(status
)) {
467 _exit(WEXITSTATUS(status
));
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. */
475 /* Other cases, consider this as an error. */
486 rc
= waitpid(monitored
, &status
, WNOHANG
);
487 priv_exit_rc_status(rc
, status
);
490 /* If priv parent gets a TERM or HUP, pass it through to child instead */
492 sig_pass_to_chld(int sig
)
496 kill(monitored
, sig
);
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
507 int rc
= waitpid(monitored
, &status
, WNOHANG
);
509 while ((rc
= waitpid(-1, &status
, WNOHANG
)) > 0) {
510 if (rc
== monitored
) priv_exit_rc_status(rc
, status
);
514 priv_exit_rc_status(rc
, status
);
517 /* Create a directory recursively. */
518 static int mkdir_p(const char *pathname
, mode_t mode
)
520 char path
[PATH_MAX
+1], current
[PATH_MAX
+1] = {};
523 if (strlcpy(path
, pathname
, sizeof(path
)) >= sizeof(path
)) {
524 errno
= ENAMETOOLONG
;
528 /* Use strtok which will provides non-empty tokens only. */
529 if (path
[0] == '/') current
[0] = '/';
530 tok
= strtok(path
, "/");
532 strcat(current
, tok
);
533 if (mkdir(current
, mode
) != 0 && errno
!= EEXIST
)
535 strcat(current
, "/");
536 tok
= strtok(NULL
, "/");
544 #define LOCALTIME "/etc/localtime"
546 priv_setup_chroot(const char *chrootdir
)
548 /* Create chroot if it does not exist */
549 if (mkdir_p(chrootdir
, 0755) == -1) {
550 fatal("privsep", "unable to create chroot directory");
553 /* Check if /etc/localtime exists in chroot or outside chroot */
555 int source
= -1, destination
= -1;
556 if (snprintf(path
, sizeof(path
),
557 "%s" LOCALTIME
, chrootdir
) >= sizeof(path
))
559 if ((source
= open(LOCALTIME
, O_RDONLY
)) == -1) {
562 log_warn("privsep", "cannot read " LOCALTIME
);
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",
576 path
[strlen(chrootdir
) + 4] = '/';
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) {
585 log_warn("privsep", "cannot create %s", path
);
591 while ((n
= read(source
, buffer
, sizeof(buffer
))) > 0) {
592 ssize_t nw
, left
= n
;
595 if ((nw
= write(destination
, p
, left
)) == -1) {
596 if (errno
== EINTR
) continue;
597 log_warn("privsep", "cannot write to %s", path
);
608 log_warn("privsep", "cannot read " LOCALTIME
);
611 log_info("privsep", LOCALTIME
" copied to chroot");
616 #else /* !ENABLE_PRIVSEP */
618 /* Reap any children. It should only be lldpcli since there is not monitored
624 while (waitpid(-1, &status
, WNOHANG
) > 0);
630 priv_drop(uid_t uid
, gid_t gid
)
634 log_debug("privsep", "dropping privileges");
635 #ifdef HAVE_SETRESGID
636 if (setresgid(gid
, gid
, gid
) == -1)
637 fatal("privsep", "setresgid() failed");
639 if (setregid(gid
, gid
) == -1)
640 fatal("privsep", "setregid() failed");
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");
648 if (setreuid(uid
, uid
) == -1)
649 fatal("privsep", "setreuid() failed");
654 priv_caps(uid_t uid
, gid_t gid
)
656 #ifdef HAVE_LINUX_CAPABILITIES
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"
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");
672 if (prctl(PR_SET_KEEPCAPS
, 1L, 0L, 0L, 0L) == -1)
673 fatal("privsep", "cannot keep capabilities");
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");
683 log_info("privsep", "no libcap support, running monitor as root");
688 priv_init(const char *chrootdir
, int ctl
, uid_t uid
, gid_t gid
)
693 /* Create socket pair */
694 if (socketpair(AF_UNIX
, SOCK_STREAM
, PF_UNSPEC
, pair
) < 0) {
696 "unable to create socket pair for privilege separation");
699 priv_unprivileged_fd(pair
[0]);
700 priv_privileged_fd(pair
[1]);
702 #ifdef ENABLE_PRIVSEP
703 /* Spawn off monitor */
704 if ((monitored
= fork()) < 0)
705 fatal("privsep", "unable to fork monitor");
708 /* We are in the children, drop privileges */
709 if (RUNNING_ON_VALGRIND
)
710 log_warnx("privsep", "running on valgrind, keep privileges");
712 priv_setup_chroot(chrootdir
);
713 if (chroot(chrootdir
) == -1)
714 fatal("privsep", "unable to chroot");
716 fatal("privsep", "unable to chdir");
723 /* We are in the monitor */
724 if (ctl
!= -1) close(ctl
);
726 if (atexit(priv_exit
) != 0)
727 fatal("privsep", "unable to set exit function");
731 /* Install signal handlers */
732 const struct sigaction pass_to_child
= {
733 .sa_handler
= sig_pass_to_chld
,
734 .sa_flags
= SA_RESTART
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
745 sigaction(SIGCHLD
, &child
, NULL
);
746 sig_chld(0); /* Reap already dead children */
747 priv_loop(pair
[1], 0);
751 const struct sigaction child
= {
752 .sa_handler
= sig_chld
,
753 .sa_flags
= SA_RESTART
755 sigaction(SIGCHLD
, &child
, NULL
);
756 sig_chld(0); /* Reap already dead children */
757 log_warnx("priv", "no privilege separation available");