1 /*
2 * $Id$
3 *
4 * Copyright (c) 2008, 2009, 2010
5 * Sten Spans <sten@blinkenlights.nl>
6 *
7 * Permission to use, copy, modify, and distribute this software for any
8 * purpose with or without fee is hereby granted, provided that the above
9 * copyright notice and this permission notice appear in all copies.
10 *
11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
18 */
19
20 #include "common.h"
21 #include "util.h"
22 #include <syslog.h>
23 #include <grp.h>
24 #include <sys/resource.h>
25 #include <pcap.h>
26
27 int8_t loglevel = CRIT;
28 int msock = -1;
29 pid_t pid = 0;
30
__nonnull()31 __nonnull()
32 static void my_vlog(const char *func, int err, const char *fmt, va_list ap) {
33 char *efmt = (char *)fmt;
34
35 if (err)
36 if (asprintf(&efmt, "%s: %s", fmt, strerror(err)) == -1)
37 efmt = (char *)fmt;
38
39 if (options & OPT_DAEMON) {
40 vsyslog((err)? LOG_ERR:LOG_INFO, efmt, ap);
41 } else {
42 if (loglevel == DEBUG)
43 fprintf(stderr, "%s: ", func);
44
45 vfprintf(stderr, efmt, ap);
46 fputc('\n', stderr);
47 }
48
49 if (efmt != fmt)
50 free(efmt);
51 }
52
__my_log(const char * func,int8_t prio,int err,const char * fmt,...)53 void __my_log(const char *func, int8_t prio, int err, const char *fmt, ...) {
54 va_list ap;
55
56 if (prio > loglevel)
57 return;
58
59 va_start(ap, fmt);
60 my_vlog(func, err, fmt, ap);
61 va_end(ap);
62 }
63
64 __noreturn
__my_fatal(const char * func,int err,const char * fmt,...)65 void __my_fatal(const char *func, int err, const char *fmt, ...) {
66 va_list ap;
67
68 va_start(ap, fmt);
69 my_vlog(func, err, fmt, ap);
70 va_end(ap);
71
72 // exit via a sigterm signal
73 if (pid)
74 parent_signal(SIGTERM, 0, &pid);
75
76 exit(EXIT_FAILURE);
77 }
78
my_malloc(size_t size)79 void * my_malloc(size_t size) {
80 void *ptr;
81
82 if ((ptr = malloc(size)) == NULL)
83 my_fatal("malloc failed");
84 memset(ptr, 0, size);
85 return(ptr);
86 }
87
my_calloc(size_t nmemb,size_t size)88 void * my_calloc(size_t nmemb, size_t size) {
89 void *ptr;
90
91 if ((ptr = calloc(nmemb, size)) == NULL)
92 my_fatal("calloc failed");
93
94 return(ptr);
95 }
96
my_strdup(const char * str)97 char * my_strdup(const char *str) {
98 char *cstr;
99
100 if ((cstr = strdup(str)) == NULL)
101 my_fatal("strdup failed");
102
103 return(cstr);
104 }
105
my_socket(int af,int type,int proto)106 int my_socket(int af, int type, int proto) {
107 int s;
108
109 if ((s = socket(af, type, proto)) == -1)
110 my_fatale("opening socket failed");
111
112 return(s);
113 }
114
my_socketpair(int spair[])115 void my_socketpair(int spair[]) {
116 int rbuf = PARENT_MSG_MAX * 10;
117
118 assert(spair != NULL);
119
120 if (socketpair(AF_UNIX, SOCK_DGRAM, 0, spair) == -1)
121 my_fatale("socketpair creation failed");
122
123 for (int i = 0; i<2; i++) {
124 if (setsockopt(spair[i], SOL_SOCKET, SO_RCVBUF,
125 &rbuf, sizeof(rbuf)) == -1)
126 my_fatale("failed to set rcvbuf");
127
128 if (setsockopt(spair[i], SOL_SOCKET, SO_SNDBUF,
129 &rbuf, sizeof(rbuf)) == -1)
130 my_fatale("failed to set sndbuf");
131 }
132 }
133
my_nonblock(int s)134 int my_nonblock(int s) {
135 int flags;
136
137 flags = fcntl(s, F_GETFL);
138 if (flags < 0)
139 return 0;
140 flags |= O_NONBLOCK;
141 if (fcntl(s, F_SETFL, flags) < 0)
142 return 0;
143
144 return flags;
145 }
146
147 // adapted from openssh's safely_chroot
__nonnull()148 __nonnull()
149 void my_chroot(const char *path) {
150 const char *cp;
151 char component[MAXPATHLEN];
152 struct stat st;
153
154 if (*path != '/')
155 my_fatal("chroot path does not begin at root");
156 if (strlen(path) >= sizeof(component))
157 my_fatal("chroot path too long");
158
159 for (cp = path; cp != NULL;) {
160 if ((cp = strchr(cp, '/')) == NULL)
161 strlcpy(component, path, sizeof(component));
162 else {
163 cp++;
164 memcpy(component, path, cp - path);
165 component[cp - path] = '\0';
166 }
167
168 if (stat(component, &st) != 0)
169 my_fatale("stat(\"%s\")", component);
170 if (st.st_uid != 0 || (st.st_mode & 022) != 0)
171 my_fatal("bad ownership or modes for chroot "
172 "directory %s\"%s\"",
173 cp == NULL ? "" : "component ", component);
174 if (!S_ISDIR(st.st_mode))
175 my_fatal("chroot path %s\"%s\" is not a directory",
176 cp == NULL ? "" : "component ", component);
177 }
178
179 if (chdir(path) == -1)
180 my_fatale("unable to chdir to chroot path \"%s\"", path);
181 if (chroot(path) == -1)
182 my_fatale("chroot(\"%s\")", path);
183 if (chdir("/") == -1)
184 my_fatale("chdir(/) after chroot");
185 }
186
__nonnull()187 __nonnull()
188 void my_drop_privs(struct passwd *pwd) {
189 if (setgroups(0, NULL) == -1)
190 my_fatale("unable to setgroups");
191
192 #ifdef HAVE_SETRESGID
193 if (setresgid(pwd->pw_gid, pwd->pw_gid, pwd->pw_gid) == -1)
194 my_fatale("unable to setresgid");
195 #elif defined(HAVE_SETREGID)
196 if (setregid(pwd->pw_gid, pwd->pw_gid) == -1)
197 my_fatale("unable to setregid");
198 #else
199 #error "no setresgid or setregid available"
200 #endif
201
202 #ifdef HAVE_SETRESUID
203 if (setresuid(pwd->pw_uid, pwd->pw_uid, pwd->pw_uid) == -1)
204 my_fatale("unable to setresuid");
205 #elif defined(HAVE_SETREUID)
206 if (setreuid(pwd->pw_uid, pwd->pw_uid) == -1)
207 my_fatale("unable to setreuid");
208 #else
209 #error "no setresuid or setreuid available"
210 #endif
211 }
212
213 /* Minimal sandbox that sets zero nprocs and filesize rlimits */
my_rlimit_child()214 void my_rlimit_child() {
215 struct rlimit rl_zero;
216
217 rl_zero.rlim_cur = rl_zero.rlim_max = 0;
218
219 #if defined(RLIMIT_NPROC)
220 if (setrlimit(RLIMIT_NPROC, &rl_zero) == -1)
221 my_fatale("setrlimit(RLIMIT_NPROC, { 0, 0 })");
222 #endif
223 if (setrlimit(RLIMIT_FSIZE, &rl_zero) == -1)
224 my_fatale("setrlimit(RLIMIT_FSIZE, { 0, 0 })");
225 }
226
__nonnull()227 __nonnull()
228 int read_line(const char *path, char *line, uint16_t len) {
229 FILE *file;
230 int ret = 0;
231
232 if ((file = fopen(path, "r")) == NULL)
233 return(0);
234
235 if (fgets(line, len, file) != NULL) {
236 line[strcspn(line, "\n")] = '\0';
237 ret = strlen(line);
238 }
239
240 fclose(file);
241 return(ret);
242 }
243
__nonnull()244 __nonnull()
245 int write_line(const char *path, char *line, uint16_t len) {
246 int fd, ret;
247
248 if ((fd = open(path, O_WRONLY|O_TRUNC)) == -1)
249 return(0);
250
251 ret = write(fd, line, len);
252 close(fd);
253 if (ret > 0)
254 return(ret);
255 return(0);
256 }
257
258 /*
259 * Actually, this is the standard IP checksum algorithm.
260 */
__nonnull()261 __nonnull()
262 uint16_t my_chksum(const void *data, size_t length, int cisco) {
263 uint32_t sum = 0;
264 const uint16_t *d = (const uint16_t *)data;
265
266 while (length > 1) {
267 sum += *d++;
268 length -= 2;
269 }
270 if (length) {
271 if (cisco) {
272 sum += htons(*(const uint8_t *)d);
273 } else {
274 sum += htons(*(const uint8_t *)d << 8);
275 }
276 }
277
278 sum = (sum >> 16) + (sum & 0xffff);
279 sum += (sum >> 16);
280 return (uint16_t)~sum;
281 }
282
my_mreq(struct parent_req * mreq)283 ssize_t my_mreq(struct parent_req *mreq) {
284 ssize_t len = 0;
285
286 assert(mreq != NULL);
287
288 len = write(msock, mreq, PARENT_REQ_LEN(mreq->len));
289 if (len < PARENT_REQ_MIN || len != PARENT_REQ_LEN(mreq->len))
290 my_fatale("only %zi bytes written", len);
291
292 memset(mreq, 0, PARENT_REQ_MAX);
293 len = read(msock, mreq, PARENT_REQ_MAX);
294 if (len < PARENT_REQ_MIN || len != PARENT_REQ_LEN(mreq->len))
295 my_fatal("invalid reply received from parent");
296
297 return(mreq->len);
298 };
299
netif_iter(struct netif * netif,struct nhead * netifs)300 struct netif *netif_iter(struct netif *netif, struct nhead *netifs) {
301
302 if (netifs == NULL)
303 return NULL;
304
305 if (netif == NULL)
306 netif = TAILQ_FIRST(netifs);
307 else
308 netif = TAILQ_NEXT(netif, entries);
309
310 for (; netif != NULL; netif = TAILQ_NEXT(netif, entries)) {
311 // skip autodetected children
312 if (!(options & OPT_ARGV) && netif->child)
313 continue;
314
315 // skip unlisted interfaces
316 if ((options & OPT_ARGV) && (netif->argv == 0))
317 continue;
318
319 // skip parents without subifs
320 if ((netif->type > NETIF_PARENT) && (netif->subif == NULL)) {
321 my_log(INFO, "skipping interface %s", netif->name);
322 continue;
323 }
324
325 break;
326 }
327
328 return(netif);
329 }
330
subif_iter(struct netif * subif,struct netif * netif)331 struct netif *subif_iter(struct netif *subif, struct netif *netif) {
332
333 if (netif == NULL)
334 return(NULL);
335
336 if (subif == NULL) {
337 if (netif->type > NETIF_PARENT)
338 return(netif->subif);
339 else if (netif->type < NETIF_REGULAR)
340 return(NULL);
341 else
342 return(netif);
343 } else if (subif == netif) {
344 return(NULL);
345 } else {
346 return(subif->subif);
347 }
348 }
349
netif_excluded(struct netif * netif,struct ehead * exclifs)350 int netif_excluded(struct netif *netif, struct ehead *exclifs) {
351 struct exclif *exclif = TAILQ_FIRST(exclifs);
352 for (; exclif != NULL; exclif = TAILQ_NEXT(exclif, entries)) {
353 if (strcmp(netif->name, exclif->name) == 0)
354 return 1;
355 }
356 return 0;
357 }
358
netif_protos(struct netif * netif,struct mhead * mqueue)359 void netif_protos(struct netif *netif, struct mhead *mqueue) {
360 struct netif *subif = NULL;
361 struct parent_msg *qmsg = NULL;
362 uint16_t protos = 0;
363
364 while ((subif = subif_iter(subif, netif)) != NULL) {
365 TAILQ_FOREACH(qmsg, mqueue, entries) {
366 if (subif->index == qmsg->index)
367 protos |= (1 << qmsg->proto);
368 }
369 }
370 netif->protos = protos;
371 }
372
netif_descr(struct netif * netif,struct mhead * mqueue)373 void netif_descr(struct netif *netif, struct mhead *mqueue) {
374 struct parent_msg *qmsg = NULL;
375 struct parent_req *mreq = NULL;
376 char *peer = NULL, *suffix = NULL;
377 char descr[IFDESCRSIZE] = {};
378 char paddr[ETHER_ADDR_LEN] = {};
379 uint16_t peers = 0, peer_suffix;
380
381 peer_suffix = PEER_PORTNAME;
382 if (options & OPT_USEDESCR)
383 peer_suffix = PEER_PORTDESCR;
384
385 TAILQ_FOREACH(qmsg, mqueue, entries) {
386 if (netif->index != qmsg->index)
387 continue;
388
389 if (!peer && qmsg->peer[PEER_HOSTNAME]) {
390 peer = my_strdup(qmsg->peer[PEER_HOSTNAME]);
391 peer[strcspn(peer, ".")] = '\0';
392 }
393
394 if (!suffix && qmsg->peer[peer_suffix])
395 suffix = my_strdup(qmsg->peer[peer_suffix]);
396
397 // this assumes a sorted queue
398 if (memcmp(paddr, qmsg->msg + ETHER_ADDR_LEN, ETHER_ADDR_LEN) == 0)
399 continue;
400
401 memcpy(paddr, qmsg->msg + ETHER_ADDR_LEN, ETHER_ADDR_LEN);
402 peers++;
403 }
404
405 if (peers == 0) {
406 memset(descr, 0, IFDESCRSIZE);
407 } else if (peers == 1) {
408 if (suffix)
409 portname_abbr(suffix);
410 if (peer && suffix)
411 snprintf(descr, IFDESCRSIZE, "connected to %s (%s)", peer, suffix);
412 else if (peer)
413 snprintf(descr, IFDESCRSIZE, "connected to %s", peer);
414 else
415 memset(descr, 0, IFDESCRSIZE);
416 } else {
417 snprintf(descr, IFDESCRSIZE, "connected to %" PRIu16 " peers", peers);
418 }
419
420 if (peer)
421 free(peer);
422 if (suffix)
423 free(suffix);
424
425 // only update if changed
426 if (strncmp(descr, netif->description, IFDESCRSIZE) == 0)
427 return;
428
429 mreq = my_malloc(PARENT_REQ_MAX);
430 mreq->op = PARENT_DESCR;
431 mreq->index = netif->index;
432 mreq->len = strlen(descr) + 1;
433 memcpy(mreq->buf, descr, mreq->len);
434
435 if (!my_mreq(mreq))
436 my_log(CRIT, "ifdescr ioctl failed on %s", netif->name);
437
438 free(mreq);
439 }
440
portname_abbr(char * portname)441 void portname_abbr(char *portname) {
442 size_t len;
443 char *media_types[] = { "FastEthernet", "GigabitEthernet",
444 "TenGigabitEthernet", NULL};
445
446 assert(portname);
447
448 for (int m = 0; media_types[m] != NULL; m++) {
449 if (strstr(portname, media_types[m]) != portname)
450 continue;
451 len = strlen(media_types[m]);
452 memmove(portname + 2, portname + len, strlen(portname + len) + 1);
453 return;
454 }
455
456 if (strcasestr(portname, "ethernet") == portname) {
457 len = strlen("ethernet");
458 memmove(portname + 3, portname + len, strlen(portname + len) + 1);
459 }
460 }
461
462 static FILE *p_fd = NULL;
463 static pcap_t *p_handle = NULL;
464 static pcap_dumper_t *p_dump = NULL;
465
my_pcap_init(int fd)466 void my_pcap_init(int fd) {
467 struct stat buf = {};
468
469 if (fstat(fd, &buf))
470 my_fatal("stdin fd not available");
471 if (isatty(fd))
472 my_fatal("please redirect stdout to tcpdump or a file");
473
474 if ((p_fd = fdopen(fd, "w")) == NULL)
475 my_fatale("failed to create stdio stream");
476
477 p_handle = pcap_open_dead(DLT_EN10MB, ETHER_MAX_LEN);
478 p_dump = pcap_dump_fopen(p_handle, p_fd);
479 fflush(p_fd);
480 }
481
my_pcap_write(struct parent_msg * msg)482 void my_pcap_write(struct parent_msg *msg) {
483 struct pcap_pkthdr pcap_rec_hdr = {};
484 struct timeval tv = {};
485
486 if (!p_dump)
487 return;
488
489 // create a pcap record header
490 if (gettimeofday(&tv, NULL) == 0) {
491 pcap_rec_hdr.ts.tv_sec = tv.tv_sec;
492 pcap_rec_hdr.ts.tv_usec = tv.tv_usec;
493 }
494 pcap_rec_hdr.caplen = msg->len;
495 pcap_rec_hdr.len = msg->len;
496 pcap_dump((void *)p_dump, &pcap_rec_hdr, msg->msg);
497 fflush(p_fd);
498 }
499
my_pcap_close()500 void my_pcap_close() {
501 if (p_dump) {
502 pcap_dump_close(p_dump);
503 p_dump = NULL;
504 }
505 if (p_handle) {
506 pcap_close(p_handle);
507 p_handle = NULL;
508 }
509 }
510
511