1 /* $OpenBSD: table.c,v 1.54 2024/06/09 10:13:05 gilles Exp $ */ 2 3 /* 4 * Copyright (c) 2013 Eric Faurot <eric@openbsd.org> 5 * Copyright (c) 2008 Gilles Chehade <gilles@poolp.org> 6 * 7 * Permission to use, copy, modify, and distribute this software for any 8 * purpose with or without fee is hereby granted, provided that the above 9 * copyright notice and this permission notice appear in all copies. 10 * 11 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES 12 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF 13 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR 14 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES 15 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN 16 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF 17 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. 18 */ 19 20 #include <sys/stat.h> 21 22 #include <net/if.h> 23 24 #include <arpa/inet.h> 25 #include <errno.h> 26 #include <regex.h> 27 #include <stdlib.h> 28 #include <string.h> 29 30 #include "smtpd.h" 31 #include "log.h" 32 33 struct table_backend *table_backend_lookup(const char *); 34 35 extern struct table_backend table_backend_static; 36 extern struct table_backend table_backend_db; 37 extern struct table_backend table_backend_getpwnam; 38 extern struct table_backend table_backend_proc; 39 40 static int table_parse_lookup(enum table_service, const char *, const char *, 41 union lookup *); 42 static int parse_sockaddr(struct sockaddr *, int, const char *); 43 44 static unsigned int last_table_id = 0; 45 46 static struct table_backend *backends[] = { 47 &table_backend_static, 48 &table_backend_db, 49 &table_backend_getpwnam, 50 &table_backend_proc, 51 NULL 52 }; 53 54 struct table_backend * 55 table_backend_lookup(const char *backend) 56 { 57 int i; 58 59 if (!strcmp(backend, "file")) 60 backend = "static"; 61 62 for (i = 0; backends[i]; i++) 63 if (!strcmp(backends[i]->name, backend)) 64 return (backends[i]); 65 66 return NULL; 67 } 68 69 const char * 70 table_service_name(enum table_service s) 71 { 72 switch (s) { 73 case K_NONE: return "none"; 74 case K_ALIAS: return "alias"; 75 case K_DOMAIN: return "domain"; 76 case K_CREDENTIALS: return "credentials"; 77 case K_NETADDR: return "netaddr"; 78 case K_USERINFO: return "userinfo"; 79 case K_SOURCE: return "source"; 80 case K_MAILADDR: return "mailaddr"; 81 case K_ADDRNAME: return "addrname"; 82 case K_MAILADDRMAP: return "mailaddrmap"; 83 case K_RELAYHOST: return "relayhost"; 84 case K_STRING: return "string"; 85 case K_REGEX: return "regex"; 86 case K_AUTH: return "auth"; 87 } 88 return "???"; 89 } 90 91 int 92 table_service_from_name(const char *service) 93 { 94 if (!strcmp(service, "none")) 95 return K_NONE; 96 if (!strcmp(service, "alias")) 97 return K_ALIAS; 98 if (!strcmp(service, "domain")) 99 return K_DOMAIN; 100 if (!strcmp(service, "credentials")) 101 return K_CREDENTIALS; 102 if (!strcmp(service, "netaddr")) 103 return K_NETADDR; 104 if (!strcmp(service, "userinfo")) 105 return K_USERINFO; 106 if (!strcmp(service, "source")) 107 return K_SOURCE; 108 if (!strcmp(service, "mailaddr")) 109 return K_MAILADDR; 110 if (!strcmp(service, "addrname")) 111 return K_ADDRNAME; 112 if (!strcmp(service, "mailaddrmap")) 113 return K_MAILADDRMAP; 114 if (!strcmp(service, "relayhost")) 115 return K_RELAYHOST; 116 if (!strcmp(service, "string")) 117 return K_STRING; 118 if (!strcmp(service, "regex")) 119 return K_REGEX; 120 if (!strcmp(service, "auth")) 121 return K_AUTH; 122 return (-1); 123 } 124 125 struct table * 126 table_find(struct smtpd *conf, const char *name) 127 { 128 return dict_get(conf->sc_tables_dict, name); 129 } 130 131 int 132 table_match(struct table *table, enum table_service kind, const char *key) 133 { 134 return table_lookup(table, kind, key, NULL); 135 } 136 137 int 138 table_lookup(struct table *table, enum table_service kind, const char *key, 139 union lookup *lk) 140 { 141 char lkey[1024], *buf = NULL; 142 int r; 143 144 r = -1; 145 if (table->t_backend->lookup == NULL) 146 errno = ENOTSUP; 147 else if (!lowercase(lkey, key, sizeof lkey)) { 148 log_warnx("warn: lookup key too long: %s", key); 149 errno = EINVAL; 150 } 151 else 152 r = table->t_backend->lookup(table, kind, lkey, lk ? &buf : NULL); 153 154 if (r == 1) { 155 log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s%s", 156 lk ? "lookup" : "match", 157 key, 158 table_service_name(kind), 159 table->t_backend->name, 160 table->t_name, 161 lk ? "\"" : "", 162 lk ? buf : "true", 163 lk ? "\"" : ""); 164 if (buf) 165 r = table_parse_lookup(kind, lkey, buf, lk); 166 } 167 else 168 log_trace(TRACE_LOOKUP, "lookup: %s \"%s\" as %s in table %s:%s -> %s%s", 169 lk ? "lookup" : "match", 170 key, 171 table_service_name(kind), 172 table->t_backend->name, 173 table->t_name, 174 (r == -1) ? "error: " : (lk ? "none" : "false"), 175 (r == -1) ? strerror(errno) : ""); 176 177 free(buf); 178 179 return (r); 180 } 181 182 int 183 table_fetch(struct table *table, enum table_service kind, union lookup *lk) 184 { 185 char *buf = NULL; 186 int r; 187 188 r = -1; 189 if (table->t_backend->fetch == NULL) 190 errno = ENOTSUP; 191 else 192 r = table->t_backend->fetch(table, kind, &buf); 193 194 if (r == 1) { 195 log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> \"%s\"", 196 table_service_name(kind), 197 table->t_backend->name, 198 table->t_name, 199 buf); 200 r = table_parse_lookup(kind, NULL, buf, lk); 201 } 202 else 203 log_trace(TRACE_LOOKUP, "lookup: fetch %s from table %s:%s -> %s%s", 204 table_service_name(kind), 205 table->t_backend->name, 206 table->t_name, 207 (r == -1) ? "error: " : "none", 208 (r == -1) ? strerror(errno) : ""); 209 210 free(buf); 211 212 return (r); 213 } 214 215 struct table * 216 table_create(struct smtpd *conf, const char *backend, const char *name, 217 const char *config) 218 { 219 struct table *t; 220 struct table_backend *tb; 221 char path[LINE_MAX]; 222 size_t n; 223 struct stat sb; 224 225 if (name && table_find(conf, name)) 226 fatalx("table_create: table \"%s\" already defined", name); 227 228 if ((tb = table_backend_lookup(backend)) == NULL) { 229 if ((size_t)snprintf(path, sizeof(path), PATH_LIBEXEC"/table-%s", 230 backend) >= sizeof(path)) { 231 fatalx("table_create: path too long \"" 232 PATH_LIBEXEC"/table-%s\"", backend); 233 } 234 if (stat(path, &sb) == 0) { 235 tb = table_backend_lookup("proc"); 236 (void)strlcpy(path, backend, sizeof(path)); 237 if (config) { 238 (void)strlcat(path, ":", sizeof(path)); 239 if (strlcat(path, config, sizeof(path)) 240 >= sizeof(path)) 241 fatalx("table_create: config file path too long"); 242 } 243 config = path; 244 } 245 } 246 247 if (tb == NULL) 248 fatalx("table_create: backend \"%s\" does not exist", backend); 249 250 t = xcalloc(1, sizeof(*t)); 251 t->t_services = tb->services; 252 t->t_backend = tb; 253 254 if (config) { 255 if (strlcpy(t->t_config, config, sizeof t->t_config) 256 >= sizeof t->t_config) 257 fatalx("table_create: table config \"%s\" too large", 258 t->t_config); 259 } 260 261 if (strcmp(tb->name, "static") != 0) 262 t->t_type = T_DYNAMIC; 263 264 if (name == NULL) 265 (void)snprintf(t->t_name, sizeof(t->t_name), "<dynamic:%u>", 266 last_table_id++); 267 else { 268 n = strlcpy(t->t_name, name, sizeof(t->t_name)); 269 if (n >= sizeof(t->t_name)) 270 fatalx("table_create: table name too long"); 271 } 272 273 dict_set(conf->sc_tables_dict, t->t_name, t); 274 275 return (t); 276 } 277 278 void 279 table_destroy(struct smtpd *conf, struct table *t) 280 { 281 dict_xpop(conf->sc_tables_dict, t->t_name); 282 free(t); 283 } 284 285 int 286 table_config(struct table *t) 287 { 288 if (t->t_backend->config == NULL) 289 return (1); 290 return (t->t_backend->config(t)); 291 } 292 293 void 294 table_add(struct table *t, const char *key, const char *val) 295 { 296 if (t->t_backend->add == NULL) 297 fatalx("table_add: cannot add to table"); 298 299 if (t->t_backend->add(t, key, val) == 0) 300 log_warnx("warn: failed to add \"%s\" in table \"%s\"", key, t->t_name); 301 } 302 303 void 304 table_dump(struct table *t) 305 { 306 const char *type; 307 char buf[LINE_MAX]; 308 309 switch(t->t_type) { 310 case T_NONE: 311 type = "NONE"; 312 break; 313 case T_DYNAMIC: 314 type = "DYNAMIC"; 315 break; 316 case T_LIST: 317 type = "LIST"; 318 break; 319 case T_HASH: 320 type = "HASH"; 321 break; 322 default: 323 type = "???"; 324 break; 325 } 326 327 if (t->t_config[0]) 328 snprintf(buf, sizeof(buf), " config=\"%s\"", t->t_config); 329 else 330 buf[0] = '\0'; 331 332 log_debug("TABLE \"%s\" backend=%s type=%s%s", t->t_name, 333 t->t_backend->name, type, buf); 334 335 if (t->t_backend->dump) 336 t->t_backend->dump(t); 337 } 338 339 int 340 table_check_type(struct table *t, uint32_t mask) 341 { 342 return t->t_type & mask; 343 } 344 345 int 346 table_check_service(struct table *t, uint32_t mask) 347 { 348 return t->t_services & mask; 349 } 350 351 int 352 table_check_use(struct table *t, uint32_t tmask, uint32_t smask) 353 { 354 return table_check_type(t, tmask) && table_check_service(t, smask); 355 } 356 357 int 358 table_open(struct table *t) 359 { 360 if (t->t_backend->open == NULL) 361 return (1); 362 return (t->t_backend->open(t)); 363 } 364 365 void 366 table_close(struct table *t) 367 { 368 if (t->t_backend->close) 369 t->t_backend->close(t); 370 } 371 372 int 373 table_update(struct table *t) 374 { 375 if (t->t_backend->update == NULL) 376 return (1); 377 return (t->t_backend->update(t)); 378 } 379 380 381 /* 382 * quick reminder: 383 * in *_match() s1 comes from session, s2 comes from table 384 */ 385 386 int 387 table_domain_match(const char *s1, const char *s2) 388 { 389 return hostname_match(s1, s2); 390 } 391 392 int 393 table_mailaddr_match(const char *s1, const char *s2) 394 { 395 struct mailaddr m1; 396 struct mailaddr m2; 397 398 if (!text_to_mailaddr(&m1, s1)) 399 return 0; 400 if (!text_to_mailaddr(&m2, s2)) 401 return 0; 402 return mailaddr_match(&m1, &m2); 403 } 404 405 static int table_match_mask(struct sockaddr_storage *, struct netaddr *); 406 static int table_inet4_match(struct sockaddr_in *, struct netaddr *); 407 static int table_inet6_match(struct sockaddr_in6 *, struct netaddr *); 408 409 int 410 table_netaddr_match(const char *s1, const char *s2) 411 { 412 struct netaddr n1; 413 struct netaddr n2; 414 415 if (strcasecmp(s1, s2) == 0) 416 return 1; 417 if (!text_to_netaddr(&n1, s1)) 418 return 0; 419 if (!text_to_netaddr(&n2, s2)) 420 return 0; 421 if (n1.ss.ss_family != n2.ss.ss_family) 422 return 0; 423 if (n1.ss.ss_len != n2.ss.ss_len) 424 return 0; 425 return table_match_mask(&n1.ss, &n2); 426 } 427 428 static int 429 table_match_mask(struct sockaddr_storage *ss, struct netaddr *ssmask) 430 { 431 if (ss->ss_family == AF_INET) 432 return table_inet4_match((struct sockaddr_in *)ss, ssmask); 433 434 if (ss->ss_family == AF_INET6) 435 return table_inet6_match((struct sockaddr_in6 *)ss, ssmask); 436 437 return (0); 438 } 439 440 static int 441 table_inet4_match(struct sockaddr_in *ss, struct netaddr *ssmask) 442 { 443 in_addr_t mask; 444 int i; 445 446 /* a.b.c.d/8 -> htonl(0xff000000) */ 447 mask = 0; 448 for (i = 0; i < ssmask->bits; ++i) 449 mask = (mask >> 1) | 0x80000000; 450 mask = htonl(mask); 451 452 /* (addr & mask) == (net & mask) */ 453 if ((ss->sin_addr.s_addr & mask) == 454 (((struct sockaddr_in *)ssmask)->sin_addr.s_addr & mask)) 455 return 1; 456 457 return 0; 458 } 459 460 static int 461 table_inet6_match(struct sockaddr_in6 *ss, struct netaddr *ssmask) 462 { 463 struct in6_addr *in; 464 struct in6_addr *inmask; 465 struct in6_addr mask; 466 int i; 467 468 memset(&mask, 0, sizeof(mask)); 469 for (i = 0; i < ssmask->bits / 8; i++) 470 mask.s6_addr[i] = 0xff; 471 i = ssmask->bits % 8; 472 if (i) 473 mask.s6_addr[ssmask->bits / 8] = 0xff00 >> i; 474 475 in = &ss->sin6_addr; 476 inmask = &((struct sockaddr_in6 *)&ssmask->ss)->sin6_addr; 477 478 for (i = 0; i < 16; i++) { 479 if ((in->s6_addr[i] & mask.s6_addr[i]) != 480 (inmask->s6_addr[i] & mask.s6_addr[i])) 481 return (0); 482 } 483 484 return (1); 485 } 486 487 int 488 table_regex_match(const char *string, const char *pattern) 489 { 490 regex_t preg; 491 int cflags = REG_EXTENDED|REG_NOSUB; 492 int ret; 493 494 if (strncmp(pattern, "(?i)", 4) == 0) { 495 cflags |= REG_ICASE; 496 pattern += 4; 497 } 498 499 if (regcomp(&preg, pattern, cflags) != 0) 500 return (0); 501 502 ret = regexec(&preg, string, 0, NULL, 0); 503 504 regfree(&preg); 505 506 if (ret != 0) 507 return (0); 508 509 return (1); 510 } 511 512 void 513 table_dump_all(struct smtpd *conf) 514 { 515 struct table *t; 516 void *iter; 517 518 iter = NULL; 519 while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t)) 520 table_dump(t); 521 } 522 523 void 524 table_open_all(struct smtpd *conf) 525 { 526 struct table *t; 527 void *iter; 528 529 iter = NULL; 530 while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t)) 531 if (!table_open(t)) 532 fatalx("failed to open table %s", t->t_name); 533 } 534 535 void 536 table_close_all(struct smtpd *conf) 537 { 538 struct table *t; 539 void *iter; 540 541 iter = NULL; 542 while (dict_iter(conf->sc_tables_dict, &iter, NULL, (void **)&t)) 543 table_close(t); 544 } 545 546 static int 547 table_parse_lookup(enum table_service service, const char *key, 548 const char *line, union lookup *lk) 549 { 550 char buffer[LINE_MAX], *p; 551 size_t len; 552 553 len = strlen(line); 554 555 switch (service) { 556 case K_ALIAS: 557 lk->expand = calloc(1, sizeof(*lk->expand)); 558 if (lk->expand == NULL) 559 return (-1); 560 if (!expand_line(lk->expand, line, 1)) { 561 expand_free(lk->expand); 562 return (-1); 563 } 564 return (1); 565 566 case K_DOMAIN: 567 if (strlcpy(lk->domain.name, line, sizeof(lk->domain.name)) 568 >= sizeof(lk->domain.name)) 569 return (-1); 570 return (1); 571 572 case K_CREDENTIALS: 573 574 /* credentials are stored as user:password */ 575 if (len < 3) 576 return (-1); 577 578 /* too big to fit in a smtp session line */ 579 if (len >= LINE_MAX) 580 return (-1); 581 582 p = strchr(line, ':'); 583 if (p == NULL) { 584 if (strlcpy(lk->creds.username, key, sizeof (lk->creds.username)) 585 >= sizeof (lk->creds.username)) 586 return (-1); 587 if (strlcpy(lk->creds.password, line, sizeof(lk->creds.password)) 588 >= sizeof(lk->creds.password)) 589 return (-1); 590 return (1); 591 } 592 593 if (p == line || p == line + len - 1) 594 return (-1); 595 596 memmove(lk->creds.username, line, p - line); 597 lk->creds.username[p - line] = '\0'; 598 599 if (strlcpy(lk->creds.password, p+1, sizeof(lk->creds.password)) 600 >= sizeof(lk->creds.password)) 601 return (-1); 602 603 return (1); 604 605 case K_NETADDR: 606 if (!text_to_netaddr(&lk->netaddr, line)) 607 return (-1); 608 return (1); 609 610 case K_USERINFO: 611 if (!bsnprintf(buffer, sizeof(buffer), "%s:%s", key, line)) 612 return (-1); 613 if (!text_to_userinfo(&lk->userinfo, buffer)) 614 return (-1); 615 return (1); 616 617 case K_SOURCE: 618 if (parse_sockaddr((struct sockaddr *)&lk->source.addr, 619 PF_UNSPEC, line) == -1) 620 return (-1); 621 return (1); 622 623 case K_MAILADDR: 624 if (!text_to_mailaddr(&lk->mailaddr, line)) 625 return (-1); 626 return (1); 627 628 case K_MAILADDRMAP: 629 lk->maddrmap = calloc(1, sizeof(*lk->maddrmap)); 630 if (lk->maddrmap == NULL) 631 return (-1); 632 maddrmap_init(lk->maddrmap); 633 if (!mailaddr_line(lk->maddrmap, line)) { 634 maddrmap_free(lk->maddrmap); 635 return (-1); 636 } 637 return (1); 638 639 case K_ADDRNAME: 640 if (parse_sockaddr((struct sockaddr *)&lk->addrname.addr, 641 PF_UNSPEC, key) == -1) 642 return (-1); 643 if (strlcpy(lk->addrname.name, line, sizeof(lk->addrname.name)) 644 >= sizeof(lk->addrname.name)) 645 return (-1); 646 return (1); 647 648 case K_RELAYHOST: 649 if (strlcpy(lk->relayhost, line, sizeof(lk->relayhost)) 650 >= sizeof(lk->relayhost)) 651 return (-1); 652 return (1); 653 654 default: 655 return (-1); 656 } 657 } 658 659 static int 660 parse_sockaddr(struct sockaddr *sa, int family, const char *str) 661 { 662 struct in_addr ina; 663 struct in6_addr in6a; 664 struct sockaddr_in *sin; 665 struct sockaddr_in6 *sin6; 666 char *cp; 667 char addr[NI_MAXHOST]; 668 const char *errstr; 669 670 switch (family) { 671 case PF_UNSPEC: 672 if (parse_sockaddr(sa, PF_INET, str) == 0) 673 return (0); 674 return parse_sockaddr(sa, PF_INET6, str); 675 676 case PF_INET: 677 if (inet_pton(PF_INET, str, &ina) != 1) 678 return (-1); 679 680 sin = (struct sockaddr_in *)sa; 681 memset(sin, 0, sizeof *sin); 682 sin->sin_len = sizeof(struct sockaddr_in); 683 sin->sin_family = PF_INET; 684 sin->sin_addr.s_addr = ina.s_addr; 685 return (0); 686 687 case PF_INET6: 688 if (*str == '[') 689 str++; 690 if (!strncasecmp("ipv6:", str, 5)) 691 str += 5; 692 693 if (strlcpy(addr, str, sizeof(addr)) >= sizeof(addr)) 694 return (-1); 695 if ((cp = strchr(addr, ']')) != NULL) 696 *cp = '\0'; 697 if ((cp = strchr(addr, SCOPE_DELIMITER)) != NULL) 698 *cp++ = '\0'; 699 700 if (inet_pton(PF_INET6, addr, &in6a) != 1) 701 return (-1); 702 703 sin6 = (struct sockaddr_in6 *)sa; 704 memset(sin6, 0, sizeof *sin6); 705 sin6->sin6_len = sizeof(struct sockaddr_in6); 706 sin6->sin6_family = PF_INET6; 707 sin6->sin6_addr = in6a; 708 709 if (cp == NULL) 710 return (0); 711 712 if (IN6_IS_ADDR_LINKLOCAL(&in6a) || 713 IN6_IS_ADDR_MC_LINKLOCAL(&in6a) || 714 IN6_IS_ADDR_MC_INTFACELOCAL(&in6a)) 715 if ((sin6->sin6_scope_id = if_nametoindex(cp))) 716 return (0); 717 718 sin6->sin6_scope_id = strtonum(cp, 0, UINT32_MAX, &errstr); 719 if (errstr) 720 return (-1); 721 return (0); 722 723 default: 724 break; 725 } 726 727 return (-1); 728 } 729