]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/priv.c
Add "format" option.
[thirdparty/lldpd.git] / src / priv.c
1 /*
2 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
3 *
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.
7 *
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.
15 */
16
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. */
21
22 #include "lldpd.h"
23
24 #include <stdio.h>
25 #include <unistd.h>
26 #include <errno.h>
27 #include <sys/wait.h>
28 #include <sys/stat.h>
29 #include <sys/socket.h>
30 #include <sys/un.h>
31 #include <regex.h>
32 #include <fcntl.h>
33 #include <pwd.h>
34 #include <grp.h>
35 #include <sys/utsname.h>
36 #include <sys/ioctl.h>
37 #include <netdb.h>
38 #include <linux/sockios.h>
39 #include <linux/if_packet.h>
40
41 /* Use resolv.h */
42 #ifdef HAVE_SYS_TYPES_H
43 # include <sys/types.h>
44 #endif
45 #ifdef HAVE_NETINET_IN_H
46 # include <netinet/in.h>
47 #endif
48 #ifdef HAVE_ARPA_NAMESER_H
49 # include <arpa/nameser.h> /* DNS HEADER struct */
50 #endif
51 #ifdef HAVE_NETDB_H
52 # include <netdb.h>
53 #endif
54 #include <resolv.h>
55
56 enum {
57 PRIV_PING,
58 PRIV_CREATE_CTL_SOCKET,
59 PRIV_DELETE_CTL_SOCKET,
60 PRIV_GET_HOSTNAME,
61 PRIV_OPEN,
62 PRIV_ETHTOOL,
63 PRIV_IFACE_INIT,
64 PRIV_IFACE_MULTICAST,
65 PRIV_SNMP_SOCKET,
66 };
67
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);
71
72 static int remote; /* Other side */
73 static int monitored = -1; /* Child */
74 static int sock = -1;
75
76 /* UID/GID of unprivileged user */
77 static gid_t gid = 0;
78 static uid_t uid = 0;
79
80 /* Proxies */
81
82 static void
83 priv_ping()
84 {
85 int cmd, rc;
86 cmd = PRIV_PING;
87 must_write(remote, &cmd, sizeof(int));
88 must_read(remote, &rc, sizeof(int));
89 LLOG_DEBUG("monitor ready");
90 }
91
92 /* Proxy for ctl_create, no argument since this is the monitor that decides the
93 * location of the socket */
94 int
95 priv_ctl_create()
96 {
97 int cmd, rc;
98 cmd = PRIV_CREATE_CTL_SOCKET;
99 must_write(remote, &cmd, sizeof(int));
100 must_read(remote, &rc, sizeof(int));
101 if (rc == -1)
102 return -1;
103 return receive_fd(remote);
104 }
105
106 /* Proxy for ctl_cleanup */
107 void
108 priv_ctl_cleanup()
109 {
110 int cmd, rc;
111 cmd = PRIV_DELETE_CTL_SOCKET;
112 must_write(remote, &cmd, sizeof(int));
113 must_read(remote, &rc, sizeof(int));
114 }
115
116 /* Proxy for gethostbyname */
117 char *
118 priv_gethostbyname()
119 {
120 int cmd, rc;
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)
126 fatal(NULL);
127 must_read(remote, buf, rc+1);
128 return buf;
129 }
130
131 /* Proxy for open */
132 int
133 priv_open(char *file)
134 {
135 int cmd, len, rc;
136 cmd = PRIV_OPEN;
137 must_write(remote, &cmd, sizeof(int));
138 len = strlen(file);
139 must_write(remote, &len, sizeof(int));
140 must_write(remote, file, len + 1);
141 must_read(remote, &rc, sizeof(int));
142 if (rc == -1)
143 return rc;
144 return receive_fd(remote);
145 }
146
147 /* Proxy for ethtool ioctl */
148 int
149 priv_ethtool(char *ifname, struct ethtool_cmd *ethc)
150 {
151 int cmd, rc, len;
152 cmd = PRIV_ETHTOOL;
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));
158 if (rc != 0)
159 return rc;
160 must_read(remote, ethc, sizeof(struct ethtool_cmd));
161 return rc;
162 }
163
164 int
165 priv_iface_init(const char *name)
166 {
167 int cmd, rc;
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);
174 }
175
176 int
177 priv_iface_multicast(const char *name, u_int8_t *mac, int add)
178 {
179 int cmd, rc;
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));
186 return rc;
187 }
188
189 int
190 priv_snmp_socket(struct sockaddr_un *addr)
191 {
192 int cmd, rc;
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));
197 if (rc < 0)
198 return rc;
199 return receive_fd(remote);
200 }
201
202 static void
203 asroot_ping()
204 {
205 int rc = 1;
206 must_write(remote, &rc, sizeof(int));
207 }
208
209 static void
210 asroot_ctl_create()
211 {
212 int rc;
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));
216 return;
217 }
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));
225 send_fd(remote, rc);
226 close(rc);
227 }
228
229 static void
230 asroot_ctl_cleanup()
231 {
232 int rc = 0;
233 ctl_cleanup(LLDPD_CTL_SOCKET);
234
235 /* Ack */
236 must_write(remote, &rc, sizeof(int));
237 }
238
239 static void
240 asroot_gethostbyname()
241 {
242 struct utsname un;
243 struct hostent *hp;
244 int len;
245 if (uname(&un) != 0)
246 fatal("[priv]: failed to get system information");
247 if ((hp = gethostbyname(un.nodename)) == NULL) {
248 LLOG_INFO("[priv]: unable to get system name");
249 res_init();
250 len = strlen(un.nodename);
251 must_write(remote, &len, sizeof(int));
252 must_write(remote, un.nodename, len + 1);
253 } else {
254 len = strlen(hp->h_name);
255 must_write(remote, &len, sizeof(int));
256 must_write(remote, hp->h_name, len + 1);
257 }
258 }
259
260 static void
261 asroot_open()
262 {
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",
277 NULL
278 };
279 const char **f;
280 char *file;
281 int fd, len, rc;
282 regex_t preg;
283
284 must_read(remote, &len, sizeof(len));
285 if ((file = (char *)malloc(len + 1)) == NULL)
286 fatal(NULL);
287 must_read(remote, file, len);
288 file[len] = '\0';
289
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) {
295 regfree(&preg);
296 break;
297 }
298 regfree(&preg);
299 }
300 if (*f == NULL) {
301 LLOG_WARNX("[priv]: not authorized to open %s", file);
302 rc = -1;
303 must_write(remote, &rc, sizeof(int));
304 free(file);
305 return;
306 }
307 if ((fd = open(file, O_RDONLY)) == -1) {
308 rc = -1;
309 must_write(remote, &rc, sizeof(int));
310 free(file);
311 return;
312 }
313 free(file);
314 must_write(remote, &fd, sizeof(int));
315 send_fd(remote, fd);
316 close(fd);
317 }
318
319 static void
320 asroot_ethtool()
321 {
322 struct ifreq ifr;
323 struct ethtool_cmd ethc;
324 int len, rc;
325 char *ifname;
326
327 memset(&ifr, 0, sizeof(ifr));
328 memset(&ethc, 0, sizeof(ethc));
329 must_read(remote, &len, sizeof(int));
330 if ((ifname = (char*)malloc(len + 1)) == NULL)
331 fatal(NULL);
332 must_read(remote, ifname, len);
333 ifname[len] = '\0';
334 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
335 free(ifname);
336 ifr.ifr_data = (caddr_t)&ethc;
337 ethc.cmd = ETHTOOL_GSET;
338 if ((rc = ioctl(sock, SIOCETHTOOL, &ifr)) != 0) {
339 must_write(remote, &rc, sizeof(int));
340 return;
341 }
342 must_write(remote, &rc, sizeof(int));
343 must_write(remote, &ethc, sizeof(struct ethtool_cmd));
344 }
345
346 static void
347 asroot_iface_init()
348 {
349 struct sockaddr_ll sa;
350 int s;
351 char ifname[IFNAMSIZ];
352
353 must_read(remote, ifname, IFNAMSIZ);
354 ifname[IFNAMSIZ-1] = '\0';
355
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));
360 return;
361 }
362 memset(&sa, 0, sizeof(sa));
363 sa.sll_family = AF_PACKET;
364 sa.sll_protocol = 0;
365 sa.sll_ifindex = if_nametoindex(ifname);
366 if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
367 must_write(remote, &errno, sizeof(errno));
368 close(s);
369 return;
370 }
371 errno = 0;
372 must_write(remote, &errno, sizeof(errno));
373 send_fd(remote, s);
374 close(s);
375 }
376
377 static void
378 asroot_iface_multicast()
379 {
380 int add, rc = 0;
381 struct ifreq ifr;
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));
387
388 if (ioctl(sock, (add)?SIOCADDMULTI:SIOCDELMULTI,
389 &ifr) < 0) {
390 must_write(remote, &errno, sizeof(errno));
391 return;
392 }
393
394 must_write(remote, &rc, sizeof(rc));
395 }
396
397 static void
398 asroot_snmp_socket()
399 {
400 int sock, rc;
401 static struct sockaddr_un *addr = NULL;
402 struct sockaddr_un bogus;
403
404 if (!addr) {
405 addr = (struct sockaddr_un *)malloc(sizeof(struct sockaddr_un));
406 must_read(remote, addr, sizeof(struct sockaddr_un));
407 } else
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';
414
415 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
416 LLOG_WARN("[priv]: cannot open socket");
417 must_write(remote, &sock, sizeof(int));
418 return;
419 }
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));
424 close(sock);
425 rc = -1;
426 must_write(remote, &rc, sizeof(int));
427 return;
428 }
429 must_write(remote, &rc, sizeof(int));
430 send_fd(remote, sock);
431 close(sock);
432 }
433
434 struct dispatch_actions {
435 int msg;
436 void(*function)(void);
437 };
438
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},
449 {-1, NULL}
450 };
451
452 /* Main loop, run as root */
453 static void
454 priv_loop()
455 {
456 int cmd;
457 struct dispatch_actions *a;
458
459 while (!may_read(remote, &cmd, sizeof(int))) {
460 for (a = actions; a->function != NULL; a++) {
461 if (cmd == a->msg) {
462 a->function();
463 break;
464 }
465 }
466 if (a->function == NULL)
467 fatal("[priv]: bogus message received");
468 }
469 /* Should never be there */
470 }
471
472 static void
473 priv_exit()
474 {
475 int status;
476 int rc;
477 if ((rc = waitpid(monitored, &status, WNOHANG)) == 0) {
478 LLOG_DEBUG("[priv]: killing child");
479 kill(monitored, SIGTERM);
480 }
481 if ((rc = waitpid(monitored, &status, WNOHANG)) == -1)
482 _exit(0);
483 LLOG_DEBUG("[priv]: waiting for child %d to terminate", monitored);
484 }
485
486 /* If priv parent gets a TERM or HUP, pass it through to child instead */
487 static void
488 sig_pass_to_chld(int sig)
489 {
490 int oerrno = errno;
491 if (monitored != -1)
492 kill(monitored, sig);
493 errno = oerrno;
494 }
495
496 /* if parent gets a SIGCHLD, it will exit */
497 static void
498 sig_chld(int sig)
499 {
500 LLOG_DEBUG("[priv]: received signal %d, exiting", sig);
501 priv_exit();
502 }
503
504 /* Initialization */
505 void
506 priv_init(char *chrootdir)
507 {
508 int pair[2];
509 struct passwd *user;
510 struct group *group;
511 gid_t gidset[1];
512 int status;
513
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");
517
518 /* Get users */
519 if ((user = getpwnam(PRIVSEP_USER)) == NULL)
520 fatal("[priv]: no " PRIVSEP_USER " user for privilege separation");
521 uid = user->pw_uid;
522 if ((group = getgrnam(PRIVSEP_GROUP)) == NULL)
523 fatal("[priv]: no " PRIVSEP_GROUP " group for privilege separation");
524 gid = group->gr_gid;
525
526 /* Spawn off monitor */
527 if ((monitored = fork()) < 0)
528 fatal("[priv]: unable to fork monitor");
529 switch (monitored) {
530 case 0:
531 /* We are in the children, drop privileges */
532 if (chroot(chrootdir) == -1)
533 fatal("[priv]: unable to chroot");
534 if (chdir("/") != 0)
535 fatal("[priv]: unable to chdir");
536 gidset[0] = gid;
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");
543 remote = pair[0];
544 close(pair[1]);
545 priv_ping();
546 break;
547 default:
548 /* We are in the monitor */
549 remote = pair[1];
550 close(pair[0]);
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");
555 }
556
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 */
565 _exit(1);
566 priv_loop();
567 exit(0);
568 }
569 }
570
571 /* Stolen from sbin/pflogd/privsep.c from OpenBSD */
572 /*
573 * Copyright (c) 2003 Can Erkin Acar
574 * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
575 *
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.
579 *
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.
587 */
588
589 /* Read all data or return 1 for error. */
590 static int
591 may_read(int fd, void *buf, size_t n)
592 {
593 char *s = buf;
594 ssize_t res, pos = 0;
595
596 while (n > pos) {
597 res = read(fd, s + pos, n - pos);
598 switch (res) {
599 case -1:
600 if (errno == EINTR || errno == EAGAIN)
601 continue;
602 case 0:
603 return (1);
604 default:
605 pos += res;
606 }
607 }
608 return (0);
609 }
610
611 /* Read data with the assertion that it all must come through, or
612 * else abort the process. Based on atomicio() from openssh. */
613 static void
614 must_read(int fd, void *buf, size_t n)
615 {
616 char *s = buf;
617 ssize_t res, pos = 0;
618
619 while (n > pos) {
620 res = read(fd, s + pos, n - pos);
621 switch (res) {
622 case -1:
623 if (errno == EINTR || errno == EAGAIN)
624 continue;
625 case 0:
626 _exit(0);
627 default:
628 pos += res;
629 }
630 }
631 }
632
633 /* Write data with the assertion that it all has to be written, or
634 * else abort the process. Based on atomicio() from openssh. */
635 static void
636 must_write(int fd, const void *buf, size_t n)
637 {
638 const char *s = buf;
639 ssize_t res, pos = 0;
640
641 while (n > pos) {
642 res = write(fd, s + pos, n - pos);
643 switch (res) {
644 case -1:
645 if (errno == EINTR || errno == EAGAIN)
646 continue;
647 case 0:
648 _exit(0);
649 default:
650 pos += res;
651 }
652 }
653 }