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