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