1 /* $NetBSD: conf.c,v 1.10 2025/02/11 17:48:30 christos Exp $ */
2
3 /*-
4 * Copyright (c) 2015 The NetBSD Foundation, Inc.
5 * All rights reserved.
6 *
7 * This code is derived from software contributed to The NetBSD Foundation
8 * by Christos Zoulas.
9 *
10 * Redistribution and use in source and binary forms, with or without
11 * modification, are permitted provided that the following conditions
12 * are met:
13 * 1. Redistributions of source code must retain the above copyright
14 * notice, this list of conditions and the following disclaimer.
15 * 2. Redistributions in binary form must reproduce the above copyright
16 * notice, this list of conditions and the following disclaimer in the
17 * documentation and/or other materials provided with the distribution.
18 *
19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29 * POSSIBILITY OF SUCH DAMAGE.
30 */
31 #ifdef HAVE_CONFIG_H
32 #include "config.h"
33 #endif
34
35 #ifdef HAVE_SYS_CDEFS_H
36 #include <sys/cdefs.h>
37 #endif
38 __RCSID("$NetBSD: conf.c,v 1.10 2025/02/11 17:48:30 christos Exp $");
39
40 #include <stdio.h>
41 #ifdef HAVE_LIBUTIL_H
42 #include <libutil.h>
43 #endif
44 #ifdef HAVE_UTIL_H
45 #include <util.h>
46 #endif
47 #include <string.h>
48 #include <ctype.h>
49 #include <inttypes.h>
50 #include <netdb.h>
51 #include <unistd.h>
52 #include <pwd.h>
53 #include <syslog.h>
54 #include <errno.h>
55 #include <stdlib.h>
56 #include <limits.h>
57 #include <ifaddrs.h>
58 #include <arpa/inet.h>
59 #include <netinet/in.h>
60 #include <net/if.h>
61 #include <net/route.h>
62 #include <sys/socket.h>
63 #include <dirent.h>
64
65 #include "bl.h"
66 #include "internal.h"
67 #include "support.h"
68 #include "conf.h"
69
70
71 struct sockaddr_if {
72 uint8_t sif_len;
73 sa_family_t sif_family;
74 in_port_t sif_port;
75 char sif_name[16];
76 };
77
78 #define SIF_NAME(a) \
79 ((const struct sockaddr_if *)(const void *)(a))->sif_name
80
81 static int conf_is_interface(const char *);
82
83 #define FSTAR -1
84 #define FEQUAL -2
85
86 static void
advance(char ** p)87 advance(char **p)
88 {
89 char *ep = *p;
90 while (*ep && !isspace((unsigned char)*ep))
91 ep++;
92 while (*ep && isspace((unsigned char)*ep))
93 *ep++ = '\0';
94 *p = ep;
95 }
96
97 static int
conf_getnum(const char * f,size_t l,bool local,void * rp,const char * name,const char * p)98 conf_getnum(const char *f, size_t l, bool local, void *rp, const char *name,
99 const char *p)
100 {
101 int e;
102 intmax_t im;
103 int *r = rp;
104
105 if (strcmp(p, "*") == 0) {
106 *r = FSTAR;
107 return 0;
108 }
109 if (strcmp(p, "=") == 0) {
110 if (local)
111 goto out;
112 *r = FEQUAL;
113 return 0;
114 }
115
116 im = strtoi(p, NULL, 0, 0, INT_MAX, &e);
117 if (e == 0) {
118 *r = (int)im;
119 return 0;
120 }
121
122 if (f == NULL)
123 return -1;
124 (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number for %s [%s]", __func__, f, l,
125 name, p);
126 return -1;
127 out:
128 (*lfun)(LOG_ERR, "%s: %s, %zu: `=' for %s not allowed in local config",
129 __func__, f, l, name);
130 return -1;
131
132 }
133
134 static int
conf_getnfail(const char * f,size_t l,bool local,struct conf * c,const char * p)135 conf_getnfail(const char *f, size_t l, bool local, struct conf *c,
136 const char *p)
137 {
138 return conf_getnum(f, l, local, &c->c_nfail, "nfail", p);
139 }
140
141 static int
conf_getsecs(const char * f,size_t l,bool local,struct conf * c,const char * p)142 conf_getsecs(const char *f, size_t l, bool local, struct conf *c, const char *p)
143 {
144 int e;
145 char *ep;
146 intmax_t tot, im;
147
148 tot = 0;
149 if (strcmp(p, "*") == 0) {
150 c->c_duration = FSTAR;
151 return 0;
152 }
153 if (strcmp(p, "=") == 0) {
154 if (local)
155 goto out;
156 c->c_duration = FEQUAL;
157 return 0;
158 }
159 again:
160 im = strtoi(p, &ep, 0, 0, INT_MAX, &e);
161
162 if (e == ENOTSUP) {
163 switch (*ep) {
164 case 'd':
165 im *= 24;
166 /*FALLTHROUGH*/
167 case 'h':
168 im *= 60;
169 /*FALLTHROUGH*/
170 case 'm':
171 im *= 60;
172 /*FALLTHROUGH*/
173 case 's':
174 e = 0;
175 tot += im;
176 if (ep[1] != '\0') {
177 p = ep + 2;
178 goto again;
179 }
180 break;
181 }
182 } else
183 tot = im;
184
185 if (e == 0) {
186 c->c_duration = (int)tot;
187 return 0;
188 }
189
190 if (f == NULL)
191 return -1;
192 (*lfun)(LOG_ERR, "%s: %s, %zu: Bad number [%s]", __func__, f, l, p);
193 return -1;
194 out:
195 (*lfun)(LOG_ERR, "%s: %s, %zu: `=' duration not allowed in local"
196 " config", __func__, f, l);
197 return -1;
198
199 }
200
201 static int
conf_getport(const char * f,size_t l,bool local,void * r,const char * p)202 conf_getport(const char *f, size_t l, bool local, void *r, const char *p)
203 {
204 struct servent *sv;
205
206 // XXX: Pass in the proto instead
207 if ((sv = getservbyname(p, "tcp")) != NULL) {
208 *(int *)r = ntohs(sv->s_port);
209 return 0;
210 }
211 if ((sv = getservbyname(p, "udp")) != NULL) {
212 *(int *)r = ntohs(sv->s_port);
213 return 0;
214 }
215
216 return conf_getnum(f, l, local, r, "service", p);
217 }
218
219 static int
conf_getmask(const char * f,size_t l,bool local,const char ** p,int * mask)220 conf_getmask(const char *f, size_t l, bool local, const char **p, int *mask)
221 {
222 char *d;
223 const char *s = *p;
224
225 if ((d = strchr(s, ':')) != NULL) {
226 *d++ = '\0';
227 *p = d;
228 }
229 if ((d = strchr(s, '/')) == NULL) {
230 *mask = FSTAR;
231 return 0;
232 }
233
234 *d++ = '\0';
235 return conf_getnum(f, l, local, mask, "mask", d);
236 }
237
238 static int
conf_gethostport(const char * f,size_t l,bool local,struct conf * c,const char * p)239 conf_gethostport(const char *f, size_t l, bool local, struct conf *c,
240 const char *p)
241 {
242 char *d; // XXX: Ok to write to string.
243 in_port_t *port = NULL;
244 const char *pstr;
245
246 if (strcmp(p, "*") == 0) {
247 c->c_port = FSTAR;
248 c->c_lmask = FSTAR;
249 return 0;
250 }
251
252 if ((d = strchr(p, ']')) != NULL) {
253 *d++ = '\0';
254 pstr = d;
255 p++;
256 } else
257 pstr = p;
258
259 if (conf_getmask(f, l, local, &pstr, &c->c_lmask) == -1)
260 goto out;
261
262 if (d) {
263 struct sockaddr_in6 *sin6 = (void *)&c->c_ss;
264 if (debug)
265 (*lfun)(LOG_DEBUG, "%s: host6 %s", __func__, p);
266 if (strcmp(p, "*") != 0) {
267 if (inet_pton(AF_INET6, p, &sin6->sin6_addr) != 1)
268 goto out;
269 sin6->sin6_family = AF_INET6;
270 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
271 sin6->sin6_len = sizeof(*sin6);
272 #endif
273 port = &sin6->sin6_port;
274 }
275 if (!*pstr)
276 pstr = "*";
277 } else if (pstr != p || strchr(p, '.') || conf_is_interface(p)) {
278 if (pstr == p)
279 pstr = "*";
280 struct sockaddr_in *sin = (void *)&c->c_ss;
281 struct sockaddr_if *sif = (void *)&c->c_ss;
282 if (debug)
283 (*lfun)(LOG_DEBUG, "%s: host4 %s", __func__, p);
284 if (strcmp(p, "*") != 0) {
285 if (conf_is_interface(p)) {
286 if (!local)
287 goto out2;
288 if (debug)
289 (*lfun)(LOG_DEBUG, "%s: interface %s",
290 __func__, p);
291 if (c->c_lmask != FSTAR)
292 goto out1;
293 sif->sif_family = AF_MAX;
294 strlcpy(sif->sif_name, p,
295 sizeof(sif->sif_name));
296 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
297 sif->sif_len = sizeof(*sif);
298 #endif
299 port = &sif->sif_port;
300 } else if (inet_pton(AF_INET, p, &sin->sin_addr) != -1)
301 {
302 sin->sin_family = AF_INET;
303 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
304 sin->sin_len = sizeof(*sin);
305 #endif
306 port = &sin->sin_port;
307 } else
308 goto out;
309 }
310 }
311
312 if (conf_getport(f, l, local, &c->c_port, pstr) == -1)
313 return -1;
314
315 if (port && c->c_port != FSTAR && c->c_port != FEQUAL)
316 *port = htons((in_port_t)c->c_port);
317 return 0;
318 out:
319 (*lfun)(LOG_ERR, "%s: %s, %zu: Bad address [%s]", __func__, f, l, p);
320 return -1;
321 out1:
322 (*lfun)(LOG_ERR, "%s: %s, %zu: Can't specify mask %d with "
323 "interface [%s]", __func__, f, l, c->c_lmask, p);
324 return -1;
325 out2:
326 (*lfun)(LOG_ERR, "%s: %s, %zu: Interface spec does not make sense "
327 "with remote config [%s]", __func__, f, l, p);
328 return -1;
329 }
330
331 static int
conf_getproto(const char * f,size_t l,bool local __unused,struct conf * c,const char * p)332 conf_getproto(const char *f, size_t l, bool local __unused, struct conf *c,
333 const char *p)
334 {
335 if (strcmp(p, "stream") == 0) {
336 c->c_proto = IPPROTO_TCP;
337 return 0;
338 }
339 if (strcmp(p, "dgram") == 0) {
340 c->c_proto = IPPROTO_UDP;
341 return 0;
342 }
343 return conf_getnum(f, l, local, &c->c_proto, "protocol", p);
344 }
345
346 static int
conf_getfamily(const char * f,size_t l,bool local __unused,struct conf * c,const char * p)347 conf_getfamily(const char *f, size_t l, bool local __unused, struct conf *c,
348 const char *p)
349 {
350 if (strncmp(p, "tcp", 3) == 0 || strncmp(p, "udp", 3) == 0) {
351 c->c_family = p[3] == '6' ? AF_INET6 : AF_INET;
352 return 0;
353 }
354 return conf_getnum(f, l, local, &c->c_family, "family", p);
355 }
356
357 static int
conf_getuid(const char * f,size_t l,bool local __unused,struct conf * c,const char * p)358 conf_getuid(const char *f, size_t l, bool local __unused, struct conf *c,
359 const char *p)
360 {
361 struct passwd *pw;
362
363 if ((pw = getpwnam(p)) != NULL) {
364 c->c_uid = (int)pw->pw_uid;
365 return 0;
366 }
367
368 return conf_getnum(f, l, local, &c->c_uid, "user", p);
369 }
370
371
372 static int
conf_getname(const char * f,size_t l,bool local,struct conf * c,const char * p)373 conf_getname(const char *f, size_t l, bool local, struct conf *c,
374 const char *p)
375 {
376 if (conf_getmask(f, l, local, &p, &c->c_rmask) == -1)
377 return -1;
378
379 if (strcmp(p, "*") == 0) {
380 strlcpy(c->c_name, rulename, CONFNAMESZ);
381 return 0;
382 }
383
384 if (strcmp(p, "=") == 0) {
385 if (local)
386 goto out;
387 c->c_name[0] = '\0';
388 return 0;
389 }
390
391 snprintf(c->c_name, CONFNAMESZ, "%s%s", *p == '-' ? rulename : "", p);
392 return 0;
393 out:
394 (*lfun)(LOG_ERR, "%s: %s, %zu: `=' name not allowed in local"
395 " config", __func__, f, l);
396 return -1;
397 }
398
399 static int
getvalue(const char * f,size_t l,bool local,void * r,char ** p,int (* fun)(const char *,size_t,bool,struct conf *,const char *))400 getvalue(const char *f, size_t l, bool local, void *r, char **p,
401 int (*fun)(const char *, size_t, bool, struct conf *, const char *))
402 {
403 char *ep = *p;
404
405 advance(p);
406 return (*fun)(f, l, local, r, ep);
407 }
408
409
410 static int
conf_parseline(const char * f,size_t l,char * p,struct conf * c,bool local)411 conf_parseline(const char *f, size_t l, char *p, struct conf *c, bool local)
412 {
413 int e;
414
415 c->c_lineno = l;
416
417 while (*p && isspace((unsigned char)*p))
418 p++;
419
420 memset(c, 0, sizeof(*c));
421 e = getvalue(f, l, local, c, &p, conf_gethostport);
422 if (e) return -1;
423 e = getvalue(f, l, local, c, &p, conf_getproto);
424 if (e) return -1;
425 e = getvalue(f, l, local, c, &p, conf_getfamily);
426 if (e) return -1;
427 e = getvalue(f, l, local, c, &p, conf_getuid);
428 if (e) return -1;
429 e = getvalue(f, l, local, c, &p, conf_getname);
430 if (e) return -1;
431 e = getvalue(f, l, local, c, &p, conf_getnfail);
432 if (e) return -1;
433 e = getvalue(f, l, local, c, &p, conf_getsecs);
434 if (e) return -1;
435
436 return 0;
437 }
438
439 static int
conf_sort(const void * v1,const void * v2)440 conf_sort(const void *v1, const void *v2)
441 {
442 const struct conf *c1 = v1;
443 const struct conf *c2 = v2;
444
445 #define CMP(a, b, f) \
446 if ((a)->f > (b)->f) return -1; \
447 else if ((a)->f < (b)->f) return 1
448
449 CMP(c1, c2, c_ss.ss_family);
450 CMP(c1, c2, c_lmask);
451 CMP(c1, c2, c_port);
452 CMP(c1, c2, c_proto);
453 CMP(c1, c2, c_family);
454 CMP(c1, c2, c_rmask);
455 CMP(c1, c2, c_uid);
456 #undef CMP
457 return 0;
458 }
459
460 static int
conf_is_interface(const char * name)461 conf_is_interface(const char *name)
462 {
463 const struct ifaddrs *ifa;
464
465 for (ifa = ifas; ifa; ifa = ifa->ifa_next)
466 if (strcmp(ifa->ifa_name, name) == 0)
467 return 1;
468 return 0;
469 }
470
471 #define MASK(m) ((uint32_t)~((1 << (32 - (m))) - 1))
472
473 static int
conf_amask_eq(const void * v1,const void * v2,size_t len,int mask)474 conf_amask_eq(const void *v1, const void *v2, size_t len, int mask)
475 {
476 const uint32_t *a1 = v1;
477 const uint32_t *a2 = v2;
478 uint32_t m;
479 int omask = mask;
480
481 switch (mask) {
482 case FSTAR:
483 if (memcmp(v1, v2, len) == 0)
484 return 1;
485 goto out;
486 case FEQUAL:
487 (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
488 mask);
489 abort();
490 default:
491 break;
492 }
493
494 for (size_t i = 0; i < (len >> 2); i++) {
495 if (mask > 32) {
496 m = htonl((uint32_t)~0);
497 mask -= 32;
498 } else if (mask) {
499 m = htonl(MASK(mask));
500 mask = 0;
501 } else
502 return 1;
503 if ((a1[i] & m) != (a2[i] & m))
504 goto out;
505 }
506 return 1;
507 out:
508 if (debug > 1) {
509 char b1[256], b2[256];
510 blhexdump(b1, sizeof(b1), "a1", v1, len);
511 blhexdump(b2, sizeof(b2), "a2", v2, len);
512 (*lfun)(LOG_DEBUG, "%s: %s != %s [0x%x]", __func__,
513 b1, b2, omask);
514 }
515 return 0;
516 }
517
518 /*
519 * Apply the mask to the given address
520 */
521 static void
conf_apply_mask(void * v,size_t len,int mask)522 conf_apply_mask(void *v, size_t len, int mask)
523 {
524 uint32_t *a = v;
525 uint32_t m;
526
527 switch (mask) {
528 case FSTAR:
529 return;
530 case FEQUAL:
531 (*lfun)(LOG_CRIT, "%s: Internal error: bad mask %d", __func__,
532 mask);
533 abort();
534 default:
535 break;
536 }
537 len >>= 2;
538
539 for (size_t i = 0; i < len; i++) {
540 if (mask > 32) {
541 m = htonl((uint32_t)~0);
542 mask -= 32;
543 } else if (mask) {
544 m = htonl(MASK(mask));
545 mask = 0;
546 } else
547 m = 0;
548 a[i] &= m;
549 }
550 }
551
552 /*
553 * apply the mask and the port to the address given
554 */
555 static void
conf_addr_set(struct conf * c,const struct sockaddr_storage * ss)556 conf_addr_set(struct conf *c, const struct sockaddr_storage *ss)
557 {
558 struct sockaddr_in *sin;
559 struct sockaddr_in6 *sin6;
560 in_port_t *port;
561 void *addr;
562 size_t alen;
563
564 c->c_lmask = c->c_rmask;
565 c->c_ss = *ss;
566
567 if (c->c_ss.ss_family != c->c_family) {
568 (*lfun)(LOG_CRIT, "%s: Internal error: mismatched family "
569 "%u != %u", __func__, c->c_ss.ss_family, c->c_family);
570 abort();
571 }
572
573 switch (c->c_ss.ss_family) {
574 case AF_INET:
575 sin = (void *)&c->c_ss;
576 port = &sin->sin_port;
577 addr = &sin->sin_addr;
578 alen = sizeof(sin->sin_addr);
579 break;
580 case AF_INET6:
581 sin6 = (void *)&c->c_ss;
582 port = &sin6->sin6_port;
583 addr = &sin6->sin6_addr;
584 alen = sizeof(sin6->sin6_addr);
585 break;
586 default:
587 (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
588 __func__, c->c_ss.ss_family);
589 abort();
590 }
591
592 *port = htons((in_port_t)c->c_port);
593 conf_apply_mask(addr, alen, c->c_lmask);
594 if (c->c_lmask == FSTAR)
595 c->c_lmask = (int)(alen * 8);
596 if (debug) {
597 char buf[128];
598 sockaddr_snprintf(buf, sizeof(buf), "%a:%p", (void *)&c->c_ss);
599 (*lfun)(LOG_DEBUG, "Applied address %s", buf);
600 }
601 }
602
603 /*
604 * Compared two addresses for equality applying the mask
605 */
606 static int
conf_inet_eq(const void * v1,const void * v2,int mask)607 conf_inet_eq(const void *v1, const void *v2, int mask)
608 {
609 const struct sockaddr *sa1 = v1;
610 const struct sockaddr *sa2 = v2;
611 size_t size;
612
613 if (sa1->sa_family != sa2->sa_family)
614 return 0;
615
616 switch (sa1->sa_family) {
617 case AF_INET: {
618 const struct sockaddr_in *s1 = v1;
619 const struct sockaddr_in *s2 = v2;
620 size = sizeof(s1->sin_addr);
621 v1 = &s1->sin_addr;
622 v2 = &s2->sin_addr;
623 break;
624 }
625
626 case AF_INET6: {
627 const struct sockaddr_in6 *s1 = v1;
628 const struct sockaddr_in6 *s2 = v2;
629 size = sizeof(s1->sin6_addr);
630 v1 = &s1->sin6_addr;
631 v2 = &s2->sin6_addr;
632 break;
633 }
634
635 default:
636 (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
637 __func__, sa1->sa_family);
638 abort();
639 }
640
641 return conf_amask_eq(v1, v2, size, mask);
642 }
643
644 static int
conf_addr_in_interface(const struct sockaddr_storage * s1,const struct sockaddr_storage * s2,int mask)645 conf_addr_in_interface(const struct sockaddr_storage *s1,
646 const struct sockaddr_storage *s2, int mask)
647 {
648 const char *name = SIF_NAME(s2);
649 const struct ifaddrs *ifa;
650
651 for (ifa = ifas; ifa; ifa = ifa->ifa_next) {
652 if ((ifa->ifa_flags & IFF_UP) == 0)
653 continue;
654
655 if (strcmp(ifa->ifa_name, name) != 0)
656 continue;
657
658 if (s1->ss_family != ifa->ifa_addr->sa_family)
659 continue;
660
661 bool eq;
662 switch (s1->ss_family) {
663 case AF_INET:
664 case AF_INET6:
665 eq = conf_inet_eq(ifa->ifa_addr, s1, mask);
666 break;
667 default:
668 (*lfun)(LOG_ERR, "Bad family %u", s1->ss_family);
669 continue;
670 }
671 if (eq)
672 return 1;
673 }
674 return 0;
675 }
676
677 static int
conf_addr_eq(const struct sockaddr_storage * s1,const struct sockaddr_storage * s2,int mask)678 conf_addr_eq(const struct sockaddr_storage *s1,
679 const struct sockaddr_storage *s2, int mask)
680 {
681 switch (s2->ss_family) {
682 case 0:
683 return 1;
684 case AF_MAX:
685 return conf_addr_in_interface(s1, s2, mask);
686 case AF_INET:
687 case AF_INET6:
688 return conf_inet_eq(s1, s2, mask);
689 default:
690 (*lfun)(LOG_CRIT, "%s: Internal error: bad family %u",
691 __func__, s1->ss_family);
692 abort();
693 }
694 }
695
696 static int
conf_eq(const struct conf * c1,const struct conf * c2)697 conf_eq(const struct conf *c1, const struct conf *c2)
698 {
699 if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, FSTAR))
700 return 0;
701
702 #define CMP(a, b, f) \
703 if ((a)->f != (b)->f) \
704 return 0;
705
706 CMP(c1, c2, c_port);
707 CMP(c1, c2, c_proto);
708 CMP(c1, c2, c_family);
709 CMP(c1, c2, c_uid);
710 #undef CMP
711
712 return 1;
713 }
714
715 static int
conf_match(const struct conf * c1,const struct conf * c2)716 conf_match(const struct conf *c1, const struct conf *c2)
717 {
718
719 if (!conf_addr_eq(&c1->c_ss, &c2->c_ss, c2->c_lmask))
720 return 0;
721
722 #define CMP(a, b, f) \
723 if ((a)->f != (b)->f && (b)->f != FSTAR && (b)->f != FEQUAL) { \
724 if (debug > 1) \
725 (*lfun)(LOG_DEBUG, "%s: %s fail %d != %d", __func__, \
726 __STRING(f), (a)->f, (b)->f); \
727 return 0; \
728 }
729 CMP(c1, c2, c_port);
730 CMP(c1, c2, c_proto);
731 CMP(c1, c2, c_family);
732 CMP(c1, c2, c_uid);
733 #undef CMP
734 return 1;
735 }
736
737 static const char *
conf_num(char * b,size_t l,int n)738 conf_num(char *b, size_t l, int n)
739 {
740 switch (n) {
741 case FSTAR:
742 return "*";
743 case FEQUAL:
744 return "=";
745 default:
746 snprintf(b, l, "%d", n);
747 return b;
748 }
749 }
750
751 static const char *
fmtname(const char * n)752 fmtname(const char *n) {
753 size_t l = strlen(rulename);
754 if (l == 0)
755 return "*";
756 if (strncmp(n, rulename, l) == 0) {
757 if (n[l] != '\0')
758 return n + l;
759 else
760 return "*";
761 } else if (!*n)
762 return "=";
763 else
764 return n;
765 }
766
767 static void
fmtport(char * b,size_t l,int port)768 fmtport(char *b, size_t l, int port)
769 {
770 char buf[128];
771
772 if (port == FSTAR)
773 return;
774
775 if (b[0] == '\0' || strcmp(b, "*") == 0)
776 snprintf(b, l, "%d", port);
777 else {
778 snprintf(buf, sizeof(buf), ":%d", port);
779 strlcat(b, buf, l);
780 }
781 }
782
783 static const char *
fmtmask(char * b,size_t l,int fam,int mask)784 fmtmask(char *b, size_t l, int fam, int mask)
785 {
786 char buf[128];
787
788 switch (mask) {
789 case FSTAR:
790 return "";
791 case FEQUAL:
792 if (strcmp(b, "=") == 0)
793 return "";
794 else {
795 strlcat(b, "/=", l);
796 return b;
797 }
798 default:
799 break;
800 }
801
802 switch (fam) {
803 case AF_INET:
804 if (mask == 32)
805 return "";
806 break;
807 case AF_INET6:
808 if (mask == 128)
809 return "";
810 break;
811 default:
812 break;
813 }
814
815 snprintf(buf, sizeof(buf), "/%d", mask);
816 strlcat(b, buf, l);
817 return b;
818 }
819
820 static const char *
conf_namemask(char * b,size_t l,const struct conf * c)821 conf_namemask(char *b, size_t l, const struct conf *c)
822 {
823 strlcpy(b, fmtname(c->c_name), l);
824 fmtmask(b, l, c->c_family, c->c_rmask);
825 return b;
826 }
827
828 const char *
conf_print(char * buf,size_t len,const char * pref,const char * delim,const struct conf * c)829 conf_print(char *buf, size_t len, const char *pref, const char *delim,
830 const struct conf *c)
831 {
832 char ha[128], hb[32], b[5][64];
833 int sp;
834
835 #define N(n, v) conf_num(b[n], sizeof(b[n]), (v))
836
837 switch (c->c_ss.ss_family) {
838 case 0:
839 snprintf(ha, sizeof(ha), "*");
840 break;
841 case AF_MAX:
842 snprintf(ha, sizeof(ha), "%s", SIF_NAME(&c->c_ss));
843 break;
844 default:
845 sockaddr_snprintf(ha, sizeof(ha), "%a", (const void *)&c->c_ss);
846 break;
847 }
848
849 fmtmask(ha, sizeof(ha), c->c_family, c->c_lmask);
850 fmtport(ha, sizeof(ha), c->c_port);
851
852 sp = *delim == '\t' ? 20 : -1;
853 hb[0] = '\0';
854 if (*delim)
855 snprintf(buf, len, "%s%*.*s%s%s%s" "%s%s%s%s"
856 "%s%s" "%s%s%s",
857 pref, sp, sp, ha, delim, N(0, c->c_proto), delim,
858 N(1, c->c_family), delim, N(2, c->c_uid), delim,
859 conf_namemask(hb, sizeof(hb), c), delim,
860 N(3, c->c_nfail), delim, N(4, c->c_duration));
861 else
862 snprintf(buf, len, "%starget:%s, proto:%s, family:%s, "
863 "uid:%s, name:%s, nfail:%s, duration:%s", pref,
864 ha, N(0, c->c_proto), N(1, c->c_family), N(2, c->c_uid),
865 conf_namemask(hb, sizeof(hb), c),
866 N(3, c->c_nfail), N(4, c->c_duration));
867 return buf;
868 }
869
870 /*
871 * Apply the local config match to the result
872 */
873 static void
conf_apply(struct conf * c,const struct conf * sc)874 conf_apply(struct conf *c, const struct conf *sc)
875 {
876 char buf[BUFSIZ];
877
878 if (debug) {
879 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
880 conf_print(buf, sizeof(buf), "merge:\t", "", sc));
881 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
882 conf_print(buf, sizeof(buf), "to:\t", "", c));
883 }
884 memcpy(c->c_name, sc->c_name, CONFNAMESZ);
885 c->c_uid = sc->c_uid;
886 c->c_rmask = sc->c_rmask;
887 c->c_nfail = sc->c_nfail;
888 c->c_duration = sc->c_duration;
889
890 if (debug)
891 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
892 conf_print(buf, sizeof(buf), "result:\t", "", c));
893 }
894
895 /*
896 * Merge a remote configuration to the result
897 */
898 static void
conf_merge(struct conf * c,const struct conf * sc)899 conf_merge(struct conf *c, const struct conf *sc)
900 {
901 char buf[BUFSIZ];
902
903 if (debug) {
904 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
905 conf_print(buf, sizeof(buf), "merge:\t", "", sc));
906 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
907 conf_print(buf, sizeof(buf), "to:\t", "", c));
908 }
909
910 if (sc->c_name[0])
911 memcpy(c->c_name, sc->c_name, CONFNAMESZ);
912 if (sc->c_uid != FEQUAL)
913 c->c_uid = sc->c_uid;
914 if (sc->c_rmask != FEQUAL)
915 c->c_lmask = c->c_rmask = sc->c_rmask;
916 if (sc->c_nfail != FEQUAL)
917 c->c_nfail = sc->c_nfail;
918 if (sc->c_duration != FEQUAL)
919 c->c_duration = sc->c_duration;
920 if (debug)
921 (*lfun)(LOG_DEBUG, "%s: %s", __func__,
922 conf_print(buf, sizeof(buf), "result:\t", "", c));
923 }
924
925 static void
confset_init(struct confset * cs)926 confset_init(struct confset *cs)
927 {
928 cs->cs_c = NULL;
929 cs->cs_n = 0;
930 cs->cs_m = 0;
931 }
932
933 static int
confset_grow(struct confset * cs)934 confset_grow(struct confset *cs)
935 {
936 void *tc;
937
938 cs->cs_m += 10;
939 tc = realloc(cs->cs_c, cs->cs_m * sizeof(*cs->cs_c));
940 if (tc == NULL) {
941 (*lfun)(LOG_ERR, "%s: Can't grow confset (%m)", __func__);
942 return -1;
943 }
944 cs->cs_c = tc;
945 return 0;
946 }
947
948 static struct conf *
confset_get(struct confset * cs)949 confset_get(struct confset *cs)
950 {
951 return &cs->cs_c[cs->cs_n];
952 }
953
954 static bool
confset_full(const struct confset * cs)955 confset_full(const struct confset *cs)
956 {
957 return cs->cs_n == cs->cs_m;
958 }
959
960 static void
confset_sort(struct confset * cs)961 confset_sort(struct confset *cs)
962 {
963 qsort(cs->cs_c, cs->cs_n, sizeof(*cs->cs_c), conf_sort);
964 }
965
966 static void
confset_add(struct confset * cs)967 confset_add(struct confset *cs)
968 {
969 cs->cs_n++;
970 }
971
972 static void
confset_free(struct confset * cs)973 confset_free(struct confset *cs)
974 {
975 free(cs->cs_c);
976 confset_init(cs);
977 }
978
979 static void
confset_merge(struct confset * dc,struct confset * sc)980 confset_merge(struct confset *dc, struct confset *sc)
981 {
982 size_t i, j;
983 char buf[BUFSIZ];
984
985 /* Check each rule of the src confset (sc) */
986 for (i = 0; i < sc->cs_n; i++) {
987 /* Compare to each rule in the dest confset (dc) */
988 for (j = 0; j < dc->cs_n; j++) {
989 if (conf_eq(&dc->cs_c[j], &sc->cs_c[i])) {
990 break;
991 }
992 }
993
994 if (j == dc->cs_n) {
995 /* This is a new rule to add to the dest confset. */
996 if (confset_full(dc) && confset_grow(dc) == -1)
997 return;
998
999 *confset_get(dc) = sc->cs_c[i];
1000 confset_add(dc);
1001 continue;
1002 }
1003
1004 /* We had a match above. */
1005 /*
1006 * Check whether the rule from the src confset is more
1007 * restrictive than the existing one. Adjust the
1008 * existing rule if necessary.
1009 */
1010 if (sc->cs_c[i].c_nfail == dc->cs_c[j].c_nfail &&
1011 sc->cs_c[i].c_duration && dc->cs_c[j].c_duration) {
1012 (*lfun)(LOG_DEBUG, "skipping existing rule: %s",
1013 conf_print(buf, sizeof (buf), "", "\t", &sc->cs_c[i]));
1014 continue;
1015 }
1016
1017 if (sc->cs_c[i].c_nfail < dc->cs_c[j].c_nfail)
1018 dc->cs_c[j].c_nfail = sc->cs_c[i].c_nfail;
1019
1020 if (sc->cs_c[i].c_duration > dc->cs_c[j].c_duration)
1021 dc->cs_c[j].c_duration = sc->cs_c[i].c_duration;
1022
1023 (*lfun)(LOG_DEBUG, "adjusted existing rule: %s",
1024 conf_print(buf, sizeof (buf), "", "\t", &dc->cs_c[j]));
1025 }
1026
1027 confset_free(sc);
1028 }
1029
1030 static void
confset_list(const struct confset * cs,const char * msg,const char * where)1031 confset_list(const struct confset *cs, const char *msg, const char *where)
1032 {
1033 char buf[BUFSIZ];
1034
1035 (*lfun)(LOG_DEBUG, "[%s]", msg);
1036 (*lfun)(LOG_DEBUG, "%20.20s\ttype\tproto\towner\tname\tnfail\tduration",
1037 where);
1038 for (size_t i = 0; i < cs->cs_n; i++)
1039 (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf), "", "\t",
1040 &cs->cs_c[i]));
1041 }
1042
1043 /*
1044 * Match a configuration against the given list and apply the function
1045 * to it, returning the matched entry number.
1046 */
1047 static size_t
confset_match(const struct confset * cs,struct conf * c,void (* fun)(struct conf *,const struct conf *))1048 confset_match(const struct confset *cs, struct conf *c,
1049 void (*fun)(struct conf *, const struct conf *))
1050 {
1051 char buf[BUFSIZ];
1052 size_t i;
1053
1054 for (i = 0; i < cs->cs_n; i++) {
1055 if (debug)
1056 (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
1057 "check:\t", "", &cs->cs_c[i]));
1058 if (conf_match(c, &cs->cs_c[i])) {
1059 if (debug)
1060 (*lfun)(LOG_DEBUG, "%s",
1061 conf_print(buf, sizeof(buf),
1062 "found:\t", "", &cs->cs_c[i]));
1063 (*fun)(c, &cs->cs_c[i]);
1064 break;
1065 }
1066 }
1067 return i;
1068 }
1069
1070 #ifdef AF_ROUTE
1071 static int
conf_route_perm(int fd)1072 conf_route_perm(int fd) {
1073 #if defined(RTM_IFANNOUNCE) && defined(SA_SIZE)
1074 /*
1075 * Send a routing message that is not supported to check for access
1076 * We expect EOPNOTSUPP for having access, since we are sending a
1077 * request the system does not understand and EACCES if we don't have
1078 * access.
1079 */
1080 static struct sockaddr_in sin = {
1081 #ifdef HAVE_STRUCT_SOCKADDR_SA_LEN
1082 .sin_len = sizeof(sin),
1083 #endif
1084 .sin_family = AF_INET,
1085 };
1086 char buf[4096];
1087 struct rt_msghdr *rtm = (void *)buf;
1088 char *cp = (char *)(rtm + 1);
1089 size_t l;
1090
1091 #define NEXTADDR(s) \
1092 l = SA_SIZE(sizeof(*s)); memmove(cp, s, l); cp += l;
1093 memset(buf, 0, sizeof(buf));
1094 rtm->rtm_type = RTM_IFANNOUNCE;
1095 rtm->rtm_flags = 0;
1096 rtm->rtm_addrs = RTA_DST|RTA_GATEWAY;
1097 rtm->rtm_version = RTM_VERSION;
1098 rtm->rtm_seq = 666;
1099 NEXTADDR(&sin);
1100 NEXTADDR(&sin);
1101 rtm->rtm_msglen = (u_short)((char *)cp - (char *)rtm);
1102 if (write(fd, rtm, rtm->rtm_msglen) != -1) {
1103 (*lfun)(LOG_ERR, "Writing to routing socket succeeded!");
1104 return 0;
1105 }
1106 switch (errno) {
1107 case EACCES:
1108 return 0;
1109 case EOPNOTSUPP:
1110 return 1;
1111 default:
1112 (*lfun)(LOG_ERR,
1113 "Unexpected error writing to routing socket (%m)");
1114 return 0;
1115 }
1116 #else
1117 return 0;
1118 #endif
1119 }
1120 #endif
1121
1122 static int
conf_handle_inet(int fd,const void * lss,struct conf * cr)1123 conf_handle_inet(int fd, const void *lss, struct conf *cr)
1124 {
1125 char buf[BUFSIZ];
1126 int proto;
1127 socklen_t slen = sizeof(proto);
1128
1129 if (getsockopt(fd, SOL_SOCKET, SO_TYPE, &proto, &slen) == -1) {
1130 (*lfun)(LOG_ERR, "getsockopt failed (%m)");
1131 return -1;
1132 }
1133
1134 if (debug) {
1135 sockaddr_snprintf(buf, sizeof(buf), "%a:%p", lss);
1136 (*lfun)(LOG_DEBUG, "listening socket: %s", buf);
1137 }
1138
1139 switch (proto) {
1140 case SOCK_STREAM:
1141 cr->c_proto = IPPROTO_TCP;
1142 break;
1143 case SOCK_DGRAM:
1144 cr->c_proto = IPPROTO_UDP;
1145 break;
1146 default:
1147 (*lfun)(LOG_ERR, "unsupported protocol %d", proto);
1148 return -1;
1149 }
1150 return 0;
1151 }
1152
1153 const struct conf *
conf_find(int fd,uid_t uid,const struct sockaddr_storage * rss,struct conf * cr)1154 conf_find(int fd, uid_t uid, const struct sockaddr_storage *rss,
1155 struct conf *cr)
1156 {
1157 socklen_t slen;
1158 struct sockaddr_storage lss;
1159 size_t i;
1160 char buf[BUFSIZ];
1161
1162 memset(cr, 0, sizeof(*cr));
1163 slen = sizeof(lss);
1164 memset(&lss, 0, slen);
1165 if (getsockname(fd, (void *)&lss, &slen) == -1) {
1166 (*lfun)(LOG_ERR, "getsockname failed (%m)");
1167 return NULL;
1168 }
1169
1170 switch (lss.ss_family) {
1171 case AF_INET:
1172 cr->c_port = ntohs(((struct sockaddr_in *)&lss)->sin_port);
1173 if (conf_handle_inet(fd, &lss, cr) == -1)
1174 return NULL;
1175 break;
1176 case AF_INET6:
1177 cr->c_port = ntohs(((struct sockaddr_in6 *)&lss)->sin6_port);
1178 if (conf_handle_inet(fd, &lss, cr) == -1)
1179 return NULL;
1180 break;
1181 #ifdef AF_ROUTE
1182 case AF_ROUTE:
1183 if (!conf_route_perm(fd)) {
1184 (*lfun)(LOG_ERR,
1185 "permission denied to routing socket (%m)");
1186 return NULL;
1187 }
1188 cr->c_proto = FSTAR;
1189 cr->c_port = FSTAR;
1190 memcpy(&lss, rss, sizeof(lss));
1191 break;
1192 #endif
1193 default:
1194 (*lfun)(LOG_ERR, "unsupported family %d", lss.ss_family);
1195 return NULL;
1196 }
1197
1198 cr->c_ss = lss;
1199 cr->c_lmask = FSTAR;
1200 cr->c_uid = (int)uid;
1201 cr->c_family = lss.ss_family;
1202 cr->c_name[0] = '\0';
1203 cr->c_rmask = FSTAR;
1204 cr->c_nfail = FSTAR;
1205 cr->c_duration = FSTAR;
1206
1207 if (debug)
1208 (*lfun)(LOG_DEBUG, "%s", conf_print(buf, sizeof(buf),
1209 "look:\t", "", cr));
1210
1211 /* match the local config */
1212 i = confset_match(&lconf, cr, conf_apply);
1213 if (i == lconf.cs_n) {
1214 if (debug)
1215 (*lfun)(LOG_DEBUG, "not found");
1216 return NULL;
1217 }
1218
1219 conf_addr_set(cr, rss);
1220 /* match the remote config */
1221 confset_match(&rconf, cr, conf_merge);
1222 /* to apply the mask */
1223 conf_addr_set(cr, &cr->c_ss);
1224
1225 return cr;
1226 }
1227
1228 static void
conf_parsefile(FILE * fp,const char * config_file)1229 conf_parsefile(FILE *fp, const char *config_file)
1230 {
1231 char *line;
1232 size_t lineno, len;
1233 struct confset lc, rc, *cs;
1234
1235 lineno = 0;
1236
1237 confset_init(&rc);
1238 confset_init(&lc);
1239 cs = &lc;
1240 for (; (line = fparseln(fp, &len, &lineno, NULL, 0)) != NULL;
1241 free(line))
1242 {
1243 if (!*line)
1244 continue;
1245 if (strcmp(line, "[local]") == 0) {
1246 cs = &lc;
1247 continue;
1248 }
1249 if (strcmp(line, "[remote]") == 0) {
1250 cs = &rc;
1251 continue;
1252 }
1253
1254 if (confset_full(cs)) {
1255 if (confset_grow(cs) == -1) {
1256 confset_free(&lc);
1257 confset_free(&rc);
1258 free(line);
1259 return;
1260 }
1261 }
1262 if (conf_parseline(config_file, lineno, line, confset_get(cs),
1263 cs == &lc) == -1)
1264 continue;
1265 confset_add(cs);
1266 }
1267
1268 confset_merge(&rconf, &rc);
1269 confset_merge(&lconf, &lc);
1270 }
1271
1272
1273 static void
conf_parsedir(DIR * dir,const char * config_path)1274 conf_parsedir(DIR *dir, const char *config_path)
1275 {
1276 long path_max;
1277 struct dirent *dent;
1278 char *path;
1279 FILE *fp;
1280
1281 if ((path_max = pathconf(config_path, _PC_PATH_MAX)) == -1)
1282 path_max = 2048;
1283
1284 if ((path = malloc((size_t)path_max)) == NULL) {
1285 (*lfun)(LOG_ERR, "%s: Failed to allocate memory for path (%m)",
1286 __func__);
1287 return;
1288 }
1289
1290 while ((dent = readdir(dir)) != NULL) {
1291 if (strcmp(dent->d_name, ".") == 0 ||
1292 strcmp(dent->d_name, "..") == 0)
1293 continue;
1294
1295 (void) snprintf(path, (size_t)path_max, "%s/%s", config_path,
1296 dent->d_name);
1297 if ((fp = fopen(path, "r")) == NULL) {
1298 (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__,
1299 path);
1300 continue;
1301 }
1302 conf_parsefile(fp, path);
1303 fclose(fp);
1304 }
1305
1306 free(path);
1307 }
1308
1309 void
conf_parse(const char * config_path)1310 conf_parse(const char *config_path)
1311 {
1312 char *path;
1313 DIR *dir;
1314 FILE *fp;
1315
1316 if ((dir = opendir(config_path)) != NULL) {
1317 /*
1318 * If config_path is a directory, parse the configuration files
1319 * in the directory. Then we're done here.
1320 */
1321 conf_parsedir(dir, config_path);
1322 closedir(dir);
1323 goto out;
1324 } else if ((fp = fopen(config_path, "r")) != NULL) {
1325 /* If config_path is a file, parse it. */
1326 conf_parsefile(fp, config_path);
1327 fclose(fp);
1328 }
1329
1330 /*
1331 * Append ".d" to config_path, and if that is a directory, parse the
1332 * configuration files in the directory.
1333 */
1334 if (asprintf(&path, "%s.d", config_path) < 0) {
1335 (*lfun)(LOG_ERR, "%s: Failed to allocate memory for path (%m)",
1336 __func__);
1337 goto out;
1338 }
1339
1340 if ((dir = opendir(path)) != NULL) {
1341 conf_parsedir(dir, path);
1342 closedir(dir);
1343 }
1344 free(path);
1345
1346 out:
1347 if (dir == NULL && fp == NULL) {
1348 (*lfun)(LOG_ERR, "%s: Cannot open `%s' (%m)", __func__,
1349 config_path);
1350 return;
1351 }
1352
1353 confset_sort(&lconf);
1354 confset_sort(&rconf);
1355
1356 if (debug) {
1357 confset_list(&lconf, "local", "target");
1358 confset_list(&rconf, "remote", "source");
1359 }
1360 }
1361