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