]>
git.ipfire.org Git - thirdparty/lldpd.git/blob - src/priv.c
2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 * Permission to use, copy, modify, and distribute this software for any
5 * purpose with or without fee is hereby granted, provided that the above
6 * copyright notice and this permission notice appear in all copies.
8 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
11 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
12 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
13 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
14 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 /* This file contains code for privilege separation. When an error arises in
18 * monitor (which is running as root), it just stops instead of trying to
19 * recover. This module also contains proxies to privileged operations. In this
20 * case, error can be non fatal. */
29 #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_CREATE_CTL_SOCKET
,
59 PRIV_DELETE_CTL_SOCKET
,
68 static int may_read(int, void *, size_t);
69 static void must_read(int, void *, size_t);
70 static void must_write(int, const void *, size_t);
72 static int remote
; /* Other side */
73 static int monitored
= -1; /* Child */
76 /* UID/GID of unprivileged user */
87 must_write(remote
, &cmd
, sizeof(int));
88 must_read(remote
, &rc
, sizeof(int));
89 LLOG_DEBUG("monitor ready");
92 /* Proxy for ctl_create, no argument since this is the monitor that decides the
93 * location of the socket */
98 cmd
= PRIV_CREATE_CTL_SOCKET
;
99 must_write(remote
, &cmd
, sizeof(int));
100 must_read(remote
, &rc
, sizeof(int));
103 return receive_fd(remote
);
106 /* Proxy for ctl_cleanup */
111 cmd
= PRIV_DELETE_CTL_SOCKET
;
112 must_write(remote
, &cmd
, sizeof(int));
113 must_read(remote
, &rc
, sizeof(int));
116 /* Proxy for gethostbyname */
121 static char *buf
= NULL
;
122 cmd
= PRIV_GET_HOSTNAME
;
123 must_write(remote
, &cmd
, sizeof(int));
124 must_read(remote
, &rc
, sizeof(int));
125 if ((buf
= (char*)realloc(buf
, rc
+1)) == NULL
)
127 must_read(remote
, buf
, rc
+1);
133 priv_open(char *file
)
137 must_write(remote
, &cmd
, sizeof(int));
139 must_write(remote
, &len
, sizeof(int));
140 must_write(remote
, file
, len
+ 1);
141 must_read(remote
, &rc
, sizeof(int));
144 return receive_fd(remote
);
147 /* Proxy for ethtool ioctl */
149 priv_ethtool(char *ifname
, struct ethtool_cmd
*ethc
)
153 must_write(remote
, &cmd
, sizeof(int));
154 len
= strlen(ifname
);
155 must_write(remote
, &len
, sizeof(int));
156 must_write(remote
, ifname
, len
+ 1);
157 must_read(remote
, &rc
, sizeof(int));
160 must_read(remote
, ethc
, sizeof(struct ethtool_cmd
));
165 priv_iface_init(const char *name
)
168 cmd
= PRIV_IFACE_INIT
;
169 must_write(remote
, &cmd
, sizeof(int));
170 must_write(remote
, name
, IFNAMSIZ
);
171 must_read(remote
, &rc
, sizeof(int));
172 if (rc
!= 0) return -1;
173 return receive_fd(remote
);
177 priv_iface_multicast(const char *name
, u_int8_t
*mac
, int add
)
180 cmd
= PRIV_IFACE_MULTICAST
;
181 must_write(remote
, &cmd
, sizeof(int));
182 must_write(remote
, name
, IFNAMSIZ
);
183 must_write(remote
, mac
, ETH_ALEN
);
184 must_write(remote
, &add
, sizeof(int));
185 must_read(remote
, &rc
, sizeof(int));
190 priv_snmp_socket(struct sockaddr_un
*addr
)
193 cmd
= PRIV_SNMP_SOCKET
;
194 must_write(remote
, &cmd
, sizeof(int));
195 must_write(remote
, addr
, sizeof(struct sockaddr_un
));
196 must_read(remote
, &rc
, sizeof(int));
199 return receive_fd(remote
);
206 must_write(remote
, &rc
, sizeof(int));
213 if ((rc
= ctl_create(LLDPD_CTL_SOCKET
)) == -1) {
214 LLOG_WARN("[priv]: unable to create control socket");
215 must_write(remote
, &rc
, sizeof(int));
218 if (chown(LLDPD_CTL_SOCKET
, uid
, gid
) == -1)
219 LLOG_WARN("[priv]: unable to chown control socket");
220 if (chmod(LLDPD_CTL_SOCKET
,
221 S_IRUSR
| S_IWUSR
| S_IXUSR
|
222 S_IRGRP
| S_IWGRP
| S_IXGRP
) == -1)
223 LLOG_WARN("[priv]: unable to chmod control socket");
224 must_write(remote
, &rc
, sizeof(int));
233 ctl_cleanup(LLDPD_CTL_SOCKET
);
236 must_write(remote
, &rc
, sizeof(int));
240 asroot_gethostbyname()
246 fatal("[priv]: failed to get system information");
247 if ((hp
= gethostbyname(un
.nodename
)) == NULL
) {
248 LLOG_INFO("[priv]: unable to get system name");
250 len
= strlen(un
.nodename
);
251 must_write(remote
, &len
, sizeof(int));
252 must_write(remote
, un
.nodename
, len
+ 1);
254 len
= strlen(hp
->h_name
);
255 must_write(remote
, &len
, sizeof(int));
256 must_write(remote
, hp
->h_name
, len
+ 1);
263 const char* authorized
[] = {
264 "/proc/sys/net/ipv4/ip_forward",
265 "/proc/net/bonding/[^.][^/]*",
266 "/proc/self/net/bonding/[^.][^/]*",
267 SYSFS_CLASS_NET
"[^.][^/]*/brforward",
268 SYSFS_CLASS_NET
"[^.][^/]*/brport",
269 SYSFS_CLASS_NET
"[^.][^/]*/brif/[^.][^/]*/port_no",
270 SYSFS_CLASS_NET
"[^.][^/]*/ifalias",
271 SYSFS_CLASS_DMI
"product_version",
272 SYSFS_CLASS_DMI
"product_serial",
273 SYSFS_CLASS_DMI
"product_name",
274 SYSFS_CLASS_DMI
"bios_version",
275 SYSFS_CLASS_DMI
"sys_vendor",
276 SYSFS_CLASS_DMI
"chassis_asset_tag",
284 must_read(remote
, &len
, sizeof(len
));
285 if ((file
= (char *)malloc(len
+ 1)) == NULL
)
287 must_read(remote
, file
, len
);
290 for (f
=authorized
; *f
!= NULL
; f
++) {
291 if (regcomp(&preg
, *f
, REG_NOSUB
) != 0)
292 /* Should not happen */
293 fatal("unable to compile a regex");
294 if (regexec(&preg
, file
, 0, NULL
, 0) == 0) {
301 LLOG_WARNX("[priv]: not authorized to open %s", file
);
303 must_write(remote
, &rc
, sizeof(int));
307 if ((fd
= open(file
, O_RDONLY
)) == -1) {
309 must_write(remote
, &rc
, sizeof(int));
314 must_write(remote
, &fd
, sizeof(int));
323 struct ethtool_cmd ethc
;
327 memset(&ifr
, 0, sizeof(ifr
));
328 memset(ðc
, 0, sizeof(ethc
));
329 must_read(remote
, &len
, sizeof(int));
330 if ((ifname
= (char*)malloc(len
+ 1)) == NULL
)
332 must_read(remote
, ifname
, len
);
334 strlcpy(ifr
.ifr_name
, ifname
, sizeof(ifr
.ifr_name
));
336 ifr
.ifr_data
= (caddr_t
)ðc
;
337 ethc
.cmd
= ETHTOOL_GSET
;
338 if ((rc
= ioctl(sock
, SIOCETHTOOL
, &ifr
)) != 0) {
339 must_write(remote
, &rc
, sizeof(int));
342 must_write(remote
, &rc
, sizeof(int));
343 must_write(remote
, ðc
, sizeof(struct ethtool_cmd
));
349 struct sockaddr_ll sa
;
351 char ifname
[IFNAMSIZ
];
353 must_read(remote
, ifname
, IFNAMSIZ
);
354 ifname
[IFNAMSIZ
-1] = '\0';
356 /* Open listening socket to receive/send frames */
357 if ((s
= socket(PF_PACKET
, SOCK_RAW
,
358 htons(ETH_P_ALL
))) < 0) {
359 must_write(remote
, &errno
, sizeof(errno
));
362 memset(&sa
, 0, sizeof(sa
));
363 sa
.sll_family
= AF_PACKET
;
365 sa
.sll_ifindex
= if_nametoindex(ifname
);
366 if (bind(s
, (struct sockaddr
*)&sa
, sizeof(sa
)) < 0) {
367 must_write(remote
, &errno
, sizeof(errno
));
372 must_write(remote
, &errno
, sizeof(errno
));
378 asroot_iface_multicast()
382 memset(&ifr
, 0, sizeof(ifr
));
383 must_read(remote
, ifr
.ifr_name
, IFNAMSIZ
);
384 ifr
.ifr_name
[IFNAMSIZ
-1] = '\0';
385 must_read(remote
, ifr
.ifr_hwaddr
.sa_data
, ETH_ALEN
);
386 must_read(remote
, &add
, sizeof(int));
388 if (ioctl(sock
, (add
)?SIOCADDMULTI
:SIOCDELMULTI
,
390 must_write(remote
, &errno
, sizeof(errno
));
394 must_write(remote
, &rc
, sizeof(rc
));
401 static struct sockaddr_un
*addr
= NULL
;
402 struct sockaddr_un bogus
;
405 addr
= (struct sockaddr_un
*)malloc(sizeof(struct sockaddr_un
));
406 must_read(remote
, addr
, sizeof(struct sockaddr_un
));
408 /* We have already been asked to connect to a socket. We will
409 * connect to the same socket. */
410 must_read(remote
, &bogus
, sizeof(struct sockaddr_un
));
411 if (addr
->sun_family
!= AF_UNIX
)
412 fatal("someone is trying to trick me");
413 addr
->sun_path
[sizeof(addr
->sun_path
)-1] = '\0';
415 if ((sock
= socket(PF_UNIX
, SOCK_STREAM
, 0)) < 0) {
416 LLOG_WARN("[priv]: cannot open socket");
417 must_write(remote
, &sock
, sizeof(int));
420 if ((rc
= connect(sock
, (struct sockaddr
*) addr
,
421 sizeof(struct sockaddr_un
))) != 0) {
422 LLOG_INFO("[priv]: cannot connect to %s: %s",
423 addr
->sun_path
, strerror(errno
));
426 must_write(remote
, &rc
, sizeof(int));
429 must_write(remote
, &rc
, sizeof(int));
430 send_fd(remote
, sock
);
434 struct dispatch_actions
{
436 void(*function
)(void);
439 static struct dispatch_actions actions
[] = {
440 {PRIV_PING
, asroot_ping
},
441 {PRIV_CREATE_CTL_SOCKET
, asroot_ctl_create
},
442 {PRIV_DELETE_CTL_SOCKET
, asroot_ctl_cleanup
},
443 {PRIV_GET_HOSTNAME
, asroot_gethostbyname
},
444 {PRIV_OPEN
, asroot_open
},
445 {PRIV_ETHTOOL
, asroot_ethtool
},
446 {PRIV_IFACE_INIT
, asroot_iface_init
},
447 {PRIV_IFACE_MULTICAST
, asroot_iface_multicast
},
448 {PRIV_SNMP_SOCKET
, asroot_snmp_socket
},
452 /* Main loop, run as root */
457 struct dispatch_actions
*a
;
459 while (!may_read(remote
, &cmd
, sizeof(int))) {
460 for (a
= actions
; a
->function
!= NULL
; a
++) {
466 if (a
->function
== NULL
)
467 fatal("[priv]: bogus message received");
469 /* Should never be there */
477 if ((rc
= waitpid(monitored
, &status
, WNOHANG
)) == 0) {
478 LLOG_DEBUG("[priv]: killing child");
479 kill(monitored
, SIGTERM
);
481 if ((rc
= waitpid(monitored
, &status
, WNOHANG
)) == -1)
483 LLOG_DEBUG("[priv]: waiting for child %d to terminate", monitored
);
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 parent gets a SIGCHLD, it will exit */
500 LLOG_DEBUG("[priv]: received signal %d, exiting", sig
);
506 priv_init(char *chrootdir
)
514 /* Create socket pair */
515 if (socketpair(AF_LOCAL
, SOCK_DGRAM
, PF_UNSPEC
, pair
) < 0)
516 fatal("[priv]: unable to create socket pair for privilege separation");
519 if ((user
= getpwnam(PRIVSEP_USER
)) == NULL
)
520 fatal("[priv]: no " PRIVSEP_USER
" user for privilege separation");
522 if ((group
= getgrnam(PRIVSEP_GROUP
)) == NULL
)
523 fatal("[priv]: no " PRIVSEP_GROUP
" group for privilege separation");
526 /* Spawn off monitor */
527 if ((monitored
= fork()) < 0)
528 fatal("[priv]: unable to fork monitor");
531 /* We are in the children, drop privileges */
532 if (chroot(chrootdir
) == -1)
533 fatal("[priv]: unable to chroot");
535 fatal("[priv]: unable to chdir");
537 if (setresgid(gid
, gid
, gid
) == -1)
538 fatal("[priv]: setresgid() failed");
539 if (setgroups(1, gidset
) == -1)
540 fatal("[priv]: setgroups() failed");
541 if (setresuid(uid
, uid
, uid
) == -1)
542 fatal("[priv]: setresuid() failed");
548 /* We are in the monitor */
551 if (atexit(priv_exit
) != 0)
552 fatal("[priv]: unable to set exit function");
553 if ((sock
= socket(AF_INET
, SOCK_DGRAM
, 0)) == -1) {
554 fatal("[priv]: unable to get a socket");
557 signal(SIGALRM
, sig_pass_to_chld
);
558 signal(SIGTERM
, sig_pass_to_chld
);
559 signal(SIGHUP
, sig_pass_to_chld
);
560 signal(SIGINT
, sig_pass_to_chld
);
561 signal(SIGQUIT
, sig_pass_to_chld
);
562 signal(SIGCHLD
, sig_chld
);
563 if (waitpid(monitored
, &status
, WNOHANG
) != 0)
564 /* Child is already dead */
571 /* Stolen from sbin/pflogd/privsep.c from OpenBSD */
573 * Copyright (c) 2003 Can Erkin Acar
574 * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
576 * Permission to use, copy, modify, and distribute this software for any
577 * purpose with or without fee is hereby granted, provided that the above
578 * copyright notice and this permission notice appear in all copies.
580 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
581 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
582 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
583 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
584 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
585 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
586 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
589 /* Read all data or return 1 for error. */
591 may_read(int fd
, void *buf
, size_t n
)
594 ssize_t res
, pos
= 0;
597 res
= read(fd
, s
+ pos
, n
- pos
);
600 if (errno
== EINTR
|| errno
== EAGAIN
)
611 /* Read data with the assertion that it all must come through, or
612 * else abort the process. Based on atomicio() from openssh. */
614 must_read(int fd
, void *buf
, size_t n
)
617 ssize_t res
, pos
= 0;
620 res
= read(fd
, s
+ pos
, n
- pos
);
623 if (errno
== EINTR
|| errno
== EAGAIN
)
633 /* Write data with the assertion that it all has to be written, or
634 * else abort the process. Based on atomicio() from openssh. */
636 must_write(int fd
, const void *buf
, size_t n
)
639 ssize_t res
, pos
= 0;
642 res
= write(fd
, s
+ pos
, n
- pos
);
645 if (errno
== EINTR
|| errno
== EAGAIN
)