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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 * 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 * 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 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 * 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 * 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 * 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 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 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 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 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 * 949 confset_get(struct confset *cs) 950 { 951 return &cs->cs_c[cs->cs_n]; 952 } 953 954 static bool 955 confset_full(const struct confset *cs) 956 { 957 return cs->cs_n == cs->cs_m; 958 } 959 960 static void 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 967 confset_add(struct confset *cs) 968 { 969 cs->cs_n++; 970 } 971 972 static void 973 confset_free(struct confset *cs) 974 { 975 free(cs->cs_c); 976 confset_init(cs); 977 } 978 979 static void 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 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 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 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 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 * 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 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 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 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