]> git.ipfire.org Git - thirdparty/lldpd.git/blob - src/daemon/priv.c
log: convert LLOG_* to log_* and add more debug logs
[thirdparty/lldpd.git] / src / daemon / priv.c
1 /* -*- mode: c; c-file-style: "openbsd" -*- */
2 /*
3 * Copyright (c) 2008 Vincent Bernat <bernat@luffy.cx>
4 *
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.
8 *
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.
16 */
17
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. */
22
23 #include "lldpd.h"
24
25 #include <stdio.h>
26 #include <unistd.h>
27 #include <errno.h>
28 #include <sys/wait.h>
29 #include <sys/stat.h>
30 #include <sys/socket.h>
31 #include <sys/un.h>
32 #include <regex.h>
33 #include <fcntl.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_DELETE_CTL_SOCKET,
59 PRIV_GET_HOSTNAME,
60 PRIV_OPEN,
61 PRIV_ETHTOOL,
62 PRIV_IFACE_INIT,
63 PRIV_IFACE_MULTICAST,
64 PRIV_SNMP_SOCKET,
65 };
66
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);
70
71 static int remote; /* Other side */
72 static int monitored = -1; /* Child */
73 static int sock = -1;
74
75 /* Proxies */
76
77 static void
78 priv_ping()
79 {
80 int cmd, rc;
81 cmd = PRIV_PING;
82 must_write(remote, &cmd, sizeof(int));
83 must_read(remote, &rc, sizeof(int));
84 log_debug("privsep", "monitor ready");
85 }
86
87 /* Proxy for ctl_cleanup */
88 void
89 priv_ctl_cleanup()
90 {
91 int cmd, rc;
92 cmd = PRIV_DELETE_CTL_SOCKET;
93 must_write(remote, &cmd, sizeof(int));
94 must_read(remote, &rc, sizeof(int));
95 }
96
97 /* Proxy for gethostbyname */
98 char *
99 priv_gethostbyname()
100 {
101 int cmd, rc;
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);
109 return buf;
110 }
111
112 /* Proxy for open */
113 int
114 priv_open(char *file)
115 {
116 int cmd, len, rc;
117 cmd = PRIV_OPEN;
118 must_write(remote, &cmd, sizeof(int));
119 len = strlen(file);
120 must_write(remote, &len, sizeof(int));
121 must_write(remote, file, len + 1);
122 must_read(remote, &rc, sizeof(int));
123 if (rc == -1)
124 return rc;
125 return receive_fd(remote);
126 }
127
128 /* Proxy for ethtool ioctl */
129 int
130 priv_ethtool(char *ifname, struct ethtool_cmd *ethc)
131 {
132 int cmd, rc, len;
133 cmd = PRIV_ETHTOOL;
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));
139 if (rc != 0)
140 return rc;
141 must_read(remote, ethc, sizeof(struct ethtool_cmd));
142 return rc;
143 }
144
145 int
146 priv_iface_init(const char *name)
147 {
148 int cmd, rc;
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);
155 }
156
157 int
158 priv_iface_multicast(const char *name, u_int8_t *mac, int add)
159 {
160 int cmd, rc;
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));
167 return rc;
168 }
169
170 int
171 priv_snmp_socket(struct sockaddr_un *addr)
172 {
173 int cmd, rc;
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));
178 if (rc < 0)
179 return rc;
180 return receive_fd(remote);
181 }
182
183 static void
184 asroot_ping()
185 {
186 int rc = 1;
187 must_write(remote, &rc, sizeof(int));
188 }
189
190 static void
191 asroot_ctl_cleanup()
192 {
193 int rc = 0;
194 ctl_cleanup(LLDPD_CTL_SOCKET);
195
196 /* Ack */
197 must_write(remote, &rc, sizeof(int));
198 }
199
200 static void
201 asroot_gethostbyname()
202 {
203 struct utsname un;
204 struct hostent *hp;
205 int len;
206 if (uname(&un) != 0)
207 fatal("privsep", "failed to get system information");
208 if ((hp = gethostbyname(un.nodename)) == NULL) {
209 log_info("privsep", "unable to get system name");
210 res_init();
211 len = strlen(un.nodename);
212 must_write(remote, &len, sizeof(int));
213 must_write(remote, un.nodename, len + 1);
214 } else {
215 len = strlen(hp->h_name);
216 must_write(remote, &len, sizeof(int));
217 must_write(remote, hp->h_name, len + 1);
218 }
219 }
220
221 static void
222 asroot_open()
223 {
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",
238 NULL
239 };
240 const char **f;
241 char *file;
242 int fd, len, rc;
243 regex_t preg;
244
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);
249 file[len] = '\0';
250
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) {
256 regfree(&preg);
257 break;
258 }
259 regfree(&preg);
260 }
261 if (*f == NULL) {
262 log_warnx("privsep", "not authorized to open %s", file);
263 rc = -1;
264 must_write(remote, &rc, sizeof(int));
265 free(file);
266 return;
267 }
268 if ((fd = open(file, O_RDONLY)) == -1) {
269 rc = -1;
270 must_write(remote, &rc, sizeof(int));
271 free(file);
272 return;
273 }
274 free(file);
275 must_write(remote, &fd, sizeof(int));
276 send_fd(remote, fd);
277 close(fd);
278 }
279
280 static void
281 asroot_ethtool()
282 {
283 struct ifreq ifr;
284 struct ethtool_cmd ethc;
285 int len, rc;
286 char *ifname;
287
288 memset(&ifr, 0, sizeof(ifr));
289 memset(&ethc, 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);
294 ifname[len] = '\0';
295 strlcpy(ifr.ifr_name, ifname, sizeof(ifr.ifr_name));
296 free(ifname);
297 ifr.ifr_data = (caddr_t)&ethc;
298 ethc.cmd = ETHTOOL_GSET;
299 if ((rc = ioctl(sock, SIOCETHTOOL, &ifr)) != 0) {
300 must_write(remote, &rc, sizeof(int));
301 return;
302 }
303 must_write(remote, &rc, sizeof(int));
304 must_write(remote, &ethc, sizeof(struct ethtool_cmd));
305 }
306
307 static void
308 asroot_iface_init()
309 {
310 struct sockaddr_ll sa;
311 int s, rc = 0;
312 char ifname[IFNAMSIZ];
313
314 must_read(remote, ifname, IFNAMSIZ);
315 ifname[IFNAMSIZ-1] = '\0';
316
317 /* Open listening socket to receive/send frames */
318 if ((s = socket(PF_PACKET, SOCK_RAW,
319 htons(ETH_P_ALL))) < 0) {
320 rc = errno;
321 must_write(remote, &rc, sizeof(rc));
322 return;
323 }
324 memset(&sa, 0, sizeof(sa));
325 sa.sll_family = AF_PACKET;
326 sa.sll_protocol = 0;
327 sa.sll_ifindex = if_nametoindex(ifname);
328 if (bind(s, (struct sockaddr*)&sa, sizeof(sa)) < 0) {
329 rc = errno;
330 must_write(remote, &rc, sizeof(rc));
331 close(s);
332 return;
333 }
334 must_write(remote, &rc, sizeof(rc));
335 send_fd(remote, s);
336 close(s);
337 }
338
339 static void
340 asroot_iface_multicast()
341 {
342 int add, rc = 0;
343 struct ifreq ifr;
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));
349
350 if (ioctl(sock, (add)?SIOCADDMULTI:SIOCDELMULTI,
351 &ifr) < 0)
352 rc = errno;
353
354 must_write(remote, &rc, sizeof(rc));
355 }
356
357 static void
358 asroot_snmp_socket()
359 {
360 int sock, rc;
361 static struct sockaddr_un *addr = NULL;
362 struct sockaddr_un bogus;
363
364 if (!addr) {
365 addr = (struct sockaddr_un *)malloc(sizeof(struct sockaddr_un));
366 must_read(remote, addr, sizeof(struct sockaddr_un));
367 } else
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';
374
375 if ((sock = socket(PF_UNIX, SOCK_STREAM, 0)) < 0) {
376 log_warn("privsep", "cannot open socket");
377 must_write(remote, &sock, sizeof(int));
378 return;
379 }
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));
384 close(sock);
385 rc = -1;
386 must_write(remote, &rc, sizeof(int));
387 return;
388 }
389 must_write(remote, &rc, sizeof(int));
390 send_fd(remote, sock);
391 close(sock);
392 }
393
394 struct dispatch_actions {
395 int msg;
396 void(*function)(void);
397 };
398
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},
408 {-1, NULL}
409 };
410
411 /* Main loop, run as root */
412 static void
413 priv_loop()
414 {
415 int cmd;
416 struct dispatch_actions *a;
417
418 while (!may_read(remote, &cmd, sizeof(int))) {
419 for (a = actions; a->function != NULL; a++) {
420 if (cmd == a->msg) {
421 a->function();
422 break;
423 }
424 }
425 if (a->function == NULL)
426 fatal("privsep", "bogus message received");
427 }
428 /* Should never be there */
429 }
430
431 static void
432 priv_exit()
433 {
434 int status;
435 int rc;
436 if ((rc = waitpid(monitored, &status, WNOHANG)) == 0) {
437 log_debug("privsep", "killing child");
438 kill(monitored, SIGTERM);
439 }
440 if ((rc = waitpid(monitored, &status, WNOHANG)) == -1)
441 _exit(0);
442 log_debug("privsep", "waiting for child %d to terminate", monitored);
443 }
444
445 /* If priv parent gets a TERM or HUP, pass it through to child instead */
446 static void
447 sig_pass_to_chld(int sig)
448 {
449 int oerrno = errno;
450 if (monitored != -1)
451 kill(monitored, sig);
452 errno = oerrno;
453 }
454
455 /* if parent gets a SIGCHLD, it will exit */
456 static void
457 sig_chld(int sig)
458 {
459 log_debug("privsep", "received signal %d, exiting", sig);
460 priv_exit();
461 }
462
463 /* Initialization */
464 void
465 priv_init(char *chrootdir, int ctl, uid_t uid, gid_t gid)
466 {
467
468 int pair[2];
469 gid_t gidset[1];
470 int status;
471
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");
475
476 /* Spawn off monitor */
477 if ((monitored = fork()) < 0)
478 fatal("privsep", "unable to fork monitor");
479 switch (monitored) {
480 case 0:
481 /* We are in the children, drop privileges */
482 if (RUNNING_ON_VALGRIND)
483 log_warnx("privsep", "running on valgrind, keep privileges");
484 else {
485 if (chroot(chrootdir) == -1)
486 fatal("privsep", "unable to chroot");
487 if (chdir("/") != 0)
488 fatal("privsep", "unable to chdir");
489 gidset[0] = gid;
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");
496 }
497 remote = pair[0];
498 close(pair[1]);
499 priv_ping();
500 break;
501 default:
502 /* We are in the monitor */
503 if (ctl != -1) close(ctl);
504 remote = pair[1];
505 close(pair[0]);
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");
510 }
511
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 */
520 _exit(1);
521 priv_loop();
522 exit(0);
523 }
524 }
525
526 /* Stolen from sbin/pflogd/privsep.c from OpenBSD */
527 /*
528 * Copyright (c) 2003 Can Erkin Acar
529 * Copyright (c) 2003 Anil Madhavapeddy <anil@recoil.org>
530 *
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.
534 *
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.
542 */
543
544 /* Read all data or return 1 for error. */
545 static int
546 may_read(int fd, void *buf, size_t n)
547 {
548 char *s = buf;
549 ssize_t res, pos = 0;
550
551 while (n > pos) {
552 res = read(fd, s + pos, n - pos);
553 switch (res) {
554 case -1:
555 if (errno == EINTR || errno == EAGAIN)
556 continue;
557 case 0:
558 return (1);
559 default:
560 pos += res;
561 }
562 }
563 return (0);
564 }
565
566 /* Read data with the assertion that it all must come through, or
567 * else abort the process. Based on atomicio() from openssh. */
568 static void
569 must_read(int fd, void *buf, size_t n)
570 {
571 char *s = buf;
572 ssize_t res, pos = 0;
573
574 while (n > pos) {
575 res = read(fd, s + pos, n - pos);
576 switch (res) {
577 case -1:
578 if (errno == EINTR || errno == EAGAIN)
579 continue;
580 case 0:
581 _exit(0);
582 default:
583 pos += res;
584 }
585 }
586 }
587
588 /* Write data with the assertion that it all has to be written, or
589 * else abort the process. Based on atomicio() from openssh. */
590 static void
591 must_write(int fd, const void *buf, size_t n)
592 {
593 const char *s = buf;
594 ssize_t res, pos = 0;
595
596 while (n > pos) {
597 res = write(fd, s + pos, n - pos);
598 switch (res) {
599 case -1:
600 if (errno == EINTR || errno == EAGAIN)
601 continue;
602 case 0:
603 _exit(0);
604 default:
605 pos += res;
606 }
607 }
608 }