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