]>
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. */
30 #include <sys/socket.h>
35 #include <sys/utsname.h>
36 #include <sys/ioctl.h>
38 #include <linux/sockios.h>
39 #include <linux/if_packet.h>
42 #ifdef HAVE_SYS_TYPES_H
43 # include <sys/types.h>
45 #ifdef HAVE_NETINET_IN_H
46 # include <netinet/in.h>
48 #ifdef HAVE_ARPA_NAMESER_H
49 # include <arpa/nameser.h> /* DNS HEADER struct */
58 PRIV_DELETE_CTL_SOCKET
,
67 static int may_read(int, void *, size_t);
68 static void must_read(int, void *, size_t);
69 static void must_write(int, const void *, size_t);
71 static int remote
; /* Other side */
72 static int monitored
= -1; /* Child */
82 must_write(remote
, &cmd
, sizeof(int));
83 must_read(remote
, &rc
, sizeof(int));
84 log_debug("privsep", "monitor ready");
87 /* Proxy for ctl_cleanup */
92 cmd
= PRIV_DELETE_CTL_SOCKET
;
93 must_write(remote
, &cmd
, sizeof(int));
94 must_read(remote
, &rc
, sizeof(int));
97 /* Proxy for gethostbyname */
102 static char *buf
= NULL
;
103 cmd
= PRIV_GET_HOSTNAME
;
104 must_write(remote
, &cmd
, sizeof(int));
105 must_read(remote
, &rc
, sizeof(int));
106 if ((buf
= (char*)realloc(buf
, rc
+1)) == NULL
)
107 fatal("privsep", NULL
);
108 must_read(remote
, buf
, rc
+1);
114 priv_open(char *file
)
118 must_write(remote
, &cmd
, sizeof(int));
120 must_write(remote
, &len
, sizeof(int));
121 must_write(remote
, file
, len
+ 1);
122 must_read(remote
, &rc
, sizeof(int));
125 return receive_fd(remote
);
128 /* Proxy for ethtool ioctl */
130 priv_ethtool(char *ifname
, struct ethtool_cmd
*ethc
)
134 must_write(remote
, &cmd
, sizeof(int));
135 len
= strlen(ifname
);
136 must_write(remote
, &len
, sizeof(int));
137 must_write(remote
, ifname
, len
+ 1);
138 must_read(remote
, &rc
, sizeof(int));
141 must_read(remote
, ethc
, sizeof(struct ethtool_cmd
));
146 priv_iface_init(const char *name
)
149 cmd
= PRIV_IFACE_INIT
;
150 must_write(remote
, &cmd
, sizeof(int));
151 must_write(remote
, name
, IFNAMSIZ
);
152 must_read(remote
, &rc
, sizeof(int));
153 if (rc
!= 0) return -1;
154 return receive_fd(remote
);
158 priv_iface_multicast(const char *name
, u_int8_t
*mac
, int add
)
161 cmd
= PRIV_IFACE_MULTICAST
;
162 must_write(remote
, &cmd
, sizeof(int));
163 must_write(remote
, name
, IFNAMSIZ
);
164 must_write(remote
, mac
, ETH_ALEN
);
165 must_write(remote
, &add
, sizeof(int));
166 must_read(remote
, &rc
, sizeof(int));
171 priv_snmp_socket(struct sockaddr_un
*addr
)
174 cmd
= PRIV_SNMP_SOCKET
;
175 must_write(remote
, &cmd
, sizeof(int));
176 must_write(remote
, addr
, sizeof(struct sockaddr_un
));
177 must_read(remote
, &rc
, sizeof(int));
180 return receive_fd(remote
);
187 must_write(remote
, &rc
, sizeof(int));
194 ctl_cleanup(LLDPD_CTL_SOCKET
);
197 must_write(remote
, &rc
, sizeof(int));
201 asroot_gethostbyname()
207 fatal("privsep", "failed to get system information");
208 if ((hp
= gethostbyname(un
.nodename
)) == NULL
) {
209 log_info("privsep", "unable to get system name");
211 len
= strlen(un
.nodename
);
212 must_write(remote
, &len
, sizeof(int));
213 must_write(remote
, un
.nodename
, len
+ 1);
215 len
= strlen(hp
->h_name
);
216 must_write(remote
, &len
, sizeof(int));
217 must_write(remote
, hp
->h_name
, len
+ 1);
224 const char* authorized
[] = {
225 "/proc/sys/net/ipv4/ip_forward",
226 "/proc/net/bonding/[^.][^/]*",
227 "/proc/self/net/bonding/[^.][^/]*",
228 SYSFS_CLASS_NET
"[^.][^/]*/brforward",
229 SYSFS_CLASS_NET
"[^.][^/]*/brport",
230 SYSFS_CLASS_NET
"[^.][^/]*/brif/[^.][^/]*/port_no",
231 SYSFS_CLASS_NET
"[^.][^/]*/ifalias",
232 SYSFS_CLASS_DMI
"product_version",
233 SYSFS_CLASS_DMI
"product_serial",
234 SYSFS_CLASS_DMI
"product_name",
235 SYSFS_CLASS_DMI
"bios_version",
236 SYSFS_CLASS_DMI
"sys_vendor",
237 SYSFS_CLASS_DMI
"chassis_asset_tag",
245 must_read(remote
, &len
, sizeof(len
));
246 if ((file
= (char *)malloc(len
+ 1)) == NULL
)
247 fatal("privsep", NULL
);
248 must_read(remote
, file
, len
);
251 for (f
=authorized
; *f
!= NULL
; f
++) {
252 if (regcomp(&preg
, *f
, REG_NOSUB
) != 0)
253 /* Should not happen */
254 fatal("privsep", "unable to compile a regex");
255 if (regexec(&preg
, file
, 0, NULL
, 0) == 0) {
262 log_warnx("privsep", "not authorized to open %s", file
);
264 must_write(remote
, &rc
, sizeof(int));
268 if ((fd
= open(file
, O_RDONLY
)) == -1) {
270 must_write(remote
, &rc
, sizeof(int));
275 must_write(remote
, &fd
, sizeof(int));
284 struct ethtool_cmd ethc
;
288 memset(&ifr
, 0, sizeof(ifr
));
289 memset(ðc
, 0, sizeof(ethc
));
290 must_read(remote
, &len
, sizeof(int));
291 if ((ifname
= (char*)malloc(len
+ 1)) == NULL
)
292 fatal("privsep", NULL
);
293 must_read(remote
, ifname
, len
);
295 strlcpy(ifr
.ifr_name
, ifname
, sizeof(ifr
.ifr_name
));
297 ifr
.ifr_data
= (caddr_t
)ðc
;
298 ethc
.cmd
= ETHTOOL_GSET
;
299 if ((rc
= ioctl(sock
, SIOCETHTOOL
, &ifr
)) != 0) {
300 must_write(remote
, &rc
, sizeof(int));
303 must_write(remote
, &rc
, sizeof(int));
304 must_write(remote
, ðc
, sizeof(struct ethtool_cmd
));
310 struct sockaddr_ll sa
;
312 char ifname
[IFNAMSIZ
];
314 must_read(remote
, ifname
, IFNAMSIZ
);
315 ifname
[IFNAMSIZ
-1] = '\0';
317 /* Open listening socket to receive/send frames */
318 if ((s
= socket(PF_PACKET
, SOCK_RAW
,
319 htons(ETH_P_ALL
))) < 0) {
321 must_write(remote
, &rc
, sizeof(rc
));
324 memset(&sa
, 0, sizeof(sa
));
325 sa
.sll_family
= AF_PACKET
;
327 sa
.sll_ifindex
= if_nametoindex(ifname
);
328 if (bind(s
, (struct sockaddr
*)&sa
, sizeof(sa
)) < 0) {
330 must_write(remote
, &rc
, sizeof(rc
));
334 must_write(remote
, &rc
, sizeof(rc
));
340 asroot_iface_multicast()
344 memset(&ifr
, 0, sizeof(ifr
));
345 must_read(remote
, ifr
.ifr_name
, IFNAMSIZ
);
346 ifr
.ifr_name
[IFNAMSIZ
-1] = '\0';
347 must_read(remote
, ifr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
348 must_read(remote
, &add
, sizeof(int));
350 if (ioctl(sock
, (add
)?SIOCADDMULTI
:SIOCDELMULTI
,
354 must_write(remote
, &rc
, sizeof(rc
));
361 static struct sockaddr_un
*addr
= NULL
;
362 struct sockaddr_un bogus
;
365 addr
= (struct sockaddr_un
*)malloc(sizeof(struct sockaddr_un
));
366 must_read(remote
, addr
, sizeof(struct sockaddr_un
));
368 /* We have already been asked to connect to a socket. We will
369 * connect to the same socket. */
370 must_read(remote
, &bogus
, sizeof(struct sockaddr_un
));
371 if (addr
->sun_family
!= AF_UNIX
)
372 fatal("privsep", "someone is trying to trick me");
373 addr
->sun_path
[sizeof(addr
->sun_path
)-1] = '\0';
375 if ((sock
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0) {
376 log_warn("privsep", "cannot open socket");
377 must_write(remote
, &sock
, sizeof(int));
380 if ((rc
= connect(sock
, (struct sockaddr
*) addr
,
381 sizeof(struct sockaddr_un
))) != 0) {
382 log_info("privsep", "cannot connect to %s: %s",
383 addr
->sun_path
, strerror(errno
));
386 must_write(remote
, &rc
, sizeof(int));
389 must_write(remote
, &rc
, sizeof(int));
390 send_fd(remote
, sock
);
394 struct dispatch_actions
{
396 void(*function
)(void);
399 static struct dispatch_actions actions
[] = {
400 {PRIV_PING
, asroot_ping
},
401 {PRIV_DELETE_CTL_SOCKET
, asroot_ctl_cleanup
},
402 {PRIV_GET_HOSTNAME
, asroot_gethostbyname
},
403 {PRIV_OPEN
, asroot_open
},
404 {PRIV_ETHTOOL
, asroot_ethtool
},
405 {PRIV_IFACE_INIT
, asroot_iface_init
},
406 {PRIV_IFACE_MULTICAST
, asroot_iface_multicast
},
407 {PRIV_SNMP_SOCKET
, asroot_snmp_socket
},
411 /* Main loop, run as root */
416 struct dispatch_actions
*a
;
418 while (!may_read(remote
, &cmd
, sizeof(int))) {
419 for (a
= actions
; a
->function
!= NULL
; a
++) {
425 if (a
->function
== NULL
)
426 fatal("privsep", "bogus message received");
428 /* Should never be there */
436 if ((rc
= waitpid(monitored
, &status
, WNOHANG
)) == 0) {
437 log_debug("privsep", "killing child");
438 kill(monitored
, SIGTERM
);
440 if ((rc
= waitpid(monitored
, &status
, WNOHANG
)) == -1)
442 log_debug("privsep", "waiting for child %d to terminate", monitored
);
445 /* If priv parent gets a TERM or HUP, pass it through to child instead */
447 sig_pass_to_chld(int sig
)
451 kill(monitored
, sig
);
455 /* if parent gets a SIGCHLD, it will exit */
459 log_debug("privsep", "received signal %d, exiting", sig
);
465 priv_init(char *chrootdir
, int ctl
, uid_t uid
, gid_t gid
)
472 /* Create socket pair */
473 if (socketpair(AF_LOCAL
, SOCK_DGRAM
, PF_UNSPEC
, pair
) < 0)
474 fatal("privsep", "unable to create socket pair for privilege separation");
476 /* Spawn off monitor */
477 if ((monitored
= fork()) < 0)
478 fatal("privsep", "unable to fork monitor");
481 /* We are in the children, drop privileges */
482 if (RUNNING_ON_VALGRIND
)
483 log_warnx("privsep", "running on valgrind, keep privileges");
485 if (chroot(chrootdir
) == -1)
486 fatal("privsep", "unable to chroot");
488 fatal("privsep", "unable to chdir");
490 if (setresgid(gid
, gid
, gid
) == -1)
491 fatal("privsep", "setresgid() failed");
492 if (setgroups(1, gidset
) == -1)
493 fatal("privsep", "setgroups() failed");
494 if (setresuid(uid
, uid
, uid
) == -1)
495 fatal("privsep", "setresuid() failed");
502 /* We are in the monitor */
503 if (ctl
!= -1) close(ctl
);
506 if (atexit(priv_exit
) != 0)
507 fatal("privsep", "unable to set exit function");
508 if ((sock
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
509 fatal("privsep", "unable to get a socket");
512 signal(SIGALRM
, sig_pass_to_chld
);
513 signal(SIGTERM
, sig_pass_to_chld
);
514 signal(SIGHUP
, sig_pass_to_chld
);
515 signal(SIGINT
, sig_pass_to_chld
);
516 signal(SIGQUIT
, sig_pass_to_chld
);
517 signal(SIGCHLD
, sig_chld
);
518 if (waitpid(monitored
, &status
, WNOHANG
) != 0)
519 /* Child is already dead */
526 /* Stolen from sbin/pflogd/privsep.c from OpenBSD */
528 * Copyright (c) 2003 Can Erkin Acar
529 * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
531 * Permission to use, copy, modify, and/or distribute this software for any
532 * purpose with or without fee is hereby granted, provided that the above
533 * copyright notice and this permission notice appear in all copies.
535 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
536 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
537 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
538 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
539 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
540 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
541 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
544 /* Read all data or return 1 for error. */
546 may_read(int fd
, void *buf
, size_t n
)
549 ssize_t res
, pos
= 0;
552 res
= read(fd
, s
+ pos
, n
- pos
);
555 if (errno
== EINTR
|| errno
== EAGAIN
)
566 /* Read data with the assertion that it all must come through, or
567 * else abort the process. Based on atomicio() from openssh. */
569 must_read(int fd
, void *buf
, size_t n
)
572 ssize_t res
, pos
= 0;
575 res
= read(fd
, s
+ pos
, n
- pos
);
578 if (errno
== EINTR
|| errno
== EAGAIN
)
588 /* Write data with the assertion that it all has to be written, or
589 * else abort the process. Based on atomicio() from openssh. */
591 must_write(int fd
, const void *buf
, size_t n
)
594 ssize_t res
, pos
= 0;
597 res
= write(fd
, s
+ pos
, n
- pos
);
600 if (errno
== EINTR
|| errno
== EAGAIN
)