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