1 /* 2 * Copyright (c) 1998-2003 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 #include <sendmail.h> 15 16 SM_RCSID("@(#)$Id: map.c,v 8.645.2.10 2003/07/24 18:24:17 ca Exp $") 17 18 #if LDAPMAP 19 # include <sm/ldap.h> 20 #endif /* LDAPMAP */ 21 22 #if NDBM 23 # include <ndbm.h> 24 # ifdef R_FIRST 25 ERROR README: You are running the Berkeley DB version of ndbm.h. See 26 ERROR README: the README file about tweaking Berkeley DB so it can 27 ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile 28 ERROR README: and use -DNEWDB instead. 29 # endif /* R_FIRST */ 30 #endif /* NDBM */ 31 #if NEWDB 32 # include "sm/bdb.h" 33 #endif /* NEWDB */ 34 #if NIS 35 struct dom_binding; /* forward reference needed on IRIX */ 36 # include <rpcsvc/ypclnt.h> 37 # if NDBM 38 # define NDBM_YP_COMPAT /* create YP-compatible NDBM files */ 39 # endif /* NDBM */ 40 #endif /* NIS */ 41 42 #if NEWDB 43 # if DB_VERSION_MAJOR < 2 44 static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *)); 45 # endif /* DB_VERSION_MAJOR < 2 */ 46 # if DB_VERSION_MAJOR == 2 47 static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *)); 48 # endif /* DB_VERSION_MAJOR == 2 */ 49 # if DB_VERSION_MAJOR > 2 50 static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **)); 51 # endif /* DB_VERSION_MAJOR > 2 */ 52 #endif /* NEWDB */ 53 static bool extract_canonname __P((char *, char *, char *, char[], int)); 54 static void map_close __P((STAB *, int)); 55 static void map_init __P((STAB *, int)); 56 #ifdef LDAPMAP 57 static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *)); 58 #endif /* LDAPMAP */ 59 #if NISPLUS 60 static bool nisplus_getcanonname __P((char *, int, int *)); 61 #endif /* NISPLUS */ 62 #if NIS 63 static bool nis_getcanonname __P((char *, int, int *)); 64 #endif /* NIS */ 65 #if NETINFO 66 static bool ni_getcanonname __P((char *, int, int *)); 67 #endif /* NETINFO */ 68 static bool text_getcanonname __P((char *, int, int *)); 69 70 /* default error message for trying to open a map in write mode */ 71 #ifdef ENOSYS 72 # define SM_EMAPCANTWRITE ENOSYS 73 #else /* ENOSYS */ 74 # ifdef EFTYPE 75 # define SM_EMAPCANTWRITE EFTYPE 76 # else /* EFTYPE */ 77 # define SM_EMAPCANTWRITE ENXIO 78 # endif /* EFTYPE */ 79 #endif /* ENOSYS */ 80 81 /* 82 ** MAP.C -- implementations for various map classes. 83 ** 84 ** Each map class implements a series of functions: 85 ** 86 ** bool map_parse(MAP *map, char *args) 87 ** Parse the arguments from the config file. Return true 88 ** if they were ok, false otherwise. Fill in map with the 89 ** values. 90 ** 91 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat) 92 ** Look up the key in the given map. If found, do any 93 ** rewriting the map wants (including "args" if desired) 94 ** and return the value. Set *pstat to the appropriate status 95 ** on error and return NULL. Args will be NULL if called 96 ** from the alias routines, although this should probably 97 ** not be relied upon. It is suggested you call map_rewrite 98 ** to return the results -- it takes care of null termination 99 ** and uses a dynamically expanded buffer as needed. 100 ** 101 ** void map_store(MAP *map, char *key, char *value) 102 ** Store the key:value pair in the map. 103 ** 104 ** bool map_open(MAP *map, int mode) 105 ** Open the map for the indicated mode. Mode should 106 ** be either O_RDONLY or O_RDWR. Return true if it 107 ** was opened successfully, false otherwise. If the open 108 ** failed and the MF_OPTIONAL flag is not set, it should 109 ** also print an error. If the MF_ALIAS bit is set 110 ** and this map class understands the @:@ convention, it 111 ** should call aliaswait() before returning. 112 ** 113 ** void map_close(MAP *map) 114 ** Close the map. 115 ** 116 ** This file also includes the implementation for getcanonname. 117 ** It is currently implemented in a pretty ad-hoc manner; it ought 118 ** to be more properly integrated into the map structure. 119 */ 120 121 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL 122 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */ 123 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */ 124 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */ 125 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */ 126 127 /* 128 ** MAP_PARSEARGS -- parse config line arguments for database lookup 129 ** 130 ** This is a generic version of the map_parse method. 131 ** 132 ** Parameters: 133 ** map -- the map being initialized. 134 ** ap -- a pointer to the args on the config line. 135 ** 136 ** Returns: 137 ** true -- if everything parsed OK. 138 ** false -- otherwise. 139 ** 140 ** Side Effects: 141 ** null terminates the filename; stores it in map 142 */ 143 144 bool 145 map_parseargs(map, ap) 146 MAP *map; 147 char *ap; 148 { 149 register char *p = ap; 150 151 /* 152 ** There is no check whether there is really an argument, 153 ** but that's not important enough to warrant extra code. 154 */ 155 156 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 157 map->map_spacesub = SpaceSub; /* default value */ 158 for (;;) 159 { 160 while (isascii(*p) && isspace(*p)) 161 p++; 162 if (*p != '-') 163 break; 164 switch (*++p) 165 { 166 case 'N': 167 map->map_mflags |= MF_INCLNULL; 168 map->map_mflags &= ~MF_TRY0NULL; 169 break; 170 171 case 'O': 172 map->map_mflags &= ~MF_TRY1NULL; 173 break; 174 175 case 'o': 176 map->map_mflags |= MF_OPTIONAL; 177 break; 178 179 case 'f': 180 map->map_mflags |= MF_NOFOLDCASE; 181 break; 182 183 case 'm': 184 map->map_mflags |= MF_MATCHONLY; 185 break; 186 187 case 'A': 188 map->map_mflags |= MF_APPEND; 189 break; 190 191 case 'q': 192 map->map_mflags |= MF_KEEPQUOTES; 193 break; 194 195 case 'a': 196 map->map_app = ++p; 197 break; 198 199 case 'T': 200 map->map_tapp = ++p; 201 break; 202 203 case 'k': 204 while (isascii(*++p) && isspace(*p)) 205 continue; 206 map->map_keycolnm = p; 207 break; 208 209 case 'v': 210 while (isascii(*++p) && isspace(*p)) 211 continue; 212 map->map_valcolnm = p; 213 break; 214 215 case 'z': 216 if (*++p != '\\') 217 map->map_coldelim = *p; 218 else 219 { 220 switch (*++p) 221 { 222 case 'n': 223 map->map_coldelim = '\n'; 224 break; 225 226 case 't': 227 map->map_coldelim = '\t'; 228 break; 229 230 default: 231 map->map_coldelim = '\\'; 232 } 233 } 234 break; 235 236 case 't': 237 map->map_mflags |= MF_NODEFER; 238 break; 239 240 241 case 'S': 242 map->map_spacesub = *++p; 243 break; 244 245 case 'D': 246 map->map_mflags |= MF_DEFER; 247 break; 248 249 default: 250 syserr("Illegal option %c map %s", *p, map->map_mname); 251 break; 252 } 253 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 254 p++; 255 if (*p != '\0') 256 *p++ = '\0'; 257 } 258 if (map->map_app != NULL) 259 map->map_app = newstr(map->map_app); 260 if (map->map_tapp != NULL) 261 map->map_tapp = newstr(map->map_tapp); 262 if (map->map_keycolnm != NULL) 263 map->map_keycolnm = newstr(map->map_keycolnm); 264 if (map->map_valcolnm != NULL) 265 map->map_valcolnm = newstr(map->map_valcolnm); 266 267 if (*p != '\0') 268 { 269 map->map_file = p; 270 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 271 p++; 272 if (*p != '\0') 273 *p++ = '\0'; 274 map->map_file = newstr(map->map_file); 275 } 276 277 while (*p != '\0' && isascii(*p) && isspace(*p)) 278 p++; 279 if (*p != '\0') 280 map->map_rebuild = newstr(p); 281 282 if (map->map_file == NULL && 283 !bitset(MCF_OPTFILE, map->map_class->map_cflags)) 284 { 285 syserr("No file name for %s map %s", 286 map->map_class->map_cname, map->map_mname); 287 return false; 288 } 289 return true; 290 } 291 /* 292 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications. 293 ** 294 ** It also adds the map_app string. It can be used as a utility 295 ** in the map_lookup method. 296 ** 297 ** Parameters: 298 ** map -- the map that causes this. 299 ** s -- the string to rewrite, NOT necessarily null terminated. 300 ** slen -- the length of s. 301 ** av -- arguments to interpolate into buf. 302 ** 303 ** Returns: 304 ** Pointer to rewritten result. This is static data that 305 ** should be copied if it is to be saved! 306 */ 307 308 char * 309 map_rewrite(map, s, slen, av) 310 register MAP *map; 311 register const char *s; 312 size_t slen; 313 char **av; 314 { 315 register char *bp; 316 register char c; 317 char **avp; 318 register char *ap; 319 size_t l; 320 size_t len; 321 static size_t buflen = 0; 322 static char *buf = NULL; 323 324 if (tTd(39, 1)) 325 { 326 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s); 327 if (av == NULL) 328 sm_dprintf(" (nullv)"); 329 else 330 { 331 for (avp = av; *avp != NULL; avp++) 332 sm_dprintf("\n\t%s", *avp); 333 } 334 sm_dprintf("\n"); 335 } 336 337 /* count expected size of output (can safely overestimate) */ 338 l = len = slen; 339 if (av != NULL) 340 { 341 const char *sp = s; 342 343 while (l-- > 0 && (c = *sp++) != '\0') 344 { 345 if (c != '%') 346 continue; 347 if (l-- <= 0) 348 break; 349 c = *sp++; 350 if (!(isascii(c) && isdigit(c))) 351 continue; 352 for (avp = av; --c >= '0' && *avp != NULL; avp++) 353 continue; 354 if (*avp == NULL) 355 continue; 356 len += strlen(*avp); 357 } 358 } 359 if (map->map_app != NULL) 360 len += strlen(map->map_app); 361 if (buflen < ++len) 362 { 363 /* need to malloc additional space */ 364 buflen = len; 365 if (buf != NULL) 366 sm_free(buf); 367 buf = sm_pmalloc_x(buflen); 368 } 369 370 bp = buf; 371 if (av == NULL) 372 { 373 memmove(bp, s, slen); 374 bp += slen; 375 376 /* assert(len > slen); */ 377 len -= slen; 378 } 379 else 380 { 381 while (slen-- > 0 && (c = *s++) != '\0') 382 { 383 if (c != '%') 384 { 385 pushc: 386 if (len-- <= 1) 387 break; 388 *bp++ = c; 389 continue; 390 } 391 if (slen-- <= 0 || (c = *s++) == '\0') 392 c = '%'; 393 if (c == '%') 394 goto pushc; 395 if (!(isascii(c) && isdigit(c))) 396 { 397 if (len-- <= 1) 398 break; 399 *bp++ = '%'; 400 goto pushc; 401 } 402 for (avp = av; --c >= '0' && *avp != NULL; avp++) 403 continue; 404 if (*avp == NULL) 405 continue; 406 407 /* transliterate argument into output string */ 408 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len) 409 *bp++ = c; 410 } 411 } 412 if (map->map_app != NULL && len > 0) 413 (void) sm_strlcpy(bp, map->map_app, len); 414 else 415 *bp = '\0'; 416 if (tTd(39, 1)) 417 sm_dprintf("map_rewrite => %s\n", buf); 418 return buf; 419 } 420 /* 421 ** INITMAPS -- rebuild alias maps 422 ** 423 ** Parameters: 424 ** none. 425 ** 426 ** Returns: 427 ** none. 428 */ 429 430 void 431 initmaps() 432 { 433 #if XDEBUG 434 checkfd012("entering initmaps"); 435 #endif /* XDEBUG */ 436 stabapply(map_init, 0); 437 #if XDEBUG 438 checkfd012("exiting initmaps"); 439 #endif /* XDEBUG */ 440 } 441 /* 442 ** MAP_INIT -- rebuild a map 443 ** 444 ** Parameters: 445 ** s -- STAB entry: if map: try to rebuild 446 ** unused -- unused variable 447 ** 448 ** Returns: 449 ** none. 450 ** 451 ** Side Effects: 452 ** will close already open rebuildable map. 453 */ 454 455 /* ARGSUSED1 */ 456 static void 457 map_init(s, unused) 458 register STAB *s; 459 int unused; 460 { 461 register MAP *map; 462 463 /* has to be a map */ 464 if (s->s_symtype != ST_MAP) 465 return; 466 467 map = &s->s_map; 468 if (!bitset(MF_VALID, map->map_mflags)) 469 return; 470 471 if (tTd(38, 2)) 472 sm_dprintf("map_init(%s:%s, %s)\n", 473 map->map_class->map_cname == NULL ? "NULL" : 474 map->map_class->map_cname, 475 map->map_mname == NULL ? "NULL" : map->map_mname, 476 map->map_file == NULL ? "NULL" : map->map_file); 477 478 if (!bitset(MF_ALIAS, map->map_mflags) || 479 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) 480 { 481 if (tTd(38, 3)) 482 sm_dprintf("\tnot rebuildable\n"); 483 return; 484 } 485 486 /* if already open, close it (for nested open) */ 487 if (bitset(MF_OPEN, map->map_mflags)) 488 { 489 map->map_mflags |= MF_CLOSING; 490 map->map_class->map_close(map); 491 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 492 } 493 494 (void) rebuildaliases(map, false); 495 return; 496 } 497 /* 498 ** OPENMAP -- open a map 499 ** 500 ** Parameters: 501 ** map -- map to open (it must not be open). 502 ** 503 ** Returns: 504 ** whether open succeeded. 505 */ 506 507 bool 508 openmap(map) 509 MAP *map; 510 { 511 bool restore = false; 512 bool savehold = HoldErrs; 513 bool savequick = QuickAbort; 514 int saveerrors = Errors; 515 516 if (!bitset(MF_VALID, map->map_mflags)) 517 return false; 518 519 /* better safe than sorry... */ 520 if (bitset(MF_OPEN, map->map_mflags)) 521 return true; 522 523 /* Don't send a map open error out via SMTP */ 524 if ((OnlyOneError || QuickAbort) && 525 (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 526 { 527 restore = true; 528 HoldErrs = true; 529 QuickAbort = false; 530 } 531 532 errno = 0; 533 if (map->map_class->map_open(map, O_RDONLY)) 534 { 535 if (tTd(38, 4)) 536 sm_dprintf("openmap()\t%s:%s %s: valid\n", 537 map->map_class->map_cname == NULL ? "NULL" : 538 map->map_class->map_cname, 539 map->map_mname == NULL ? "NULL" : 540 map->map_mname, 541 map->map_file == NULL ? "NULL" : 542 map->map_file); 543 map->map_mflags |= MF_OPEN; 544 map->map_pid = CurrentPid; 545 } 546 else 547 { 548 if (tTd(38, 4)) 549 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n", 550 map->map_class->map_cname == NULL ? "NULL" : 551 map->map_class->map_cname, 552 map->map_mname == NULL ? "NULL" : 553 map->map_mname, 554 map->map_file == NULL ? "NULL" : 555 map->map_file, 556 errno == 0 ? "" : ": ", 557 errno == 0 ? "" : sm_errstring(errno)); 558 if (!bitset(MF_OPTIONAL, map->map_mflags)) 559 { 560 extern MAPCLASS BogusMapClass; 561 562 map->map_orgclass = map->map_class; 563 map->map_class = &BogusMapClass; 564 map->map_mflags |= MF_OPEN|MF_OPENBOGUS; 565 map->map_pid = CurrentPid; 566 } 567 else 568 { 569 /* don't try again */ 570 map->map_mflags &= ~MF_VALID; 571 } 572 } 573 574 if (restore) 575 { 576 Errors = saveerrors; 577 HoldErrs = savehold; 578 QuickAbort = savequick; 579 } 580 581 return bitset(MF_OPEN, map->map_mflags); 582 } 583 /* 584 ** CLOSEMAPS -- close all open maps opened by the current pid. 585 ** 586 ** Parameters: 587 ** bogus -- only close bogus maps. 588 ** 589 ** Returns: 590 ** none. 591 */ 592 593 void 594 closemaps(bogus) 595 bool bogus; 596 { 597 stabapply(map_close, bogus); 598 } 599 /* 600 ** MAP_CLOSE -- close a map opened by the current pid. 601 ** 602 ** Parameters: 603 ** s -- STAB entry: if map: try to close 604 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps. 605 ** 606 ** Returns: 607 ** none. 608 */ 609 610 /* ARGSUSED1 */ 611 static void 612 map_close(s, bogus) 613 register STAB *s; 614 int bogus; /* int because of stabapply(), used as bool */ 615 { 616 MAP *map; 617 extern MAPCLASS BogusMapClass; 618 619 if (s->s_symtype != ST_MAP) 620 return; 621 622 map = &s->s_map; 623 624 /* 625 ** close the map iff: 626 ** it is valid and open and opened by this process 627 ** and (!bogus or it's a bogus map or it is not persistent) 628 ** negate this: return iff 629 ** it is not valid or it is not open or not opened by this process 630 ** or (bogus and it's not a bogus map and it's not not-persistent) 631 */ 632 633 if (!bitset(MF_VALID, map->map_mflags) || 634 !bitset(MF_OPEN, map->map_mflags) || 635 bitset(MF_CLOSING, map->map_mflags) || 636 map->map_pid != CurrentPid || 637 (bogus && map->map_class != &BogusMapClass && 638 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags))) 639 return; 640 641 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL && 642 map->map_orgclass != &BogusMapClass) 643 map->map_class = map->map_orgclass; 644 if (tTd(38, 5)) 645 sm_dprintf("closemaps: closing %s (%s)\n", 646 map->map_mname == NULL ? "NULL" : map->map_mname, 647 map->map_file == NULL ? "NULL" : map->map_file); 648 649 if (!bitset(MF_OPENBOGUS, map->map_mflags)) 650 { 651 map->map_mflags |= MF_CLOSING; 652 map->map_class->map_close(map); 653 } 654 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING); 655 } 656 /* 657 ** GETCANONNAME -- look up name using service switch 658 ** 659 ** Parameters: 660 ** host -- the host name to look up. 661 ** hbsize -- the size of the host buffer. 662 ** trymx -- if set, try MX records. 663 ** pttl -- pointer to return TTL (can be NULL). 664 ** 665 ** Returns: 666 ** true -- if the host was found. 667 ** false -- otherwise. 668 */ 669 670 bool 671 getcanonname(host, hbsize, trymx, pttl) 672 char *host; 673 int hbsize; 674 bool trymx; 675 int *pttl; 676 { 677 int nmaps; 678 int mapno; 679 bool found = false; 680 bool got_tempfail = false; 681 auto int status; 682 char *maptype[MAXMAPSTACK]; 683 short mapreturn[MAXMAPACTIONS]; 684 685 nmaps = switch_map_find("hosts", maptype, mapreturn); 686 if (pttl != 0) 687 *pttl = SM_DEFAULT_TTL; 688 for (mapno = 0; mapno < nmaps; mapno++) 689 { 690 int i; 691 692 if (tTd(38, 20)) 693 sm_dprintf("getcanonname(%s), trying %s\n", 694 host, maptype[mapno]); 695 if (strcmp("files", maptype[mapno]) == 0) 696 { 697 found = text_getcanonname(host, hbsize, &status); 698 } 699 #if NIS 700 else if (strcmp("nis", maptype[mapno]) == 0) 701 { 702 found = nis_getcanonname(host, hbsize, &status); 703 } 704 #endif /* NIS */ 705 #if NISPLUS 706 else if (strcmp("nisplus", maptype[mapno]) == 0) 707 { 708 found = nisplus_getcanonname(host, hbsize, &status); 709 } 710 #endif /* NISPLUS */ 711 #if NAMED_BIND 712 else if (strcmp("dns", maptype[mapno]) == 0) 713 { 714 found = dns_getcanonname(host, hbsize, trymx, &status, pttl); 715 } 716 #endif /* NAMED_BIND */ 717 #if NETINFO 718 else if (strcmp("netinfo", maptype[mapno]) == 0) 719 { 720 found = ni_getcanonname(host, hbsize, &status); 721 } 722 #endif /* NETINFO */ 723 else 724 { 725 found = false; 726 status = EX_UNAVAILABLE; 727 } 728 729 /* 730 ** Heuristic: if $m is not set, we are running during system 731 ** startup. In this case, when a name is apparently found 732 ** but has no dot, treat is as not found. This avoids 733 ** problems if /etc/hosts has no FQDN but is listed first 734 ** in the service switch. 735 */ 736 737 if (found && 738 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL)) 739 break; 740 741 /* see if we should continue */ 742 if (status == EX_TEMPFAIL) 743 { 744 i = MA_TRYAGAIN; 745 got_tempfail = true; 746 } 747 else if (status == EX_NOTFOUND) 748 i = MA_NOTFOUND; 749 else 750 i = MA_UNAVAIL; 751 if (bitset(1 << mapno, mapreturn[i])) 752 break; 753 } 754 755 if (found) 756 { 757 char *d; 758 759 if (tTd(38, 20)) 760 sm_dprintf("getcanonname(%s), found\n", host); 761 762 /* 763 ** If returned name is still single token, compensate 764 ** by tagging on $m. This is because some sites set 765 ** up their DNS or NIS databases wrong. 766 */ 767 768 if ((d = strchr(host, '.')) == NULL || d[1] == '\0') 769 { 770 d = macvalue('m', CurEnv); 771 if (d != NULL && 772 hbsize > (int) (strlen(host) + strlen(d) + 1)) 773 { 774 if (host[strlen(host) - 1] != '.') 775 (void) sm_strlcat2(host, ".", d, 776 hbsize); 777 else 778 (void) sm_strlcat(host, d, hbsize); 779 } 780 else 781 return false; 782 } 783 return true; 784 } 785 786 if (tTd(38, 20)) 787 sm_dprintf("getcanonname(%s), failed, status=%d\n", host, 788 status); 789 790 if (got_tempfail) 791 SM_SET_H_ERRNO(TRY_AGAIN); 792 else 793 SM_SET_H_ERRNO(HOST_NOT_FOUND); 794 795 return false; 796 } 797 /* 798 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry 799 ** 800 ** Parameters: 801 ** name -- the name against which to match. 802 ** dot -- where to reinsert '.' to get FQDN 803 ** line -- the /etc/hosts line. 804 ** cbuf -- the location to store the result. 805 ** cbuflen -- the size of cbuf. 806 ** 807 ** Returns: 808 ** true -- if the line matched the desired name. 809 ** false -- otherwise. 810 */ 811 812 static bool 813 extract_canonname(name, dot, line, cbuf, cbuflen) 814 char *name; 815 char *dot; 816 char *line; 817 char cbuf[]; 818 int cbuflen; 819 { 820 int i; 821 char *p; 822 bool found = false; 823 824 cbuf[0] = '\0'; 825 if (line[0] == '#') 826 return false; 827 828 for (i = 1; ; i++) 829 { 830 char nbuf[MAXNAME + 1]; 831 832 p = get_column(line, i, '\0', nbuf, sizeof nbuf); 833 if (p == NULL) 834 break; 835 if (*p == '\0') 836 continue; 837 if (cbuf[0] == '\0' || 838 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL)) 839 { 840 (void) sm_strlcpy(cbuf, p, cbuflen); 841 } 842 if (sm_strcasecmp(name, p) == 0) 843 found = true; 844 else if (dot != NULL) 845 { 846 /* try looking for the FQDN as well */ 847 *dot = '.'; 848 if (sm_strcasecmp(name, p) == 0) 849 found = true; 850 *dot = '\0'; 851 } 852 } 853 if (found && strchr(cbuf, '.') == NULL) 854 { 855 /* try to add a domain on the end of the name */ 856 char *domain = macvalue('m', CurEnv); 857 858 if (domain != NULL && 859 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen) 860 { 861 p = &cbuf[i]; 862 *p++ = '.'; 863 (void) sm_strlcpy(p, domain, cbuflen - i - 1); 864 } 865 } 866 return found; 867 } 868 869 /* 870 ** DNS modules 871 */ 872 873 #if NAMED_BIND 874 # if DNSMAP 875 876 # include "sm_resolve.h" 877 # if NETINET || NETINET6 878 # include <arpa/inet.h> 879 # endif /* NETINET || NETINET6 */ 880 881 /* 882 ** DNS_MAP_OPEN -- stub to check proper value for dns map type 883 */ 884 885 bool 886 dns_map_open(map, mode) 887 MAP *map; 888 int mode; 889 { 890 if (tTd(38,2)) 891 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode); 892 893 mode &= O_ACCMODE; 894 if (mode != O_RDONLY) 895 { 896 /* issue a pseudo-error message */ 897 errno = SM_EMAPCANTWRITE; 898 return false; 899 } 900 return true; 901 } 902 903 /* 904 ** DNS_MAP_PARSEARGS -- parse dns map definition args. 905 ** 906 ** Parameters: 907 ** map -- pointer to MAP 908 ** args -- pointer to the args on the config line. 909 ** 910 ** Returns: 911 ** true -- if everything parsed OK. 912 ** false -- otherwise. 913 */ 914 915 # if _FFR_DNSMAP_MULTILIMIT 916 # if !_FFR_DNSMAP_MULTI 917 ERROR README: You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT 918 # endif /* ! _FFR_DNSMAP_MULTI */ 919 # endif /* _FFR_DNSMAP_MULTILIMIT */ 920 921 # if _FFR_DNSMAP_MULTI 922 # if _FFR_DNSMAP_MULTILIMIT 923 # define map_sizelimit map_lockfd /* overload field */ 924 # endif /* _FFR_DNSMAP_MULTILIMIT */ 925 # endif /* _FFR_DNSMAP_MULTI */ 926 927 struct dns_map 928 { 929 int dns_m_type; 930 }; 931 932 bool 933 dns_map_parseargs(map,args) 934 MAP *map; 935 char *args; 936 { 937 register char *p = args; 938 struct dns_map *map_p; 939 940 map_p = (struct dns_map *) xalloc(sizeof *map_p); 941 map_p->dns_m_type = -1; 942 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 943 944 for (;;) 945 { 946 while (isascii(*p) && isspace(*p)) 947 p++; 948 if (*p != '-') 949 break; 950 switch (*++p) 951 { 952 case 'N': 953 map->map_mflags |= MF_INCLNULL; 954 map->map_mflags &= ~MF_TRY0NULL; 955 break; 956 957 case 'O': 958 map->map_mflags &= ~MF_TRY1NULL; 959 break; 960 961 case 'o': 962 map->map_mflags |= MF_OPTIONAL; 963 break; 964 965 case 'f': 966 map->map_mflags |= MF_NOFOLDCASE; 967 break; 968 969 case 'm': 970 map->map_mflags |= MF_MATCHONLY; 971 break; 972 973 case 'A': 974 map->map_mflags |= MF_APPEND; 975 break; 976 977 case 'q': 978 map->map_mflags |= MF_KEEPQUOTES; 979 break; 980 981 case 't': 982 map->map_mflags |= MF_NODEFER; 983 break; 984 985 case 'a': 986 map->map_app = ++p; 987 break; 988 989 case 'T': 990 map->map_tapp = ++p; 991 break; 992 993 case 'd': 994 { 995 char *h; 996 997 ++p; 998 h = strchr(p, ' '); 999 if (h != NULL) 1000 *h = '\0'; 1001 map->map_timeout = convtime(p, 's'); 1002 if (h != NULL) 1003 *h = ' '; 1004 } 1005 break; 1006 1007 case 'r': 1008 while (isascii(*++p) && isspace(*p)) 1009 continue; 1010 map->map_retry = atoi(p); 1011 break; 1012 1013 # if _FFR_DNSMAP_MULTI 1014 case 'z': 1015 if (*++p != '\\') 1016 map->map_coldelim = *p; 1017 else 1018 { 1019 switch (*++p) 1020 { 1021 case 'n': 1022 map->map_coldelim = '\n'; 1023 break; 1024 1025 case 't': 1026 map->map_coldelim = '\t'; 1027 break; 1028 1029 default: 1030 map->map_coldelim = '\\'; 1031 } 1032 } 1033 break; 1034 1035 # if _FFR_DNSMAP_MULTILIMIT 1036 case 'Z': 1037 while (isascii(*++p) && isspace(*p)) 1038 continue; 1039 map->map_sizelimit = atoi(p); 1040 break; 1041 # endif /* _FFR_DNSMAP_MULTILIMIT */ 1042 # endif /* _FFR_DNSMAP_MULTI */ 1043 1044 /* Start of dns_map specific args */ 1045 case 'R': /* search field */ 1046 { 1047 char *h; 1048 1049 while (isascii(*++p) && isspace(*p)) 1050 continue; 1051 h = strchr(p, ' '); 1052 if (h != NULL) 1053 *h = '\0'; 1054 map_p->dns_m_type = dns_string_to_type(p); 1055 if (h != NULL) 1056 *h = ' '; 1057 if (map_p->dns_m_type < 0) 1058 syserr("dns map %s: wrong type %s", 1059 map->map_mname, p); 1060 } 1061 break; 1062 1063 # if _FFR_DNSMAP_BASE 1064 case 'B': /* base domain */ 1065 { 1066 char *h; 1067 1068 while (isascii(*++p) && isspace(*p)) 1069 continue; 1070 h = strchr(p, ' '); 1071 if (h != NULL) 1072 *h = '\0'; 1073 1074 /* 1075 ** slight abuse of map->map_file; it isn't 1076 ** used otherwise in this map type. 1077 */ 1078 1079 map->map_file = newstr(p); 1080 if (h != NULL) 1081 *h = ' '; 1082 } 1083 break; 1084 # endif /* _FFR_DNSMAP_BASE */ 1085 1086 } 1087 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1088 p++; 1089 if (*p != '\0') 1090 *p++ = '\0'; 1091 } 1092 if (map_p->dns_m_type < 0) 1093 syserr("dns map %s: missing -R type", map->map_mname); 1094 if (map->map_app != NULL) 1095 map->map_app = newstr(map->map_app); 1096 if (map->map_tapp != NULL) 1097 map->map_tapp = newstr(map->map_tapp); 1098 1099 /* 1100 ** Assumption: assert(sizeof int <= sizeof(ARBPTR_T)); 1101 ** Even if this assumption is wrong, we use only one byte, 1102 ** so it doesn't really matter. 1103 */ 1104 1105 map->map_db1 = (ARBPTR_T) map_p; 1106 return true; 1107 } 1108 1109 /* 1110 ** DNS_MAP_LOOKUP -- perform dns map lookup. 1111 ** 1112 ** Parameters: 1113 ** map -- pointer to MAP 1114 ** name -- name to lookup 1115 ** av -- arguments to interpolate into buf. 1116 ** statp -- pointer to status (EX_) 1117 ** 1118 ** Returns: 1119 ** result of lookup if succeeded. 1120 ** NULL -- otherwise. 1121 */ 1122 1123 char * 1124 dns_map_lookup(map, name, av, statp) 1125 MAP *map; 1126 char *name; 1127 char **av; 1128 int *statp; 1129 { 1130 # if _FFR_DNSMAP_MULTI 1131 # if _FFR_DNSMAP_MULTILIMIT 1132 int resnum = 0; 1133 # endif /* _FFR_DNSMAP_MULTILIMIT */ 1134 # endif /* _FFR_DNSMAP_MULTI */ 1135 char *vp = NULL, *result = NULL; 1136 size_t vsize; 1137 struct dns_map *map_p; 1138 RESOURCE_RECORD_T *rr = NULL; 1139 DNS_REPLY_T *r = NULL; 1140 # if NETINET6 1141 static char buf6[INET6_ADDRSTRLEN]; 1142 # endif /* NETINET6 */ 1143 1144 if (tTd(38, 20)) 1145 sm_dprintf("dns_map_lookup(%s, %s)\n", 1146 map->map_mname, name); 1147 1148 map_p = (struct dns_map *)(map->map_db1); 1149 # if _FFR_DNSMAP_BASE 1150 if (map->map_file != NULL && *map->map_file != '\0') 1151 { 1152 size_t len; 1153 char *appdomain; 1154 1155 len = strlen(map->map_file) + strlen(name) + 2; 1156 appdomain = (char *) sm_malloc(len); 1157 if (appdomain == NULL) 1158 { 1159 *statp = EX_UNAVAILABLE; 1160 return NULL; 1161 } 1162 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file); 1163 r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type, 1164 map->map_timeout, map->map_retry); 1165 sm_free(appdomain); 1166 } 1167 else 1168 # endif /* _FFR_DNSMAP_BASE */ 1169 { 1170 r = dns_lookup_int(name, C_IN, map_p->dns_m_type, 1171 map->map_timeout, map->map_retry); 1172 } 1173 1174 if (r == NULL) 1175 { 1176 result = NULL; 1177 if (h_errno == TRY_AGAIN || transienterror(errno)) 1178 *statp = EX_TEMPFAIL; 1179 else 1180 *statp = EX_NOTFOUND; 1181 goto cleanup; 1182 } 1183 *statp = EX_OK; 1184 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next) 1185 { 1186 char *type = NULL; 1187 char *value = NULL; 1188 1189 switch (rr->rr_type) 1190 { 1191 case T_NS: 1192 type = "T_NS"; 1193 value = rr->rr_u.rr_txt; 1194 break; 1195 case T_CNAME: 1196 type = "T_CNAME"; 1197 value = rr->rr_u.rr_txt; 1198 break; 1199 case T_AFSDB: 1200 type = "T_AFSDB"; 1201 value = rr->rr_u.rr_mx->mx_r_domain; 1202 break; 1203 case T_SRV: 1204 type = "T_SRV"; 1205 value = rr->rr_u.rr_srv->srv_r_target; 1206 break; 1207 case T_PTR: 1208 type = "T_PTR"; 1209 value = rr->rr_u.rr_txt; 1210 break; 1211 case T_TXT: 1212 type = "T_TXT"; 1213 value = rr->rr_u.rr_txt; 1214 break; 1215 case T_MX: 1216 type = "T_MX"; 1217 value = rr->rr_u.rr_mx->mx_r_domain; 1218 break; 1219 # if NETINET 1220 case T_A: 1221 type = "T_A"; 1222 value = inet_ntoa(*(rr->rr_u.rr_a)); 1223 break; 1224 # endif /* NETINET */ 1225 # if NETINET6 1226 case T_AAAA: 1227 type = "T_AAAA"; 1228 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6, 1229 sizeof buf6); 1230 break; 1231 # endif /* NETINET6 */ 1232 } 1233 1234 (void) strreplnonprt(value, 'X'); 1235 if (map_p->dns_m_type != rr->rr_type) 1236 { 1237 if (tTd(38, 40)) 1238 sm_dprintf("\tskipping type %s (%d) value %s\n", 1239 type != NULL ? type : "<UNKNOWN>", 1240 rr->rr_type, 1241 value != NULL ? value : "<NO VALUE>"); 1242 continue; 1243 } 1244 1245 # if NETINET6 1246 if (rr->rr_type == T_AAAA && value == NULL) 1247 { 1248 result = NULL; 1249 *statp = EX_DATAERR; 1250 if (tTd(38, 40)) 1251 sm_dprintf("\tbad T_AAAA conversion\n"); 1252 goto cleanup; 1253 } 1254 # endif /* NETINET6 */ 1255 if (tTd(38, 40)) 1256 sm_dprintf("\tfound type %s (%d) value %s\n", 1257 type != NULL ? type : "<UNKNOWN>", 1258 rr->rr_type, 1259 value != NULL ? value : "<NO VALUE>"); 1260 # if _FFR_DNSMAP_MULTI 1261 if (value != NULL && 1262 (map->map_coldelim == '\0' || 1263 # if _FFR_DNSMAP_MULTILIMIT 1264 map->map_sizelimit == 1 || 1265 # endif /* _FFR_DNSMAP_MULTILIMIT */ 1266 bitset(MF_MATCHONLY, map->map_mflags))) 1267 { 1268 /* Only care about the first match */ 1269 vp = newstr(value); 1270 break; 1271 } 1272 else if (vp == NULL) 1273 { 1274 /* First result */ 1275 vp = newstr(value); 1276 } 1277 else 1278 { 1279 /* concatenate the results */ 1280 int sz; 1281 char *new; 1282 1283 sz = strlen(vp) + strlen(value) + 2; 1284 new = xalloc(sz); 1285 (void) sm_snprintf(new, sz, "%s%c%s", 1286 vp, map->map_coldelim, value); 1287 sm_free(vp); 1288 vp = new; 1289 # if _FFR_DNSMAP_MULTILIMIT 1290 if (map->map_sizelimit > 0 && 1291 ++resnum >= map->map_sizelimit) 1292 break; 1293 # endif /* _FFR_DNSMAP_MULTILIMIT */ 1294 } 1295 # else /* _FFR_DNSMAP_MULTI */ 1296 vp = value; 1297 break; 1298 # endif /* _FFR_DNSMAP_MULTI */ 1299 } 1300 if (vp == NULL) 1301 { 1302 result = NULL; 1303 *statp = EX_NOTFOUND; 1304 if (tTd(38, 40)) 1305 sm_dprintf("\tno match found\n"); 1306 goto cleanup; 1307 } 1308 1309 # if _FFR_DNSMAP_MULTI 1310 /* Cleanly truncate for rulesets */ 1311 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim); 1312 # endif /* _FFR_DNSMAP_MULTI */ 1313 1314 vsize = strlen(vp); 1315 1316 if (LogLevel > 9) 1317 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s", 1318 name, vp); 1319 if (bitset(MF_MATCHONLY, map->map_mflags)) 1320 result = map_rewrite(map, name, strlen(name), NULL); 1321 else 1322 result = map_rewrite(map, vp, vsize, av); 1323 1324 cleanup: 1325 # if _FFR_DNSMAP_MULTI 1326 if (vp != NULL) 1327 sm_free(vp); 1328 # endif /* _FFR_DNSMAP_MULTI */ 1329 if (r != NULL) 1330 dns_free_data(r); 1331 return result; 1332 } 1333 # endif /* DNSMAP */ 1334 #endif /* NAMED_BIND */ 1335 1336 /* 1337 ** NDBM modules 1338 */ 1339 1340 #if NDBM 1341 1342 /* 1343 ** NDBM_MAP_OPEN -- DBM-style map open 1344 */ 1345 1346 bool 1347 ndbm_map_open(map, mode) 1348 MAP *map; 1349 int mode; 1350 { 1351 register DBM *dbm; 1352 int save_errno; 1353 int dfd; 1354 int pfd; 1355 long sff; 1356 int ret; 1357 int smode = S_IREAD; 1358 char dirfile[MAXPATHLEN]; 1359 char pagfile[MAXPATHLEN]; 1360 struct stat st; 1361 struct stat std, stp; 1362 1363 if (tTd(38, 2)) 1364 sm_dprintf("ndbm_map_open(%s, %s, %d)\n", 1365 map->map_mname, map->map_file, mode); 1366 map->map_lockfd = -1; 1367 mode &= O_ACCMODE; 1368 1369 /* do initial file and directory checks */ 1370 if (sm_strlcpyn(dirfile, sizeof dirfile, 2, 1371 map->map_file, ".dir") >= sizeof dirfile || 1372 sm_strlcpyn(pagfile, sizeof pagfile, 2, 1373 map->map_file, ".pag") >= sizeof pagfile) 1374 { 1375 errno = 0; 1376 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1377 syserr("dbm map \"%s\": map file %s name too long", 1378 map->map_mname, map->map_file); 1379 return false; 1380 } 1381 sff = SFF_ROOTOK|SFF_REGONLY; 1382 if (mode == O_RDWR) 1383 { 1384 sff |= SFF_CREAT; 1385 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 1386 sff |= SFF_NOSLINK; 1387 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 1388 sff |= SFF_NOHLINK; 1389 smode = S_IWRITE; 1390 } 1391 else 1392 { 1393 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 1394 sff |= SFF_NOWLINK; 1395 } 1396 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 1397 sff |= SFF_SAFEDIRPATH; 1398 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName, 1399 sff, smode, &std); 1400 if (ret == 0) 1401 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName, 1402 sff, smode, &stp); 1403 1404 if (ret != 0) 1405 { 1406 char *prob = "unsafe"; 1407 1408 /* cannot open this map */ 1409 if (ret == ENOENT) 1410 prob = "missing"; 1411 if (tTd(38, 2)) 1412 sm_dprintf("\t%s map file: %d\n", prob, ret); 1413 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1414 syserr("dbm map \"%s\": %s map file %s", 1415 map->map_mname, prob, map->map_file); 1416 return false; 1417 } 1418 if (std.st_mode == ST_MODE_NOFILE) 1419 mode |= O_CREAT|O_EXCL; 1420 1421 # if LOCK_ON_OPEN 1422 if (mode == O_RDONLY) 1423 mode |= O_SHLOCK; 1424 else 1425 mode |= O_TRUNC|O_EXLOCK; 1426 # else /* LOCK_ON_OPEN */ 1427 if ((mode & O_ACCMODE) == O_RDWR) 1428 { 1429 # if NOFTRUNCATE 1430 /* 1431 ** Warning: race condition. Try to lock the file as 1432 ** quickly as possible after opening it. 1433 ** This may also have security problems on some systems, 1434 ** but there isn't anything we can do about it. 1435 */ 1436 1437 mode |= O_TRUNC; 1438 # else /* NOFTRUNCATE */ 1439 /* 1440 ** This ugly code opens the map without truncating it, 1441 ** locks the file, then truncates it. Necessary to 1442 ** avoid race conditions. 1443 */ 1444 1445 int dirfd; 1446 int pagfd; 1447 long sff = SFF_CREAT|SFF_OPENASROOT; 1448 1449 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 1450 sff |= SFF_NOSLINK; 1451 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 1452 sff |= SFF_NOHLINK; 1453 1454 dirfd = safeopen(dirfile, mode, DBMMODE, sff); 1455 pagfd = safeopen(pagfile, mode, DBMMODE, sff); 1456 1457 if (dirfd < 0 || pagfd < 0) 1458 { 1459 save_errno = errno; 1460 if (dirfd >= 0) 1461 (void) close(dirfd); 1462 if (pagfd >= 0) 1463 (void) close(pagfd); 1464 errno = save_errno; 1465 syserr("ndbm_map_open: cannot create database %s", 1466 map->map_file); 1467 return false; 1468 } 1469 if (ftruncate(dirfd, (off_t) 0) < 0 || 1470 ftruncate(pagfd, (off_t) 0) < 0) 1471 { 1472 save_errno = errno; 1473 (void) close(dirfd); 1474 (void) close(pagfd); 1475 errno = save_errno; 1476 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}", 1477 map->map_file); 1478 return false; 1479 } 1480 1481 /* if new file, get "before" bits for later filechanged check */ 1482 if (std.st_mode == ST_MODE_NOFILE && 1483 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0)) 1484 { 1485 save_errno = errno; 1486 (void) close(dirfd); 1487 (void) close(pagfd); 1488 errno = save_errno; 1489 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file", 1490 map->map_file); 1491 return false; 1492 } 1493 1494 /* have to save the lock for the duration (bletch) */ 1495 map->map_lockfd = dirfd; 1496 (void) close(pagfd); 1497 1498 /* twiddle bits for dbm_open */ 1499 mode &= ~(O_CREAT|O_EXCL); 1500 # endif /* NOFTRUNCATE */ 1501 } 1502 # endif /* LOCK_ON_OPEN */ 1503 1504 /* open the database */ 1505 dbm = dbm_open(map->map_file, mode, DBMMODE); 1506 if (dbm == NULL) 1507 { 1508 save_errno = errno; 1509 if (bitset(MF_ALIAS, map->map_mflags) && 1510 aliaswait(map, ".pag", false)) 1511 return true; 1512 # if !LOCK_ON_OPEN && !NOFTRUNCATE 1513 if (map->map_lockfd >= 0) 1514 (void) close(map->map_lockfd); 1515 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */ 1516 errno = save_errno; 1517 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1518 syserr("Cannot open DBM database %s", map->map_file); 1519 return false; 1520 } 1521 dfd = dbm_dirfno(dbm); 1522 pfd = dbm_pagfno(dbm); 1523 if (dfd == pfd) 1524 { 1525 /* heuristic: if files are linked, this is actually gdbm */ 1526 dbm_close(dbm); 1527 # if !LOCK_ON_OPEN && !NOFTRUNCATE 1528 if (map->map_lockfd >= 0) 1529 (void) close(map->map_lockfd); 1530 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */ 1531 errno = 0; 1532 syserr("dbm map \"%s\": cannot support GDBM", 1533 map->map_mname); 1534 return false; 1535 } 1536 1537 if (filechanged(dirfile, dfd, &std) || 1538 filechanged(pagfile, pfd, &stp)) 1539 { 1540 save_errno = errno; 1541 dbm_close(dbm); 1542 # if !LOCK_ON_OPEN && !NOFTRUNCATE 1543 if (map->map_lockfd >= 0) 1544 (void) close(map->map_lockfd); 1545 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */ 1546 errno = save_errno; 1547 syserr("ndbm_map_open(%s): file changed after open", 1548 map->map_file); 1549 return false; 1550 } 1551 1552 map->map_db1 = (ARBPTR_T) dbm; 1553 1554 /* 1555 ** Need to set map_mtime before the call to aliaswait() 1556 ** as aliaswait() will call map_lookup() which requires 1557 ** map_mtime to be set 1558 */ 1559 1560 if (fstat(pfd, &st) >= 0) 1561 map->map_mtime = st.st_mtime; 1562 1563 if (mode == O_RDONLY) 1564 { 1565 # if LOCK_ON_OPEN 1566 if (dfd >= 0) 1567 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); 1568 if (pfd >= 0) 1569 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN); 1570 # endif /* LOCK_ON_OPEN */ 1571 if (bitset(MF_ALIAS, map->map_mflags) && 1572 !aliaswait(map, ".pag", true)) 1573 return false; 1574 } 1575 else 1576 { 1577 map->map_mflags |= MF_LOCKED; 1578 if (geteuid() == 0 && TrustedUid != 0) 1579 { 1580 # if HASFCHOWN 1581 if (fchown(dfd, TrustedUid, -1) < 0 || 1582 fchown(pfd, TrustedUid, -1) < 0) 1583 { 1584 int err = errno; 1585 1586 sm_syslog(LOG_ALERT, NOQID, 1587 "ownership change on %s failed: %s", 1588 map->map_file, sm_errstring(err)); 1589 message("050 ownership change on %s failed: %s", 1590 map->map_file, sm_errstring(err)); 1591 } 1592 # else /* HASFCHOWN */ 1593 sm_syslog(LOG_ALERT, NOQID, 1594 "no fchown(): cannot change ownership on %s", 1595 map->map_file); 1596 message("050 no fchown(): cannot change ownership on %s", 1597 map->map_file); 1598 # endif /* HASFCHOWN */ 1599 } 1600 } 1601 return true; 1602 } 1603 1604 1605 /* 1606 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map 1607 */ 1608 1609 char * 1610 ndbm_map_lookup(map, name, av, statp) 1611 MAP *map; 1612 char *name; 1613 char **av; 1614 int *statp; 1615 { 1616 datum key, val; 1617 int dfd, pfd; 1618 char keybuf[MAXNAME + 1]; 1619 struct stat stbuf; 1620 1621 if (tTd(38, 20)) 1622 sm_dprintf("ndbm_map_lookup(%s, %s)\n", 1623 map->map_mname, name); 1624 1625 key.dptr = name; 1626 key.dsize = strlen(name); 1627 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 1628 { 1629 if (key.dsize > sizeof keybuf - 1) 1630 key.dsize = sizeof keybuf - 1; 1631 memmove(keybuf, key.dptr, key.dsize); 1632 keybuf[key.dsize] = '\0'; 1633 makelower(keybuf); 1634 key.dptr = keybuf; 1635 } 1636 lockdbm: 1637 dfd = dbm_dirfno((DBM *) map->map_db1); 1638 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1639 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH); 1640 pfd = dbm_pagfno((DBM *) map->map_db1); 1641 if (pfd < 0 || fstat(pfd, &stbuf) < 0 || 1642 stbuf.st_mtime > map->map_mtime) 1643 { 1644 /* Reopen the database to sync the cache */ 1645 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR 1646 : O_RDONLY; 1647 1648 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1649 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); 1650 map->map_mflags |= MF_CLOSING; 1651 map->map_class->map_close(map); 1652 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 1653 if (map->map_class->map_open(map, omode)) 1654 { 1655 map->map_mflags |= MF_OPEN; 1656 map->map_pid = CurrentPid; 1657 if ((omode && O_ACCMODE) == O_RDWR) 1658 map->map_mflags |= MF_WRITABLE; 1659 goto lockdbm; 1660 } 1661 else 1662 { 1663 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1664 { 1665 extern MAPCLASS BogusMapClass; 1666 1667 *statp = EX_TEMPFAIL; 1668 map->map_orgclass = map->map_class; 1669 map->map_class = &BogusMapClass; 1670 map->map_mflags |= MF_OPEN; 1671 map->map_pid = CurrentPid; 1672 syserr("Cannot reopen NDBM database %s", 1673 map->map_file); 1674 } 1675 return NULL; 1676 } 1677 } 1678 val.dptr = NULL; 1679 if (bitset(MF_TRY0NULL, map->map_mflags)) 1680 { 1681 val = dbm_fetch((DBM *) map->map_db1, key); 1682 if (val.dptr != NULL) 1683 map->map_mflags &= ~MF_TRY1NULL; 1684 } 1685 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags)) 1686 { 1687 key.dsize++; 1688 val = dbm_fetch((DBM *) map->map_db1, key); 1689 if (val.dptr != NULL) 1690 map->map_mflags &= ~MF_TRY0NULL; 1691 } 1692 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1693 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); 1694 if (val.dptr == NULL) 1695 return NULL; 1696 if (bitset(MF_MATCHONLY, map->map_mflags)) 1697 return map_rewrite(map, name, strlen(name), NULL); 1698 else 1699 return map_rewrite(map, val.dptr, val.dsize, av); 1700 } 1701 1702 1703 /* 1704 ** NDBM_MAP_STORE -- store a datum in the database 1705 */ 1706 1707 void 1708 ndbm_map_store(map, lhs, rhs) 1709 register MAP *map; 1710 char *lhs; 1711 char *rhs; 1712 { 1713 datum key; 1714 datum data; 1715 int status; 1716 char keybuf[MAXNAME + 1]; 1717 1718 if (tTd(38, 12)) 1719 sm_dprintf("ndbm_map_store(%s, %s, %s)\n", 1720 map->map_mname, lhs, rhs); 1721 1722 key.dsize = strlen(lhs); 1723 key.dptr = lhs; 1724 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 1725 { 1726 if (key.dsize > sizeof keybuf - 1) 1727 key.dsize = sizeof keybuf - 1; 1728 memmove(keybuf, key.dptr, key.dsize); 1729 keybuf[key.dsize] = '\0'; 1730 makelower(keybuf); 1731 key.dptr = keybuf; 1732 } 1733 1734 data.dsize = strlen(rhs); 1735 data.dptr = rhs; 1736 1737 if (bitset(MF_INCLNULL, map->map_mflags)) 1738 { 1739 key.dsize++; 1740 data.dsize++; 1741 } 1742 1743 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT); 1744 if (status > 0) 1745 { 1746 if (!bitset(MF_APPEND, map->map_mflags)) 1747 message("050 Warning: duplicate alias name %s", lhs); 1748 else 1749 { 1750 static char *buf = NULL; 1751 static int bufsiz = 0; 1752 auto int xstat; 1753 datum old; 1754 1755 old.dptr = ndbm_map_lookup(map, key.dptr, 1756 (char **) NULL, &xstat); 1757 if (old.dptr != NULL && *(char *) old.dptr != '\0') 1758 { 1759 old.dsize = strlen(old.dptr); 1760 if (data.dsize + old.dsize + 2 > bufsiz) 1761 { 1762 if (buf != NULL) 1763 (void) sm_free(buf); 1764 bufsiz = data.dsize + old.dsize + 2; 1765 buf = sm_pmalloc_x(bufsiz); 1766 } 1767 (void) sm_strlcpyn(buf, bufsiz, 3, 1768 data.dptr, ",", old.dptr); 1769 data.dsize = data.dsize + old.dsize + 1; 1770 data.dptr = buf; 1771 if (tTd(38, 9)) 1772 sm_dprintf("ndbm_map_store append=%s\n", 1773 data.dptr); 1774 } 1775 } 1776 status = dbm_store((DBM *) map->map_db1, 1777 key, data, DBM_REPLACE); 1778 } 1779 if (status != 0) 1780 syserr("readaliases: dbm put (%s): %d", lhs, status); 1781 } 1782 1783 1784 /* 1785 ** NDBM_MAP_CLOSE -- close the database 1786 */ 1787 1788 void 1789 ndbm_map_close(map) 1790 register MAP *map; 1791 { 1792 if (tTd(38, 9)) 1793 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n", 1794 map->map_mname, map->map_file, map->map_mflags); 1795 1796 if (bitset(MF_WRITABLE, map->map_mflags)) 1797 { 1798 # ifdef NDBM_YP_COMPAT 1799 bool inclnull; 1800 char buf[MAXHOSTNAMELEN]; 1801 1802 inclnull = bitset(MF_INCLNULL, map->map_mflags); 1803 map->map_mflags &= ~MF_INCLNULL; 1804 1805 if (strstr(map->map_file, "/yp/") != NULL) 1806 { 1807 long save_mflags = map->map_mflags; 1808 1809 map->map_mflags |= MF_NOFOLDCASE; 1810 1811 (void) sm_snprintf(buf, sizeof buf, "%010ld", curtime()); 1812 ndbm_map_store(map, "YP_LAST_MODIFIED", buf); 1813 1814 (void) gethostname(buf, sizeof buf); 1815 ndbm_map_store(map, "YP_MASTER_NAME", buf); 1816 1817 map->map_mflags = save_mflags; 1818 } 1819 1820 if (inclnull) 1821 map->map_mflags |= MF_INCLNULL; 1822 # endif /* NDBM_YP_COMPAT */ 1823 1824 /* write out the distinguished alias */ 1825 ndbm_map_store(map, "@", "@"); 1826 } 1827 dbm_close((DBM *) map->map_db1); 1828 1829 /* release lock (if needed) */ 1830 # if !LOCK_ON_OPEN 1831 if (map->map_lockfd >= 0) 1832 (void) close(map->map_lockfd); 1833 # endif /* !LOCK_ON_OPEN */ 1834 } 1835 1836 #endif /* NDBM */ 1837 /* 1838 ** NEWDB (Hash and BTree) Modules 1839 */ 1840 1841 #if NEWDB 1842 1843 /* 1844 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives. 1845 ** 1846 ** These do rather bizarre locking. If you can lock on open, 1847 ** do that to avoid the condition of opening a database that 1848 ** is being rebuilt. If you don't, we'll try to fake it, but 1849 ** there will be a race condition. If opening for read-only, 1850 ** we immediately release the lock to avoid freezing things up. 1851 ** We really ought to hold the lock, but guarantee that we won't 1852 ** be pokey about it. That's hard to do. 1853 */ 1854 1855 /* these should be K line arguments */ 1856 # if DB_VERSION_MAJOR < 2 1857 # define db_cachesize cachesize 1858 # define h_nelem nelem 1859 # ifndef DB_CACHE_SIZE 1860 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */ 1861 # endif /* ! DB_CACHE_SIZE */ 1862 # ifndef DB_HASH_NELEM 1863 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */ 1864 # endif /* ! DB_HASH_NELEM */ 1865 # endif /* DB_VERSION_MAJOR < 2 */ 1866 1867 bool 1868 bt_map_open(map, mode) 1869 MAP *map; 1870 int mode; 1871 { 1872 # if DB_VERSION_MAJOR < 2 1873 BTREEINFO btinfo; 1874 # endif /* DB_VERSION_MAJOR < 2 */ 1875 # if DB_VERSION_MAJOR == 2 1876 DB_INFO btinfo; 1877 # endif /* DB_VERSION_MAJOR == 2 */ 1878 # if DB_VERSION_MAJOR > 2 1879 void *btinfo = NULL; 1880 # endif /* DB_VERSION_MAJOR > 2 */ 1881 1882 if (tTd(38, 2)) 1883 sm_dprintf("bt_map_open(%s, %s, %d)\n", 1884 map->map_mname, map->map_file, mode); 1885 1886 # if DB_VERSION_MAJOR < 3 1887 memset(&btinfo, '\0', sizeof btinfo); 1888 # ifdef DB_CACHE_SIZE 1889 btinfo.db_cachesize = DB_CACHE_SIZE; 1890 # endif /* DB_CACHE_SIZE */ 1891 # endif /* DB_VERSION_MAJOR < 3 */ 1892 1893 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo); 1894 } 1895 1896 bool 1897 hash_map_open(map, mode) 1898 MAP *map; 1899 int mode; 1900 { 1901 # if DB_VERSION_MAJOR < 2 1902 HASHINFO hinfo; 1903 # endif /* DB_VERSION_MAJOR < 2 */ 1904 # if DB_VERSION_MAJOR == 2 1905 DB_INFO hinfo; 1906 # endif /* DB_VERSION_MAJOR == 2 */ 1907 # if DB_VERSION_MAJOR > 2 1908 void *hinfo = NULL; 1909 # endif /* DB_VERSION_MAJOR > 2 */ 1910 1911 if (tTd(38, 2)) 1912 sm_dprintf("hash_map_open(%s, %s, %d)\n", 1913 map->map_mname, map->map_file, mode); 1914 1915 # if DB_VERSION_MAJOR < 3 1916 memset(&hinfo, '\0', sizeof hinfo); 1917 # ifdef DB_HASH_NELEM 1918 hinfo.h_nelem = DB_HASH_NELEM; 1919 # endif /* DB_HASH_NELEM */ 1920 # ifdef DB_CACHE_SIZE 1921 hinfo.db_cachesize = DB_CACHE_SIZE; 1922 # endif /* DB_CACHE_SIZE */ 1923 # endif /* DB_VERSION_MAJOR < 3 */ 1924 1925 return db_map_open(map, mode, "hash", DB_HASH, &hinfo); 1926 } 1927 1928 static bool 1929 db_map_open(map, mode, mapclassname, dbtype, openinfo) 1930 MAP *map; 1931 int mode; 1932 char *mapclassname; 1933 DBTYPE dbtype; 1934 # if DB_VERSION_MAJOR < 2 1935 const void *openinfo; 1936 # endif /* DB_VERSION_MAJOR < 2 */ 1937 # if DB_VERSION_MAJOR == 2 1938 DB_INFO *openinfo; 1939 # endif /* DB_VERSION_MAJOR == 2 */ 1940 # if DB_VERSION_MAJOR > 2 1941 void **openinfo; 1942 # endif /* DB_VERSION_MAJOR > 2 */ 1943 { 1944 DB *db = NULL; 1945 int i; 1946 int omode; 1947 int smode = S_IREAD; 1948 int fd; 1949 long sff; 1950 int save_errno; 1951 struct stat st; 1952 char buf[MAXPATHLEN]; 1953 1954 /* do initial file and directory checks */ 1955 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf) 1956 { 1957 errno = 0; 1958 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1959 syserr("map \"%s\": map file %s name too long", 1960 map->map_mname, map->map_file); 1961 return false; 1962 } 1963 i = strlen(buf); 1964 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0) 1965 { 1966 if (sm_strlcat(buf, ".db", sizeof buf) >= sizeof buf) 1967 { 1968 errno = 0; 1969 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1970 syserr("map \"%s\": map file %s name too long", 1971 map->map_mname, map->map_file); 1972 return false; 1973 } 1974 } 1975 1976 mode &= O_ACCMODE; 1977 omode = mode; 1978 1979 sff = SFF_ROOTOK|SFF_REGONLY; 1980 if (mode == O_RDWR) 1981 { 1982 sff |= SFF_CREAT; 1983 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 1984 sff |= SFF_NOSLINK; 1985 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 1986 sff |= SFF_NOHLINK; 1987 smode = S_IWRITE; 1988 } 1989 else 1990 { 1991 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 1992 sff |= SFF_NOWLINK; 1993 } 1994 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 1995 sff |= SFF_SAFEDIRPATH; 1996 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st); 1997 1998 if (i != 0) 1999 { 2000 char *prob = "unsafe"; 2001 2002 /* cannot open this map */ 2003 if (i == ENOENT) 2004 prob = "missing"; 2005 if (tTd(38, 2)) 2006 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i)); 2007 errno = i; 2008 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2009 syserr("%s map \"%s\": %s map file %s", 2010 mapclassname, map->map_mname, prob, buf); 2011 return false; 2012 } 2013 if (st.st_mode == ST_MODE_NOFILE) 2014 omode |= O_CREAT|O_EXCL; 2015 2016 map->map_lockfd = -1; 2017 2018 # if LOCK_ON_OPEN 2019 if (mode == O_RDWR) 2020 omode |= O_TRUNC|O_EXLOCK; 2021 else 2022 omode |= O_SHLOCK; 2023 # else /* LOCK_ON_OPEN */ 2024 /* 2025 ** Pre-lock the file to avoid race conditions. In particular, 2026 ** since dbopen returns NULL if the file is zero length, we 2027 ** must have a locked instance around the dbopen. 2028 */ 2029 2030 fd = open(buf, omode, DBMMODE); 2031 if (fd < 0) 2032 { 2033 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2034 syserr("db_map_open: cannot pre-open database %s", buf); 2035 return false; 2036 } 2037 2038 /* make sure no baddies slipped in just before the open... */ 2039 if (filechanged(buf, fd, &st)) 2040 { 2041 save_errno = errno; 2042 (void) close(fd); 2043 errno = save_errno; 2044 syserr("db_map_open(%s): file changed after pre-open", buf); 2045 return false; 2046 } 2047 2048 /* if new file, get the "before" bits for later filechanged check */ 2049 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0) 2050 { 2051 save_errno = errno; 2052 (void) close(fd); 2053 errno = save_errno; 2054 syserr("db_map_open(%s): cannot fstat pre-opened file", 2055 buf); 2056 return false; 2057 } 2058 2059 /* actually lock the pre-opened file */ 2060 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX)) 2061 syserr("db_map_open: cannot lock %s", buf); 2062 2063 /* set up mode bits for dbopen */ 2064 if (mode == O_RDWR) 2065 omode |= O_TRUNC; 2066 omode &= ~(O_EXCL|O_CREAT); 2067 # endif /* LOCK_ON_OPEN */ 2068 2069 # if DB_VERSION_MAJOR < 2 2070 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo); 2071 # else /* DB_VERSION_MAJOR < 2 */ 2072 { 2073 int flags = 0; 2074 # if DB_VERSION_MAJOR > 2 2075 int ret; 2076 # endif /* DB_VERSION_MAJOR > 2 */ 2077 2078 if (mode == O_RDONLY) 2079 flags |= DB_RDONLY; 2080 if (bitset(O_CREAT, omode)) 2081 flags |= DB_CREATE; 2082 if (bitset(O_TRUNC, omode)) 2083 flags |= DB_TRUNCATE; 2084 SM_DB_FLAG_ADD(flags); 2085 2086 # if DB_VERSION_MAJOR > 2 2087 ret = db_create(&db, NULL, 0); 2088 # ifdef DB_CACHE_SIZE 2089 if (ret == 0 && db != NULL) 2090 { 2091 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0); 2092 if (ret != 0) 2093 { 2094 (void) db->close(db, 0); 2095 db = NULL; 2096 } 2097 } 2098 # endif /* DB_CACHE_SIZE */ 2099 # ifdef DB_HASH_NELEM 2100 if (dbtype == DB_HASH && ret == 0 && db != NULL) 2101 { 2102 ret = db->set_h_nelem(db, DB_HASH_NELEM); 2103 if (ret != 0) 2104 { 2105 (void) db->close(db, 0); 2106 db = NULL; 2107 } 2108 } 2109 # endif /* DB_HASH_NELEM */ 2110 if (ret == 0 && db != NULL) 2111 { 2112 ret = db->open(db, 2113 DBTXN /* transaction for DB 4.1 */ 2114 buf, NULL, dbtype, flags, DBMMODE); 2115 if (ret != 0) 2116 { 2117 #ifdef DB_OLD_VERSION 2118 if (ret == DB_OLD_VERSION) 2119 ret = EINVAL; 2120 #endif /* DB_OLD_VERSION */ 2121 (void) db->close(db, 0); 2122 db = NULL; 2123 } 2124 } 2125 errno = ret; 2126 # else /* DB_VERSION_MAJOR > 2 */ 2127 errno = db_open(buf, dbtype, flags, DBMMODE, 2128 NULL, openinfo, &db); 2129 # endif /* DB_VERSION_MAJOR > 2 */ 2130 } 2131 # endif /* DB_VERSION_MAJOR < 2 */ 2132 save_errno = errno; 2133 2134 # if !LOCK_ON_OPEN 2135 if (mode == O_RDWR) 2136 map->map_lockfd = fd; 2137 else 2138 (void) close(fd); 2139 # endif /* !LOCK_ON_OPEN */ 2140 2141 if (db == NULL) 2142 { 2143 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && 2144 aliaswait(map, ".db", false)) 2145 return true; 2146 # if !LOCK_ON_OPEN 2147 if (map->map_lockfd >= 0) 2148 (void) close(map->map_lockfd); 2149 # endif /* !LOCK_ON_OPEN */ 2150 errno = save_errno; 2151 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2152 syserr("Cannot open %s database %s", 2153 mapclassname, buf); 2154 return false; 2155 } 2156 2157 # if DB_VERSION_MAJOR < 2 2158 fd = db->fd(db); 2159 # else /* DB_VERSION_MAJOR < 2 */ 2160 fd = -1; 2161 errno = db->fd(db, &fd); 2162 # endif /* DB_VERSION_MAJOR < 2 */ 2163 if (filechanged(buf, fd, &st)) 2164 { 2165 save_errno = errno; 2166 # if DB_VERSION_MAJOR < 2 2167 (void) db->close(db); 2168 # else /* DB_VERSION_MAJOR < 2 */ 2169 errno = db->close(db, 0); 2170 # endif /* DB_VERSION_MAJOR < 2 */ 2171 # if !LOCK_ON_OPEN 2172 if (map->map_lockfd >= 0) 2173 (void) close(map->map_lockfd); 2174 # endif /* !LOCK_ON_OPEN */ 2175 errno = save_errno; 2176 syserr("db_map_open(%s): file changed after open", buf); 2177 return false; 2178 } 2179 2180 if (mode == O_RDWR) 2181 map->map_mflags |= MF_LOCKED; 2182 # if LOCK_ON_OPEN 2183 if (fd >= 0 && mode == O_RDONLY) 2184 { 2185 (void) lockfile(fd, buf, NULL, LOCK_UN); 2186 } 2187 # endif /* LOCK_ON_OPEN */ 2188 2189 /* try to make sure that at least the database header is on disk */ 2190 if (mode == O_RDWR) 2191 { 2192 (void) db->sync(db, 0); 2193 if (geteuid() == 0 && TrustedUid != 0) 2194 { 2195 # if HASFCHOWN 2196 if (fchown(fd, TrustedUid, -1) < 0) 2197 { 2198 int err = errno; 2199 2200 sm_syslog(LOG_ALERT, NOQID, 2201 "ownership change on %s failed: %s", 2202 buf, sm_errstring(err)); 2203 message("050 ownership change on %s failed: %s", 2204 buf, sm_errstring(err)); 2205 } 2206 # else /* HASFCHOWN */ 2207 sm_syslog(LOG_ALERT, NOQID, 2208 "no fchown(): cannot change ownership on %s", 2209 map->map_file); 2210 message("050 no fchown(): cannot change ownership on %s", 2211 map->map_file); 2212 # endif /* HASFCHOWN */ 2213 } 2214 } 2215 2216 map->map_db2 = (ARBPTR_T) db; 2217 2218 /* 2219 ** Need to set map_mtime before the call to aliaswait() 2220 ** as aliaswait() will call map_lookup() which requires 2221 ** map_mtime to be set 2222 */ 2223 2224 if (fd >= 0 && fstat(fd, &st) >= 0) 2225 map->map_mtime = st.st_mtime; 2226 2227 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && 2228 !aliaswait(map, ".db", true)) 2229 return false; 2230 return true; 2231 } 2232 2233 2234 /* 2235 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map 2236 */ 2237 2238 char * 2239 db_map_lookup(map, name, av, statp) 2240 MAP *map; 2241 char *name; 2242 char **av; 2243 int *statp; 2244 { 2245 DBT key, val; 2246 register DB *db = (DB *) map->map_db2; 2247 int i; 2248 int st; 2249 int save_errno; 2250 int fd; 2251 struct stat stbuf; 2252 char keybuf[MAXNAME + 1]; 2253 char buf[MAXPATHLEN]; 2254 2255 memset(&key, '\0', sizeof key); 2256 memset(&val, '\0', sizeof val); 2257 2258 if (tTd(38, 20)) 2259 sm_dprintf("db_map_lookup(%s, %s)\n", 2260 map->map_mname, name); 2261 2262 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf) 2263 { 2264 errno = 0; 2265 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2266 syserr("map \"%s\": map file %s name too long", 2267 map->map_mname, map->map_file); 2268 return NULL; 2269 } 2270 i = strlen(buf); 2271 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0) 2272 buf[i - 3] = '\0'; 2273 2274 key.size = strlen(name); 2275 if (key.size > sizeof keybuf - 1) 2276 key.size = sizeof keybuf - 1; 2277 key.data = keybuf; 2278 memmove(keybuf, name, key.size); 2279 keybuf[key.size] = '\0'; 2280 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2281 makelower(keybuf); 2282 lockdb: 2283 # if DB_VERSION_MAJOR < 2 2284 fd = db->fd(db); 2285 # else /* DB_VERSION_MAJOR < 2 */ 2286 fd = -1; 2287 errno = db->fd(db, &fd); 2288 # endif /* DB_VERSION_MAJOR < 2 */ 2289 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 2290 (void) lockfile(fd, buf, ".db", LOCK_SH); 2291 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime) 2292 { 2293 /* Reopen the database to sync the cache */ 2294 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR 2295 : O_RDONLY; 2296 2297 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 2298 (void) lockfile(fd, buf, ".db", LOCK_UN); 2299 map->map_mflags |= MF_CLOSING; 2300 map->map_class->map_close(map); 2301 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 2302 if (map->map_class->map_open(map, omode)) 2303 { 2304 map->map_mflags |= MF_OPEN; 2305 map->map_pid = CurrentPid; 2306 if ((omode && O_ACCMODE) == O_RDWR) 2307 map->map_mflags |= MF_WRITABLE; 2308 db = (DB *) map->map_db2; 2309 goto lockdb; 2310 } 2311 else 2312 { 2313 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2314 { 2315 extern MAPCLASS BogusMapClass; 2316 2317 *statp = EX_TEMPFAIL; 2318 map->map_orgclass = map->map_class; 2319 map->map_class = &BogusMapClass; 2320 map->map_mflags |= MF_OPEN; 2321 map->map_pid = CurrentPid; 2322 syserr("Cannot reopen DB database %s", 2323 map->map_file); 2324 } 2325 return NULL; 2326 } 2327 } 2328 2329 st = 1; 2330 if (bitset(MF_TRY0NULL, map->map_mflags)) 2331 { 2332 # if DB_VERSION_MAJOR < 2 2333 st = db->get(db, &key, &val, 0); 2334 # else /* DB_VERSION_MAJOR < 2 */ 2335 errno = db->get(db, NULL, &key, &val, 0); 2336 switch (errno) 2337 { 2338 case DB_NOTFOUND: 2339 case DB_KEYEMPTY: 2340 st = 1; 2341 break; 2342 2343 case 0: 2344 st = 0; 2345 break; 2346 2347 default: 2348 st = -1; 2349 break; 2350 } 2351 # endif /* DB_VERSION_MAJOR < 2 */ 2352 if (st == 0) 2353 map->map_mflags &= ~MF_TRY1NULL; 2354 } 2355 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags)) 2356 { 2357 key.size++; 2358 # if DB_VERSION_MAJOR < 2 2359 st = db->get(db, &key, &val, 0); 2360 # else /* DB_VERSION_MAJOR < 2 */ 2361 errno = db->get(db, NULL, &key, &val, 0); 2362 switch (errno) 2363 { 2364 case DB_NOTFOUND: 2365 case DB_KEYEMPTY: 2366 st = 1; 2367 break; 2368 2369 case 0: 2370 st = 0; 2371 break; 2372 2373 default: 2374 st = -1; 2375 break; 2376 } 2377 # endif /* DB_VERSION_MAJOR < 2 */ 2378 if (st == 0) 2379 map->map_mflags &= ~MF_TRY0NULL; 2380 } 2381 save_errno = errno; 2382 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 2383 (void) lockfile(fd, buf, ".db", LOCK_UN); 2384 if (st != 0) 2385 { 2386 errno = save_errno; 2387 if (st < 0) 2388 syserr("db_map_lookup: get (%s)", name); 2389 return NULL; 2390 } 2391 if (bitset(MF_MATCHONLY, map->map_mflags)) 2392 return map_rewrite(map, name, strlen(name), NULL); 2393 else 2394 return map_rewrite(map, val.data, val.size, av); 2395 } 2396 2397 2398 /* 2399 ** DB_MAP_STORE -- store a datum in the NEWDB database 2400 */ 2401 2402 void 2403 db_map_store(map, lhs, rhs) 2404 register MAP *map; 2405 char *lhs; 2406 char *rhs; 2407 { 2408 int status; 2409 DBT key; 2410 DBT data; 2411 register DB *db = map->map_db2; 2412 char keybuf[MAXNAME + 1]; 2413 2414 memset(&key, '\0', sizeof key); 2415 memset(&data, '\0', sizeof data); 2416 2417 if (tTd(38, 12)) 2418 sm_dprintf("db_map_store(%s, %s, %s)\n", 2419 map->map_mname, lhs, rhs); 2420 2421 key.size = strlen(lhs); 2422 key.data = lhs; 2423 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2424 { 2425 if (key.size > sizeof keybuf - 1) 2426 key.size = sizeof keybuf - 1; 2427 memmove(keybuf, key.data, key.size); 2428 keybuf[key.size] = '\0'; 2429 makelower(keybuf); 2430 key.data = keybuf; 2431 } 2432 2433 data.size = strlen(rhs); 2434 data.data = rhs; 2435 2436 if (bitset(MF_INCLNULL, map->map_mflags)) 2437 { 2438 key.size++; 2439 data.size++; 2440 } 2441 2442 # if DB_VERSION_MAJOR < 2 2443 status = db->put(db, &key, &data, R_NOOVERWRITE); 2444 # else /* DB_VERSION_MAJOR < 2 */ 2445 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE); 2446 switch (errno) 2447 { 2448 case DB_KEYEXIST: 2449 status = 1; 2450 break; 2451 2452 case 0: 2453 status = 0; 2454 break; 2455 2456 default: 2457 status = -1; 2458 break; 2459 } 2460 # endif /* DB_VERSION_MAJOR < 2 */ 2461 if (status > 0) 2462 { 2463 if (!bitset(MF_APPEND, map->map_mflags)) 2464 message("050 Warning: duplicate alias name %s", lhs); 2465 else 2466 { 2467 static char *buf = NULL; 2468 static int bufsiz = 0; 2469 DBT old; 2470 2471 memset(&old, '\0', sizeof old); 2472 2473 old.data = db_map_lookup(map, key.data, 2474 (char **) NULL, &status); 2475 if (old.data != NULL) 2476 { 2477 old.size = strlen(old.data); 2478 if (data.size + old.size + 2 > (size_t) bufsiz) 2479 { 2480 if (buf != NULL) 2481 sm_free(buf); 2482 bufsiz = data.size + old.size + 2; 2483 buf = sm_pmalloc_x(bufsiz); 2484 } 2485 (void) sm_strlcpyn(buf, bufsiz, 3, 2486 (char *) data.data, ",", 2487 (char *) old.data); 2488 data.size = data.size + old.size + 1; 2489 data.data = buf; 2490 if (tTd(38, 9)) 2491 sm_dprintf("db_map_store append=%s\n", 2492 (char *) data.data); 2493 } 2494 } 2495 # if DB_VERSION_MAJOR < 2 2496 status = db->put(db, &key, &data, 0); 2497 # else /* DB_VERSION_MAJOR < 2 */ 2498 status = errno = db->put(db, NULL, &key, &data, 0); 2499 # endif /* DB_VERSION_MAJOR < 2 */ 2500 } 2501 if (status != 0) 2502 syserr("readaliases: db put (%s)", lhs); 2503 } 2504 2505 2506 /* 2507 ** DB_MAP_CLOSE -- add distinguished entries and close the database 2508 */ 2509 2510 void 2511 db_map_close(map) 2512 MAP *map; 2513 { 2514 register DB *db = map->map_db2; 2515 2516 if (tTd(38, 9)) 2517 sm_dprintf("db_map_close(%s, %s, %lx)\n", 2518 map->map_mname, map->map_file, map->map_mflags); 2519 2520 if (bitset(MF_WRITABLE, map->map_mflags)) 2521 { 2522 /* write out the distinguished alias */ 2523 db_map_store(map, "@", "@"); 2524 } 2525 2526 (void) db->sync(db, 0); 2527 2528 # if !LOCK_ON_OPEN 2529 if (map->map_lockfd >= 0) 2530 (void) close(map->map_lockfd); 2531 # endif /* !LOCK_ON_OPEN */ 2532 2533 # if DB_VERSION_MAJOR < 2 2534 if (db->close(db) != 0) 2535 # else /* DB_VERSION_MAJOR < 2 */ 2536 /* 2537 ** Berkeley DB can use internal shared memory 2538 ** locking for its memory pool. Closing a map 2539 ** opened by another process will interfere 2540 ** with the shared memory and locks of the parent 2541 ** process leaving things in a bad state. 2542 */ 2543 2544 /* 2545 ** If this map was not opened by the current 2546 ** process, do not close the map but recover 2547 ** the file descriptor. 2548 */ 2549 2550 if (map->map_pid != CurrentPid) 2551 { 2552 int fd = -1; 2553 2554 errno = db->fd(db, &fd); 2555 if (fd >= 0) 2556 (void) close(fd); 2557 return; 2558 } 2559 2560 if ((errno = db->close(db, 0)) != 0) 2561 # endif /* DB_VERSION_MAJOR < 2 */ 2562 syserr("db_map_close(%s, %s, %lx): db close failure", 2563 map->map_mname, map->map_file, map->map_mflags); 2564 } 2565 #endif /* NEWDB */ 2566 /* 2567 ** NIS Modules 2568 */ 2569 2570 #if NIS 2571 2572 # ifndef YPERR_BUSY 2573 # define YPERR_BUSY 16 2574 # endif /* ! YPERR_BUSY */ 2575 2576 /* 2577 ** NIS_MAP_OPEN -- open DBM map 2578 */ 2579 2580 bool 2581 nis_map_open(map, mode) 2582 MAP *map; 2583 int mode; 2584 { 2585 int yperr; 2586 register char *p; 2587 auto char *vp; 2588 auto int vsize; 2589 2590 if (tTd(38, 2)) 2591 sm_dprintf("nis_map_open(%s, %s, %d)\n", 2592 map->map_mname, map->map_file, mode); 2593 2594 mode &= O_ACCMODE; 2595 if (mode != O_RDONLY) 2596 { 2597 /* issue a pseudo-error message */ 2598 errno = SM_EMAPCANTWRITE; 2599 return false; 2600 } 2601 2602 p = strchr(map->map_file, '@'); 2603 if (p != NULL) 2604 { 2605 *p++ = '\0'; 2606 if (*p != '\0') 2607 map->map_domain = p; 2608 } 2609 2610 if (*map->map_file == '\0') 2611 map->map_file = "mail.aliases"; 2612 2613 if (map->map_domain == NULL) 2614 { 2615 yperr = yp_get_default_domain(&map->map_domain); 2616 if (yperr != 0) 2617 { 2618 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2619 syserr("451 4.3.5 NIS map %s specified, but NIS not running", 2620 map->map_file); 2621 return false; 2622 } 2623 } 2624 2625 /* check to see if this map actually exists */ 2626 vp = NULL; 2627 yperr = yp_match(map->map_domain, map->map_file, "@", 1, 2628 &vp, &vsize); 2629 if (tTd(38, 10)) 2630 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n", 2631 map->map_domain, map->map_file, yperr_string(yperr)); 2632 if (vp != NULL) 2633 sm_free(vp); 2634 2635 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY) 2636 { 2637 /* 2638 ** We ought to be calling aliaswait() here if this is an 2639 ** alias file, but powerful HP-UX NIS servers apparently 2640 ** don't insert the @:@ token into the alias map when it 2641 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX. 2642 */ 2643 2644 # if 0 2645 if (!bitset(MF_ALIAS, map->map_mflags) || 2646 aliaswait(map, NULL, true)) 2647 # endif /* 0 */ 2648 return true; 2649 } 2650 2651 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2652 { 2653 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s", 2654 map->map_file, map->map_domain, yperr_string(yperr)); 2655 } 2656 2657 return false; 2658 } 2659 2660 2661 /* 2662 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map 2663 */ 2664 2665 /* ARGSUSED3 */ 2666 char * 2667 nis_map_lookup(map, name, av, statp) 2668 MAP *map; 2669 char *name; 2670 char **av; 2671 int *statp; 2672 { 2673 char *vp; 2674 auto int vsize; 2675 int buflen; 2676 int yperr; 2677 char keybuf[MAXNAME + 1]; 2678 char *SM_NONVOLATILE result = NULL; 2679 2680 if (tTd(38, 20)) 2681 sm_dprintf("nis_map_lookup(%s, %s)\n", 2682 map->map_mname, name); 2683 2684 buflen = strlen(name); 2685 if (buflen > sizeof keybuf - 1) 2686 buflen = sizeof keybuf - 1; 2687 memmove(keybuf, name, buflen); 2688 keybuf[buflen] = '\0'; 2689 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2690 makelower(keybuf); 2691 yperr = YPERR_KEY; 2692 vp = NULL; 2693 if (bitset(MF_TRY0NULL, map->map_mflags)) 2694 { 2695 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, 2696 &vp, &vsize); 2697 if (yperr == 0) 2698 map->map_mflags &= ~MF_TRY1NULL; 2699 } 2700 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags)) 2701 { 2702 SM_FREE_CLR(vp); 2703 buflen++; 2704 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, 2705 &vp, &vsize); 2706 if (yperr == 0) 2707 map->map_mflags &= ~MF_TRY0NULL; 2708 } 2709 if (yperr != 0) 2710 { 2711 if (yperr != YPERR_KEY && yperr != YPERR_BUSY) 2712 map->map_mflags &= ~(MF_VALID|MF_OPEN); 2713 if (vp != NULL) 2714 sm_free(vp); 2715 return NULL; 2716 } 2717 SM_TRY 2718 if (bitset(MF_MATCHONLY, map->map_mflags)) 2719 result = map_rewrite(map, name, strlen(name), NULL); 2720 else 2721 result = map_rewrite(map, vp, vsize, av); 2722 SM_FINALLY 2723 if (vp != NULL) 2724 sm_free(vp); 2725 SM_END_TRY 2726 return result; 2727 } 2728 2729 2730 /* 2731 ** NIS_GETCANONNAME -- look up canonical name in NIS 2732 */ 2733 2734 static bool 2735 nis_getcanonname(name, hbsize, statp) 2736 char *name; 2737 int hbsize; 2738 int *statp; 2739 { 2740 char *vp; 2741 auto int vsize; 2742 int keylen; 2743 int yperr; 2744 static bool try0null = true; 2745 static bool try1null = true; 2746 static char *yp_domain = NULL; 2747 char host_record[MAXLINE]; 2748 char cbuf[MAXNAME]; 2749 char nbuf[MAXNAME + 1]; 2750 2751 if (tTd(38, 20)) 2752 sm_dprintf("nis_getcanonname(%s)\n", name); 2753 2754 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) 2755 { 2756 *statp = EX_UNAVAILABLE; 2757 return false; 2758 } 2759 (void) shorten_hostname(nbuf); 2760 keylen = strlen(nbuf); 2761 2762 if (yp_domain == NULL) 2763 (void) yp_get_default_domain(&yp_domain); 2764 makelower(nbuf); 2765 yperr = YPERR_KEY; 2766 vp = NULL; 2767 if (try0null) 2768 { 2769 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, 2770 &vp, &vsize); 2771 if (yperr == 0) 2772 try1null = false; 2773 } 2774 if (yperr == YPERR_KEY && try1null) 2775 { 2776 SM_FREE_CLR(vp); 2777 keylen++; 2778 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, 2779 &vp, &vsize); 2780 if (yperr == 0) 2781 try0null = false; 2782 } 2783 if (yperr != 0) 2784 { 2785 if (yperr == YPERR_KEY) 2786 *statp = EX_NOHOST; 2787 else if (yperr == YPERR_BUSY) 2788 *statp = EX_TEMPFAIL; 2789 else 2790 *statp = EX_UNAVAILABLE; 2791 if (vp != NULL) 2792 sm_free(vp); 2793 return false; 2794 } 2795 (void) sm_strlcpy(host_record, vp, sizeof host_record); 2796 sm_free(vp); 2797 if (tTd(38, 44)) 2798 sm_dprintf("got record `%s'\n", host_record); 2799 vp = strpbrk(host_record, "#\n"); 2800 if (vp != NULL) 2801 *vp = '\0'; 2802 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf)) 2803 { 2804 /* this should not happen, but.... */ 2805 *statp = EX_NOHOST; 2806 return false; 2807 } 2808 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize) 2809 { 2810 *statp = EX_UNAVAILABLE; 2811 return false; 2812 } 2813 *statp = EX_OK; 2814 return true; 2815 } 2816 2817 #endif /* NIS */ 2818 /* 2819 ** NISPLUS Modules 2820 ** 2821 ** This code donated by Sun Microsystems. 2822 */ 2823 2824 #if NISPLUS 2825 2826 # undef NIS /* symbol conflict in nis.h */ 2827 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */ 2828 # include <rpcsvc/nis.h> 2829 # include <rpcsvc/nislib.h> 2830 2831 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val 2832 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name 2833 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len) 2834 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.') 2835 2836 /* 2837 ** NISPLUS_MAP_OPEN -- open nisplus table 2838 */ 2839 2840 bool 2841 nisplus_map_open(map, mode) 2842 MAP *map; 2843 int mode; 2844 { 2845 nis_result *res = NULL; 2846 int retry_cnt, max_col, i; 2847 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 2848 2849 if (tTd(38, 2)) 2850 sm_dprintf("nisplus_map_open(%s, %s, %d)\n", 2851 map->map_mname, map->map_file, mode); 2852 2853 mode &= O_ACCMODE; 2854 if (mode != O_RDONLY) 2855 { 2856 errno = EPERM; 2857 return false; 2858 } 2859 2860 if (*map->map_file == '\0') 2861 map->map_file = "mail_aliases.org_dir"; 2862 2863 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL) 2864 { 2865 /* set default NISPLUS Domain to $m */ 2866 map->map_domain = newstr(nisplus_default_domain()); 2867 if (tTd(38, 2)) 2868 sm_dprintf("nisplus_map_open(%s): using domain %s\n", 2869 map->map_file, map->map_domain); 2870 } 2871 if (!PARTIAL_NAME(map->map_file)) 2872 { 2873 map->map_domain = newstr(""); 2874 (void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf); 2875 } 2876 else 2877 { 2878 /* check to see if this map actually exists */ 2879 (void) sm_strlcpyn(qbuf, sizeof qbuf, 3, 2880 map->map_file, ".", map->map_domain); 2881 } 2882 2883 retry_cnt = 0; 2884 while (res == NULL || res->status != NIS_SUCCESS) 2885 { 2886 res = nis_lookup(qbuf, FOLLOW_LINKS); 2887 switch (res->status) 2888 { 2889 case NIS_SUCCESS: 2890 break; 2891 2892 case NIS_TRYAGAIN: 2893 case NIS_RPCERROR: 2894 case NIS_NAMEUNREACHABLE: 2895 if (retry_cnt++ > 4) 2896 { 2897 errno = EAGAIN; 2898 return false; 2899 } 2900 /* try not to overwhelm hosed server */ 2901 sleep(2); 2902 break; 2903 2904 default: /* all other nisplus errors */ 2905 # if 0 2906 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2907 syserr("451 4.3.5 Cannot find table %s.%s: %s", 2908 map->map_file, map->map_domain, 2909 nis_sperrno(res->status)); 2910 # endif /* 0 */ 2911 errno = EAGAIN; 2912 return false; 2913 } 2914 } 2915 2916 if (NIS_RES_NUMOBJ(res) != 1 || 2917 (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ)) 2918 { 2919 if (tTd(38, 10)) 2920 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf); 2921 # if 0 2922 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2923 syserr("451 4.3.5 %s.%s: %s is not a table", 2924 map->map_file, map->map_domain, 2925 nis_sperrno(res->status)); 2926 # endif /* 0 */ 2927 errno = EBADF; 2928 return false; 2929 } 2930 /* default key column is column 0 */ 2931 if (map->map_keycolnm == NULL) 2932 map->map_keycolnm = newstr(COL_NAME(res,0)); 2933 2934 max_col = COL_MAX(res); 2935 2936 /* verify the key column exist */ 2937 for (i = 0; i < max_col; i++) 2938 { 2939 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0) 2940 break; 2941 } 2942 if (i == max_col) 2943 { 2944 if (tTd(38, 2)) 2945 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n", 2946 map->map_file, map->map_keycolnm); 2947 errno = ENOENT; 2948 return false; 2949 } 2950 2951 /* default value column is the last column */ 2952 if (map->map_valcolnm == NULL) 2953 { 2954 map->map_valcolno = max_col - 1; 2955 return true; 2956 } 2957 2958 for (i = 0; i< max_col; i++) 2959 { 2960 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0) 2961 { 2962 map->map_valcolno = i; 2963 return true; 2964 } 2965 } 2966 2967 if (tTd(38, 2)) 2968 sm_dprintf("nisplus_map_open(%s): can not find column %s\n", 2969 map->map_file, map->map_keycolnm); 2970 errno = ENOENT; 2971 return false; 2972 } 2973 2974 2975 /* 2976 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table 2977 */ 2978 2979 char * 2980 nisplus_map_lookup(map, name, av, statp) 2981 MAP *map; 2982 char *name; 2983 char **av; 2984 int *statp; 2985 { 2986 char *p; 2987 auto int vsize; 2988 char *skp; 2989 int skleft; 2990 char search_key[MAXNAME + 4]; 2991 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 2992 nis_result *result; 2993 2994 if (tTd(38, 20)) 2995 sm_dprintf("nisplus_map_lookup(%s, %s)\n", 2996 map->map_mname, name); 2997 2998 if (!bitset(MF_OPEN, map->map_mflags)) 2999 { 3000 if (nisplus_map_open(map, O_RDONLY)) 3001 { 3002 map->map_mflags |= MF_OPEN; 3003 map->map_pid = CurrentPid; 3004 } 3005 else 3006 { 3007 *statp = EX_UNAVAILABLE; 3008 return NULL; 3009 } 3010 } 3011 3012 /* 3013 ** Copy the name to the key buffer, escaping double quote characters 3014 ** by doubling them and quoting "]" and "," to avoid having the 3015 ** NIS+ parser choke on them. 3016 */ 3017 3018 skleft = sizeof search_key - 4; 3019 skp = search_key; 3020 for (p = name; *p != '\0' && skleft > 0; p++) 3021 { 3022 switch (*p) 3023 { 3024 case ']': 3025 case ',': 3026 /* quote the character */ 3027 *skp++ = '"'; 3028 *skp++ = *p; 3029 *skp++ = '"'; 3030 skleft -= 3; 3031 break; 3032 3033 case '"': 3034 /* double the quote */ 3035 *skp++ = '"'; 3036 skleft--; 3037 /* FALLTHROUGH */ 3038 3039 default: 3040 *skp++ = *p; 3041 skleft--; 3042 break; 3043 } 3044 } 3045 *skp = '\0'; 3046 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3047 makelower(search_key); 3048 3049 /* construct the query */ 3050 if (PARTIAL_NAME(map->map_file)) 3051 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s", 3052 map->map_keycolnm, search_key, map->map_file, 3053 map->map_domain); 3054 else 3055 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s", 3056 map->map_keycolnm, search_key, map->map_file); 3057 3058 if (tTd(38, 20)) 3059 sm_dprintf("qbuf=%s\n", qbuf); 3060 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL); 3061 if (result->status == NIS_SUCCESS) 3062 { 3063 int count; 3064 char *str; 3065 3066 if ((count = NIS_RES_NUMOBJ(result)) != 1) 3067 { 3068 if (LogLevel > 10) 3069 sm_syslog(LOG_WARNING, CurEnv->e_id, 3070 "%s: lookup error, expected 1 entry, got %d", 3071 map->map_file, count); 3072 3073 /* ignore second entry */ 3074 if (tTd(38, 20)) 3075 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n", 3076 name, count); 3077 } 3078 3079 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno)); 3080 /* set the length of the result */ 3081 if (p == NULL) 3082 p = ""; 3083 vsize = strlen(p); 3084 if (tTd(38, 20)) 3085 sm_dprintf("nisplus_map_lookup(%s), found %s\n", 3086 name, p); 3087 if (bitset(MF_MATCHONLY, map->map_mflags)) 3088 str = map_rewrite(map, name, strlen(name), NULL); 3089 else 3090 str = map_rewrite(map, p, vsize, av); 3091 nis_freeresult(result); 3092 *statp = EX_OK; 3093 return str; 3094 } 3095 else 3096 { 3097 if (result->status == NIS_NOTFOUND) 3098 *statp = EX_NOTFOUND; 3099 else if (result->status == NIS_TRYAGAIN) 3100 *statp = EX_TEMPFAIL; 3101 else 3102 { 3103 *statp = EX_UNAVAILABLE; 3104 map->map_mflags &= ~(MF_VALID|MF_OPEN); 3105 } 3106 } 3107 if (tTd(38, 20)) 3108 sm_dprintf("nisplus_map_lookup(%s), failed\n", name); 3109 nis_freeresult(result); 3110 return NULL; 3111 } 3112 3113 3114 3115 /* 3116 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+ 3117 */ 3118 3119 static bool 3120 nisplus_getcanonname(name, hbsize, statp) 3121 char *name; 3122 int hbsize; 3123 int *statp; 3124 { 3125 char *vp; 3126 auto int vsize; 3127 nis_result *result; 3128 char *p; 3129 char nbuf[MAXNAME + 1]; 3130 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 3131 3132 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) 3133 { 3134 *statp = EX_UNAVAILABLE; 3135 return false; 3136 } 3137 (void) shorten_hostname(nbuf); 3138 3139 p = strchr(nbuf, '.'); 3140 if (p == NULL) 3141 { 3142 /* single token */ 3143 (void) sm_snprintf(qbuf, sizeof qbuf, 3144 "[name=%s],hosts.org_dir", nbuf); 3145 } 3146 else if (p[1] != '\0') 3147 { 3148 /* multi token -- take only first token in nbuf */ 3149 *p = '\0'; 3150 (void) sm_snprintf(qbuf, sizeof qbuf, 3151 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]); 3152 } 3153 else 3154 { 3155 *statp = EX_NOHOST; 3156 return false; 3157 } 3158 3159 if (tTd(38, 20)) 3160 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n", 3161 name, qbuf); 3162 3163 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH, 3164 NULL, NULL); 3165 3166 if (result->status == NIS_SUCCESS) 3167 { 3168 int count; 3169 char *domain; 3170 3171 if ((count = NIS_RES_NUMOBJ(result)) != 1) 3172 { 3173 if (LogLevel > 10) 3174 sm_syslog(LOG_WARNING, CurEnv->e_id, 3175 "nisplus_getcanonname: lookup error, expected 1 entry, got %d", 3176 count); 3177 3178 /* ignore second entry */ 3179 if (tTd(38, 20)) 3180 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n", 3181 name, count); 3182 } 3183 3184 if (tTd(38, 20)) 3185 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n", 3186 name, (NIS_RES_OBJECT(result))->zo_domain); 3187 3188 3189 vp = ((NIS_RES_OBJECT(result))->EN_col(0)); 3190 vsize = strlen(vp); 3191 if (tTd(38, 20)) 3192 sm_dprintf("nisplus_getcanonname(%s), found %s\n", 3193 name, vp); 3194 if (strchr(vp, '.') != NULL) 3195 { 3196 domain = ""; 3197 } 3198 else 3199 { 3200 domain = macvalue('m', CurEnv); 3201 if (domain == NULL) 3202 domain = ""; 3203 } 3204 if (hbsize > vsize + (int) strlen(domain) + 1) 3205 { 3206 if (domain[0] == '\0') 3207 (void) sm_strlcpy(name, vp, hbsize); 3208 else 3209 (void) sm_snprintf(name, hbsize, 3210 "%s.%s", vp, domain); 3211 *statp = EX_OK; 3212 } 3213 else 3214 *statp = EX_NOHOST; 3215 nis_freeresult(result); 3216 return true; 3217 } 3218 else 3219 { 3220 if (result->status == NIS_NOTFOUND) 3221 *statp = EX_NOHOST; 3222 else if (result->status == NIS_TRYAGAIN) 3223 *statp = EX_TEMPFAIL; 3224 else 3225 *statp = EX_UNAVAILABLE; 3226 } 3227 if (tTd(38, 20)) 3228 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n", 3229 name, result->status, *statp); 3230 nis_freeresult(result); 3231 return false; 3232 } 3233 3234 char * 3235 nisplus_default_domain() 3236 { 3237 static char default_domain[MAXNAME + 1] = ""; 3238 char *p; 3239 3240 if (default_domain[0] != '\0') 3241 return default_domain; 3242 3243 p = nis_local_directory(); 3244 (void) sm_strlcpy(default_domain, p, sizeof default_domain); 3245 return default_domain; 3246 } 3247 3248 #endif /* NISPLUS */ 3249 /* 3250 ** LDAP Modules 3251 */ 3252 3253 /* 3254 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs 3255 */ 3256 3257 #if defined(LDAPMAP) || defined(PH_MAP) 3258 3259 # if PH_MAP 3260 # define ph_map_dequote ldapmap_dequote 3261 # endif /* PH_MAP */ 3262 3263 static char *ldapmap_dequote __P((char *)); 3264 3265 static char * 3266 ldapmap_dequote(str) 3267 char *str; 3268 { 3269 char *p; 3270 char *start; 3271 3272 if (str == NULL) 3273 return NULL; 3274 3275 p = str; 3276 if (*p == '"') 3277 { 3278 /* Should probably swallow initial whitespace here */ 3279 start = ++p; 3280 } 3281 else 3282 return str; 3283 while (*p != '"' && *p != '\0') 3284 p++; 3285 if (*p != '\0') 3286 *p = '\0'; 3287 return start; 3288 } 3289 #endif /* defined(LDAPMAP) || defined(PH_MAP) */ 3290 3291 #if LDAPMAP 3292 3293 static SM_LDAP_STRUCT *LDAPDefaults = NULL; 3294 3295 /* 3296 ** LDAPMAP_OPEN -- open LDAP map 3297 ** 3298 ** Connect to the LDAP server. Re-use existing connections since a 3299 ** single server connection to a host (with the same host, port, 3300 ** bind DN, and secret) can answer queries for multiple maps. 3301 */ 3302 3303 bool 3304 ldapmap_open(map, mode) 3305 MAP *map; 3306 int mode; 3307 { 3308 SM_LDAP_STRUCT *lmap; 3309 STAB *s; 3310 3311 if (tTd(38, 2)) 3312 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode); 3313 3314 mode &= O_ACCMODE; 3315 3316 /* sendmail doesn't have the ability to write to LDAP (yet) */ 3317 if (mode != O_RDONLY) 3318 { 3319 /* issue a pseudo-error message */ 3320 errno = SM_EMAPCANTWRITE; 3321 return false; 3322 } 3323 3324 lmap = (SM_LDAP_STRUCT *) map->map_db1; 3325 3326 s = ldapmap_findconn(lmap); 3327 if (s->s_lmap != NULL) 3328 { 3329 /* Already have a connection open to this LDAP server */ 3330 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld; 3331 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid; 3332 3333 /* Add this map as head of linked list */ 3334 lmap->ldap_next = s->s_lmap; 3335 s->s_lmap = map; 3336 3337 if (tTd(38, 2)) 3338 sm_dprintf("using cached connection\n"); 3339 return true; 3340 } 3341 3342 if (tTd(38, 2)) 3343 sm_dprintf("opening new connection\n"); 3344 3345 /* No connection yet, connect */ 3346 if (!sm_ldap_start(map->map_mname, lmap)) 3347 { 3348 if (errno == ETIMEDOUT) 3349 { 3350 if (LogLevel > 1) 3351 sm_syslog(LOG_NOTICE, CurEnv->e_id, 3352 "timeout conning to LDAP server %.100s", 3353 lmap->ldap_target == NULL ? "localhost" : lmap->ldap_target); 3354 } 3355 3356 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3357 { 3358 if (bitset(MF_NODEFER, map->map_mflags)) 3359 syserr("%s failed to %s in map %s", 3360 # if USE_LDAP_INIT 3361 "ldap_init/ldap_bind", 3362 # else /* USE_LDAP_INIT */ 3363 "ldap_open", 3364 # endif /* USE_LDAP_INIT */ 3365 lmap->ldap_target == NULL ? "localhost" 3366 : lmap->ldap_target, 3367 map->map_mname); 3368 else 3369 syserr("451 4.3.5 %s failed to %s in map %s", 3370 # if USE_LDAP_INIT 3371 "ldap_init/ldap_bind", 3372 # else /* USE_LDAP_INIT */ 3373 "ldap_open", 3374 # endif /* USE_LDAP_INIT */ 3375 lmap->ldap_target == NULL ? "localhost" 3376 : lmap->ldap_target, 3377 map->map_mname); 3378 } 3379 return false; 3380 } 3381 3382 /* Save connection for reuse */ 3383 s->s_lmap = map; 3384 return true; 3385 } 3386 3387 /* 3388 ** LDAPMAP_CLOSE -- close ldap map 3389 */ 3390 3391 void 3392 ldapmap_close(map) 3393 MAP *map; 3394 { 3395 SM_LDAP_STRUCT *lmap; 3396 STAB *s; 3397 3398 if (tTd(38, 2)) 3399 sm_dprintf("ldapmap_close(%s)\n", map->map_mname); 3400 3401 lmap = (SM_LDAP_STRUCT *) map->map_db1; 3402 3403 /* Check if already closed */ 3404 if (lmap->ldap_ld == NULL) 3405 return; 3406 3407 /* Close the LDAP connection */ 3408 sm_ldap_close(lmap); 3409 3410 /* Mark all the maps that share the connection as closed */ 3411 s = ldapmap_findconn(lmap); 3412 3413 while (s->s_lmap != NULL) 3414 { 3415 MAP *smap = s->s_lmap; 3416 3417 if (tTd(38, 2) && smap != map) 3418 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n", 3419 map->map_mname, smap->map_mname); 3420 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 3421 lmap = (SM_LDAP_STRUCT *) smap->map_db1; 3422 lmap->ldap_ld = NULL; 3423 s->s_lmap = lmap->ldap_next; 3424 lmap->ldap_next = NULL; 3425 } 3426 } 3427 3428 # ifdef SUNET_ID 3429 /* 3430 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form 3431 ** This only makes sense at Stanford University. 3432 */ 3433 3434 static char * 3435 sunet_id_hash(str) 3436 char *str; 3437 { 3438 char *p, *p_last; 3439 3440 p = str; 3441 p_last = p; 3442 while (*p != '\0') 3443 { 3444 if (islower(*p) || isdigit(*p)) 3445 { 3446 *p_last = *p; 3447 p_last++; 3448 } 3449 else if (isupper(*p)) 3450 { 3451 *p_last = tolower(*p); 3452 p_last++; 3453 } 3454 ++p; 3455 } 3456 if (*p_last != '\0') 3457 *p_last = '\0'; 3458 return str; 3459 } 3460 # endif /* SUNET_ID */ 3461 3462 /* 3463 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map 3464 */ 3465 3466 char * 3467 ldapmap_lookup(map, name, av, statp) 3468 MAP *map; 3469 char *name; 3470 char **av; 3471 int *statp; 3472 { 3473 # if _FFR_LDAP_RECURSION 3474 int plen = 0; 3475 int psize = 0; 3476 # else /* _FFR_LDAP_RECURSION */ 3477 int entries = 0; 3478 int i; 3479 int ret; 3480 int vsize; 3481 # endif /* _FFR_LDAP_RECURSION */ 3482 int msgid; 3483 int save_errno; 3484 char *vp, *p; 3485 char *result = NULL; 3486 SM_LDAP_STRUCT *lmap = NULL; 3487 char keybuf[MAXNAME + 1]; 3488 3489 if (tTd(38, 20)) 3490 sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name); 3491 3492 /* Get ldap struct pointer from map */ 3493 lmap = (SM_LDAP_STRUCT *) map->map_db1; 3494 sm_ldap_setopts(lmap->ldap_ld, lmap); 3495 3496 (void) sm_strlcpy(keybuf, name, sizeof keybuf); 3497 3498 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3499 { 3500 # ifdef SUNET_ID 3501 sunet_id_hash(keybuf); 3502 # else /* SUNET_ID */ 3503 makelower(keybuf); 3504 # endif /* SUNET_ID */ 3505 } 3506 3507 msgid = sm_ldap_search(lmap, keybuf); 3508 if (msgid == -1) 3509 { 3510 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE; 3511 save_errno = errno; 3512 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3513 { 3514 if (bitset(MF_NODEFER, map->map_mflags)) 3515 syserr("Error in ldap_search using %s in map %s", 3516 keybuf, map->map_mname); 3517 else 3518 syserr("451 4.3.5 Error in ldap_search using %s in map %s", 3519 keybuf, map->map_mname); 3520 } 3521 *statp = EX_TEMPFAIL; 3522 switch (save_errno - E_LDAPBASE) 3523 { 3524 # ifdef LDAP_SERVER_DOWN 3525 case LDAP_SERVER_DOWN: 3526 # endif /* LDAP_SERVER_DOWN */ 3527 case LDAP_TIMEOUT: 3528 case LDAP_UNAVAILABLE: 3529 /* server disappeared, try reopen on next search */ 3530 ldapmap_close(map); 3531 break; 3532 } 3533 errno = save_errno; 3534 return NULL; 3535 } 3536 3537 *statp = EX_NOTFOUND; 3538 vp = NULL; 3539 3540 # if _FFR_LDAP_RECURSION 3541 { 3542 int flags; 3543 SM_RPOOL_T *rpool; 3544 3545 flags = 0; 3546 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 3547 flags |= SM_LDAP_SINGLEMATCH; 3548 if (bitset(MF_MATCHONLY, map->map_mflags)) 3549 flags |= SM_LDAP_MATCHONLY; 3550 3551 /* Create an rpool for search related memory usage */ 3552 rpool = sm_rpool_new_x(NULL); 3553 3554 p = NULL; 3555 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim, 3556 rpool, &p, &plen, &psize, NULL); 3557 save_errno = errno; 3558 3559 /* Copy result so rpool can be freed */ 3560 if (*statp == EX_OK && p != NULL) 3561 vp = newstr(p); 3562 sm_rpool_free(rpool); 3563 3564 /* need to restart LDAP connection? */ 3565 if (*statp == EX_RESTART) 3566 { 3567 *statp = EX_TEMPFAIL; 3568 ldapmap_close(map); 3569 } 3570 3571 errno = save_errno; 3572 if (*statp != EX_OK && *statp != EX_NOTFOUND) 3573 { 3574 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3575 { 3576 if (bitset(MF_NODEFER, map->map_mflags)) 3577 syserr("Error getting LDAP results in map %s", 3578 map->map_mname); 3579 else 3580 syserr("451 4.3.5 Error getting LDAP results in map %s", 3581 map->map_mname); 3582 } 3583 errno = save_errno; 3584 return NULL; 3585 } 3586 } 3587 # else /* _FFR_LDAP_RECURSION */ 3588 3589 /* Get results */ 3590 while ((ret = ldap_result(lmap->ldap_ld, msgid, 0, 3591 (lmap->ldap_timeout.tv_sec == 0 ? NULL : 3592 &(lmap->ldap_timeout)), 3593 &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY) 3594 { 3595 LDAPMessage *entry; 3596 3597 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 3598 { 3599 entries += ldap_count_entries(lmap->ldap_ld, 3600 lmap->ldap_res); 3601 if (entries > 1) 3602 { 3603 *statp = EX_NOTFOUND; 3604 if (lmap->ldap_res != NULL) 3605 { 3606 ldap_msgfree(lmap->ldap_res); 3607 lmap->ldap_res = NULL; 3608 } 3609 (void) ldap_abandon(lmap->ldap_ld, msgid); 3610 if (vp != NULL) 3611 sm_free(vp); /* XXX */ 3612 if (tTd(38, 25)) 3613 sm_dprintf("ldap search found multiple on a single match query\n"); 3614 return NULL; 3615 } 3616 } 3617 3618 /* If we don't want multiple values and we have one, break */ 3619 if (map->map_coldelim == '\0' && vp != NULL) 3620 break; 3621 3622 /* Cycle through all entries */ 3623 for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res); 3624 entry != NULL; 3625 entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res)) 3626 { 3627 BerElement *ber; 3628 char *attr; 3629 char **vals = NULL; 3630 3631 /* 3632 ** If matching only and found an entry, 3633 ** no need to spin through attributes 3634 */ 3635 3636 if (*statp == EX_OK && 3637 bitset(MF_MATCHONLY, map->map_mflags)) 3638 continue; 3639 3640 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 3641 /* 3642 ** Reset value to prevent lingering 3643 ** LDAP_DECODING_ERROR due to 3644 ** OpenLDAP 1.X's hack (see below) 3645 */ 3646 3647 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 3648 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 3649 3650 for (attr = ldap_first_attribute(lmap->ldap_ld, entry, 3651 &ber); 3652 attr != NULL; 3653 attr = ldap_next_attribute(lmap->ldap_ld, entry, 3654 ber)) 3655 { 3656 char *tmp, *vp_tmp; 3657 3658 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 3659 { 3660 vals = ldap_get_values(lmap->ldap_ld, 3661 entry, 3662 attr); 3663 if (vals == NULL) 3664 { 3665 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 3666 if (save_errno == LDAP_SUCCESS) 3667 { 3668 ldap_memfree(attr); 3669 continue; 3670 } 3671 3672 /* Must be an error */ 3673 save_errno += E_LDAPBASE; 3674 if (!bitset(MF_OPTIONAL, 3675 map->map_mflags)) 3676 { 3677 errno = save_errno; 3678 if (bitset(MF_NODEFER, 3679 map->map_mflags)) 3680 syserr("Error getting LDAP values in map %s", 3681 map->map_mname); 3682 else 3683 syserr("451 4.3.5 Error getting LDAP values in map %s", 3684 map->map_mname); 3685 } 3686 *statp = EX_TEMPFAIL; 3687 ldap_memfree(attr); 3688 if (lmap->ldap_res != NULL) 3689 { 3690 ldap_msgfree(lmap->ldap_res); 3691 lmap->ldap_res = NULL; 3692 } 3693 (void) ldap_abandon(lmap->ldap_ld, 3694 msgid); 3695 if (vp != NULL) 3696 sm_free(vp); /* XXX */ 3697 errno = save_errno; 3698 return NULL; 3699 } 3700 } 3701 3702 *statp = EX_OK; 3703 3704 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 3705 /* 3706 ** Reset value to prevent lingering 3707 ** LDAP_DECODING_ERROR due to 3708 ** OpenLDAP 1.X's hack (see below) 3709 */ 3710 3711 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 3712 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 3713 3714 /* 3715 ** If matching only, 3716 ** no need to spin through entries 3717 */ 3718 3719 if (bitset(MF_MATCHONLY, map->map_mflags)) 3720 { 3721 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 3722 ldap_value_free(vals); 3723 3724 ldap_memfree(attr); 3725 continue; 3726 } 3727 3728 /* 3729 ** If we don't want multiple values, 3730 ** return first found. 3731 */ 3732 3733 if (map->map_coldelim == '\0') 3734 { 3735 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 3736 { 3737 vp = newstr(attr); 3738 ldap_memfree(attr); 3739 break; 3740 } 3741 3742 if (vals[0] == NULL) 3743 { 3744 ldap_value_free(vals); 3745 ldap_memfree(attr); 3746 continue; 3747 } 3748 3749 vsize = strlen(vals[0]) + 1; 3750 if (lmap->ldap_attrsep != '\0') 3751 vsize += strlen(attr) + 1; 3752 vp = xalloc(vsize); 3753 if (lmap->ldap_attrsep != '\0') 3754 sm_snprintf(vp, vsize, 3755 "%s%c%s", 3756 attr, 3757 lmap->ldap_attrsep, 3758 vals[0]); 3759 else 3760 sm_strlcpy(vp, vals[0], vsize); 3761 ldap_value_free(vals); 3762 ldap_memfree(attr); 3763 break; 3764 } 3765 3766 /* attributes only */ 3767 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 3768 { 3769 if (vp == NULL) 3770 vp = newstr(attr); 3771 else 3772 { 3773 vsize = strlen(vp) + 3774 strlen(attr) + 2; 3775 tmp = xalloc(vsize); 3776 (void) sm_snprintf(tmp, 3777 vsize, "%s%c%s", 3778 vp, map->map_coldelim, 3779 attr); 3780 sm_free(vp); /* XXX */ 3781 vp = tmp; 3782 } 3783 ldap_memfree(attr); 3784 continue; 3785 } 3786 3787 /* 3788 ** If there is more than one, 3789 ** munge then into a map_coldelim 3790 ** separated string 3791 */ 3792 3793 vsize = 0; 3794 for (i = 0; vals[i] != NULL; i++) 3795 { 3796 vsize += strlen(vals[i]) + 1; 3797 if (lmap->ldap_attrsep != '\0') 3798 vsize += strlen(attr) + 1; 3799 } 3800 vp_tmp = xalloc(vsize); 3801 *vp_tmp = '\0'; 3802 3803 p = vp_tmp; 3804 for (i = 0; vals[i] != NULL; i++) 3805 { 3806 if (lmap->ldap_attrsep != '\0') 3807 { 3808 p += sm_strlcpy(p, attr, 3809 vsize - (p - vp_tmp)); 3810 if (p >= vp_tmp + vsize) 3811 syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values"); 3812 *p++ = lmap->ldap_attrsep; 3813 } 3814 p += sm_strlcpy(p, vals[i], 3815 vsize - (p - vp_tmp)); 3816 if (p >= vp_tmp + vsize) 3817 syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values"); 3818 if (vals[i + 1] != NULL) 3819 *p++ = map->map_coldelim; 3820 } 3821 3822 ldap_value_free(vals); 3823 ldap_memfree(attr); 3824 if (vp == NULL) 3825 { 3826 vp = vp_tmp; 3827 continue; 3828 } 3829 vsize = strlen(vp) + strlen(vp_tmp) + 2; 3830 tmp = xalloc(vsize); 3831 (void) sm_snprintf(tmp, vsize, "%s%c%s", 3832 vp, map->map_coldelim, vp_tmp); 3833 3834 sm_free(vp); /* XXX */ 3835 sm_free(vp_tmp); /* XXX */ 3836 vp = tmp; 3837 } 3838 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 3839 3840 /* 3841 ** We check errno != LDAP_DECODING_ERROR since 3842 ** OpenLDAP 1.X has a very ugly *undocumented* 3843 ** hack of returning this error code from 3844 ** ldap_next_attribute() if the library freed the 3845 ** ber attribute. See: 3846 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html 3847 */ 3848 3849 if (save_errno != LDAP_SUCCESS && 3850 save_errno != LDAP_DECODING_ERROR) 3851 { 3852 /* Must be an error */ 3853 save_errno += E_LDAPBASE; 3854 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3855 { 3856 errno = save_errno; 3857 if (bitset(MF_NODEFER, map->map_mflags)) 3858 syserr("Error getting LDAP attributes in map %s", 3859 map->map_mname); 3860 else 3861 syserr("451 4.3.5 Error getting LDAP attributes in map %s", 3862 map->map_mname); 3863 } 3864 *statp = EX_TEMPFAIL; 3865 if (lmap->ldap_res != NULL) 3866 { 3867 ldap_msgfree(lmap->ldap_res); 3868 lmap->ldap_res = NULL; 3869 } 3870 (void) ldap_abandon(lmap->ldap_ld, msgid); 3871 if (vp != NULL) 3872 sm_free(vp); /* XXX */ 3873 errno = save_errno; 3874 return NULL; 3875 } 3876 3877 /* We don't want multiple values and we have one */ 3878 if (map->map_coldelim == '\0' && vp != NULL) 3879 break; 3880 } 3881 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 3882 if (save_errno != LDAP_SUCCESS && 3883 save_errno != LDAP_DECODING_ERROR) 3884 { 3885 /* Must be an error */ 3886 save_errno += E_LDAPBASE; 3887 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3888 { 3889 errno = save_errno; 3890 if (bitset(MF_NODEFER, map->map_mflags)) 3891 syserr("Error getting LDAP entries in map %s", 3892 map->map_mname); 3893 else 3894 syserr("451 4.3.5 Error getting LDAP entries in map %s", 3895 map->map_mname); 3896 } 3897 *statp = EX_TEMPFAIL; 3898 if (lmap->ldap_res != NULL) 3899 { 3900 ldap_msgfree(lmap->ldap_res); 3901 lmap->ldap_res = NULL; 3902 } 3903 (void) ldap_abandon(lmap->ldap_ld, msgid); 3904 if (vp != NULL) 3905 sm_free(vp); /* XXX */ 3906 errno = save_errno; 3907 return NULL; 3908 } 3909 ldap_msgfree(lmap->ldap_res); 3910 lmap->ldap_res = NULL; 3911 } 3912 3913 if (ret == 0) 3914 save_errno = ETIMEDOUT; 3915 else 3916 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 3917 if (save_errno != LDAP_SUCCESS) 3918 { 3919 if (ret != 0) 3920 save_errno += E_LDAPBASE; 3921 3922 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3923 { 3924 errno = save_errno; 3925 if (bitset(MF_NODEFER, map->map_mflags)) 3926 syserr("Error getting LDAP results in map %s", 3927 map->map_mname); 3928 else 3929 syserr("451 4.3.5 Error getting LDAP results in map %s", 3930 map->map_mname); 3931 } 3932 *statp = EX_TEMPFAIL; 3933 if (vp != NULL) 3934 sm_free(vp); /* XXX */ 3935 3936 switch (save_errno - E_LDAPBASE) 3937 { 3938 # ifdef LDAP_SERVER_DOWN 3939 case LDAP_SERVER_DOWN: 3940 # endif /* LDAP_SERVER_DOWN */ 3941 case LDAP_TIMEOUT: 3942 case LDAP_UNAVAILABLE: 3943 /* server disappeared, try reopen on next search */ 3944 ldapmap_close(map); 3945 break; 3946 } 3947 errno = save_errno; 3948 return NULL; 3949 } 3950 # endif /* _FFR_LDAP_RECURSION */ 3951 3952 /* Did we match anything? */ 3953 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags)) 3954 return NULL; 3955 3956 if (*statp == EX_OK) 3957 { 3958 if (LogLevel > 9) 3959 sm_syslog(LOG_INFO, CurEnv->e_id, 3960 "ldap %.100s => %s", name, 3961 vp == NULL ? "<NULL>" : vp); 3962 if (bitset(MF_MATCHONLY, map->map_mflags)) 3963 result = map_rewrite(map, name, strlen(name), NULL); 3964 else 3965 { 3966 /* vp != NULL according to test above */ 3967 result = map_rewrite(map, vp, strlen(vp), av); 3968 } 3969 if (vp != NULL) 3970 sm_free(vp); /* XXX */ 3971 } 3972 return result; 3973 } 3974 3975 /* 3976 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server 3977 ** 3978 ** Cache LDAP connections based on the host, port, bind DN, 3979 ** secret, and PID so we don't have multiple connections open to 3980 ** the same server for different maps. Need a separate connection 3981 ** per PID since a parent process may close the map before the 3982 ** child is done with it. 3983 ** 3984 ** Parameters: 3985 ** lmap -- LDAP map information 3986 ** 3987 ** Returns: 3988 ** Symbol table entry for the LDAP connection. 3989 */ 3990 3991 static STAB * 3992 ldapmap_findconn(lmap) 3993 SM_LDAP_STRUCT *lmap; 3994 { 3995 char *format; 3996 char *nbuf; 3997 STAB *SM_NONVOLATILE s = NULL; 3998 3999 # if _FFR_LDAP_SETVERSION 4000 format = "%s%c%d%c%d%c%s%c%s%d"; 4001 # else /* _FFR_LDAP_SETVERSION */ 4002 format = "%s%c%d%c%s%c%s%d"; 4003 # endif /* _FFR_LDAP_SETVERSION */ 4004 nbuf = sm_stringf_x(format, 4005 (lmap->ldap_target == NULL ? "localhost" 4006 : lmap->ldap_target), 4007 CONDELSE, 4008 lmap->ldap_port, 4009 CONDELSE, 4010 # if _FFR_LDAP_SETVERSION 4011 lmap->ldap_version, 4012 CONDELSE, 4013 # endif /* _FFR_LDAP_SETVERSION */ 4014 (lmap->ldap_binddn == NULL ? "" 4015 : lmap->ldap_binddn), 4016 CONDELSE, 4017 (lmap->ldap_secret == NULL ? "" 4018 : lmap->ldap_secret), 4019 (int) CurrentPid); 4020 SM_TRY 4021 s = stab(nbuf, ST_LMAP, ST_ENTER); 4022 SM_FINALLY 4023 sm_free(nbuf); 4024 SM_END_TRY 4025 return s; 4026 } 4027 /* 4028 ** LDAPMAP_PARSEARGS -- parse ldap map definition args. 4029 */ 4030 4031 static struct lamvalues LDAPAuthMethods[] = 4032 { 4033 { "none", LDAP_AUTH_NONE }, 4034 { "simple", LDAP_AUTH_SIMPLE }, 4035 # ifdef LDAP_AUTH_KRBV4 4036 { "krbv4", LDAP_AUTH_KRBV4 }, 4037 # endif /* LDAP_AUTH_KRBV4 */ 4038 { NULL, 0 } 4039 }; 4040 4041 static struct ladvalues LDAPAliasDereference[] = 4042 { 4043 { "never", LDAP_DEREF_NEVER }, 4044 { "always", LDAP_DEREF_ALWAYS }, 4045 { "search", LDAP_DEREF_SEARCHING }, 4046 { "find", LDAP_DEREF_FINDING }, 4047 { NULL, 0 } 4048 }; 4049 4050 static struct lssvalues LDAPSearchScope[] = 4051 { 4052 { "base", LDAP_SCOPE_BASE }, 4053 { "one", LDAP_SCOPE_ONELEVEL }, 4054 { "sub", LDAP_SCOPE_SUBTREE }, 4055 { NULL, 0 } 4056 }; 4057 4058 bool 4059 ldapmap_parseargs(map, args) 4060 MAP *map; 4061 char *args; 4062 { 4063 bool secretread = true; 4064 # if _FFR_LDAP_URI 4065 bool ldaphost = false; 4066 # endif /* _FFR_LDAP_URI */ 4067 int i; 4068 register char *p = args; 4069 SM_LDAP_STRUCT *lmap; 4070 struct lamvalues *lam; 4071 struct ladvalues *lad; 4072 struct lssvalues *lss; 4073 char ldapfilt[MAXLINE]; 4074 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD]; 4075 4076 /* Get ldap struct pointer from map */ 4077 lmap = (SM_LDAP_STRUCT *) map->map_db1; 4078 4079 /* Check if setting the initial LDAP defaults */ 4080 if (lmap == NULL || lmap != LDAPDefaults) 4081 { 4082 /* We need to alloc an SM_LDAP_STRUCT struct */ 4083 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap); 4084 if (LDAPDefaults == NULL) 4085 sm_ldap_clear(lmap); 4086 else 4087 STRUCTCOPY(*LDAPDefaults, *lmap); 4088 } 4089 4090 /* there is no check whether there is really an argument */ 4091 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 4092 map->map_spacesub = SpaceSub; /* default value */ 4093 4094 /* Check if setting up an alias or file class LDAP map */ 4095 if (bitset(MF_ALIAS, map->map_mflags)) 4096 { 4097 /* Comma separate if used as an alias file */ 4098 map->map_coldelim = ','; 4099 if (*args == '\0') 4100 { 4101 int n; 4102 char *lc; 4103 char jbuf[MAXHOSTNAMELEN]; 4104 char lcbuf[MAXLINE]; 4105 4106 /* Get $j */ 4107 expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope); 4108 if (jbuf[0] == '\0') 4109 { 4110 (void) sm_strlcpy(jbuf, "localhost", 4111 sizeof jbuf); 4112 } 4113 4114 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv); 4115 if (lc == NULL) 4116 lc = ""; 4117 else 4118 { 4119 expand(lc, lcbuf, sizeof lcbuf, CurEnv); 4120 lc = lcbuf; 4121 } 4122 4123 n = sm_snprintf(ldapfilt, sizeof ldapfilt, 4124 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))", 4125 lc, jbuf); 4126 if (n >= sizeof ldapfilt) 4127 { 4128 syserr("%s: Default LDAP string too long", 4129 map->map_mname); 4130 return false; 4131 } 4132 4133 /* default args for an alias LDAP entry */ 4134 lmap->ldap_filter = ldapfilt; 4135 lmap->ldap_attr[0] = "sendmailMTAAliasValue"; 4136 lmap->ldap_attr[1] = NULL; 4137 } 4138 } 4139 else if (bitset(MF_FILECLASS, map->map_mflags)) 4140 { 4141 /* Space separate if used as a file class file */ 4142 map->map_coldelim = ' '; 4143 } 4144 4145 for (;;) 4146 { 4147 while (isascii(*p) && isspace(*p)) 4148 p++; 4149 if (*p != '-') 4150 break; 4151 switch (*++p) 4152 { 4153 case 'N': 4154 map->map_mflags |= MF_INCLNULL; 4155 map->map_mflags &= ~MF_TRY0NULL; 4156 break; 4157 4158 case 'O': 4159 map->map_mflags &= ~MF_TRY1NULL; 4160 break; 4161 4162 case 'o': 4163 map->map_mflags |= MF_OPTIONAL; 4164 break; 4165 4166 case 'f': 4167 map->map_mflags |= MF_NOFOLDCASE; 4168 break; 4169 4170 case 'm': 4171 map->map_mflags |= MF_MATCHONLY; 4172 break; 4173 4174 case 'A': 4175 map->map_mflags |= MF_APPEND; 4176 break; 4177 4178 case 'q': 4179 map->map_mflags |= MF_KEEPQUOTES; 4180 break; 4181 4182 case 'a': 4183 map->map_app = ++p; 4184 break; 4185 4186 case 'T': 4187 map->map_tapp = ++p; 4188 break; 4189 4190 case 't': 4191 map->map_mflags |= MF_NODEFER; 4192 break; 4193 4194 case 'S': 4195 map->map_spacesub = *++p; 4196 break; 4197 4198 case 'D': 4199 map->map_mflags |= MF_DEFER; 4200 break; 4201 4202 case 'z': 4203 if (*++p != '\\') 4204 map->map_coldelim = *p; 4205 else 4206 { 4207 switch (*++p) 4208 { 4209 case 'n': 4210 map->map_coldelim = '\n'; 4211 break; 4212 4213 case 't': 4214 map->map_coldelim = '\t'; 4215 break; 4216 4217 default: 4218 map->map_coldelim = '\\'; 4219 } 4220 } 4221 break; 4222 4223 /* Start of ldapmap specific args */ 4224 case 'V': 4225 if (*++p != '\\') 4226 lmap->ldap_attrsep = *p; 4227 else 4228 { 4229 switch (*++p) 4230 { 4231 case 'n': 4232 lmap->ldap_attrsep = '\n'; 4233 break; 4234 4235 case 't': 4236 lmap->ldap_attrsep = '\t'; 4237 break; 4238 4239 default: 4240 lmap->ldap_attrsep = '\\'; 4241 } 4242 } 4243 break; 4244 4245 case 'k': /* search field */ 4246 while (isascii(*++p) && isspace(*p)) 4247 continue; 4248 lmap->ldap_filter = p; 4249 break; 4250 4251 case 'v': /* attr to return */ 4252 while (isascii(*++p) && isspace(*p)) 4253 continue; 4254 lmap->ldap_attr[0] = p; 4255 lmap->ldap_attr[1] = NULL; 4256 break; 4257 4258 case '1': 4259 map->map_mflags |= MF_SINGLEMATCH; 4260 break; 4261 4262 /* args stolen from ldapsearch.c */ 4263 case 'R': /* don't auto chase referrals */ 4264 # ifdef LDAP_REFERRALS 4265 lmap->ldap_options &= ~LDAP_OPT_REFERRALS; 4266 # else /* LDAP_REFERRALS */ 4267 syserr("compile with -DLDAP_REFERRALS for referral support"); 4268 # endif /* LDAP_REFERRALS */ 4269 break; 4270 4271 case 'n': /* retrieve attribute names only */ 4272 lmap->ldap_attrsonly = LDAPMAP_TRUE; 4273 break; 4274 4275 case 'r': /* alias dereferencing */ 4276 while (isascii(*++p) && isspace(*p)) 4277 continue; 4278 4279 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0) 4280 p += 11; 4281 4282 for (lad = LDAPAliasDereference; 4283 lad != NULL && lad->lad_name != NULL; lad++) 4284 { 4285 if (sm_strncasecmp(p, lad->lad_name, 4286 strlen(lad->lad_name)) == 0) 4287 break; 4288 } 4289 if (lad->lad_name != NULL) 4290 lmap->ldap_deref = lad->lad_code; 4291 else 4292 { 4293 /* bad config line */ 4294 if (!bitset(MCF_OPTFILE, 4295 map->map_class->map_cflags)) 4296 { 4297 char *ptr; 4298 4299 if ((ptr = strchr(p, ' ')) != NULL) 4300 *ptr = '\0'; 4301 syserr("Deref must be [never|always|search|find] (not %s) in map %s", 4302 p, map->map_mname); 4303 if (ptr != NULL) 4304 *ptr = ' '; 4305 return false; 4306 } 4307 } 4308 break; 4309 4310 case 's': /* search scope */ 4311 while (isascii(*++p) && isspace(*p)) 4312 continue; 4313 4314 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0) 4315 p += 11; 4316 4317 for (lss = LDAPSearchScope; 4318 lss != NULL && lss->lss_name != NULL; lss++) 4319 { 4320 if (sm_strncasecmp(p, lss->lss_name, 4321 strlen(lss->lss_name)) == 0) 4322 break; 4323 } 4324 if (lss->lss_name != NULL) 4325 lmap->ldap_scope = lss->lss_code; 4326 else 4327 { 4328 /* bad config line */ 4329 if (!bitset(MCF_OPTFILE, 4330 map->map_class->map_cflags)) 4331 { 4332 char *ptr; 4333 4334 if ((ptr = strchr(p, ' ')) != NULL) 4335 *ptr = '\0'; 4336 syserr("Scope must be [base|one|sub] (not %s) in map %s", 4337 p, map->map_mname); 4338 if (ptr != NULL) 4339 *ptr = ' '; 4340 return false; 4341 } 4342 } 4343 break; 4344 4345 case 'h': /* ldap host */ 4346 while (isascii(*++p) && isspace(*p)) 4347 continue; 4348 # if _FFR_LDAP_URI 4349 if (lmap->ldap_uri) 4350 { 4351 syserr("Can not specify both an LDAP host and an LDAP URI in map %s", 4352 map->map_mname); 4353 return false; 4354 } 4355 ldaphost = true; 4356 # endif /* _FFR_LDAP_URI */ 4357 lmap->ldap_target = p; 4358 break; 4359 4360 case 'b': /* search base */ 4361 while (isascii(*++p) && isspace(*p)) 4362 continue; 4363 lmap->ldap_base = p; 4364 break; 4365 4366 case 'p': /* ldap port */ 4367 while (isascii(*++p) && isspace(*p)) 4368 continue; 4369 lmap->ldap_port = atoi(p); 4370 break; 4371 4372 case 'l': /* time limit */ 4373 while (isascii(*++p) && isspace(*p)) 4374 continue; 4375 lmap->ldap_timelimit = atoi(p); 4376 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit; 4377 break; 4378 4379 case 'Z': 4380 while (isascii(*++p) && isspace(*p)) 4381 continue; 4382 lmap->ldap_sizelimit = atoi(p); 4383 break; 4384 4385 case 'd': /* Dn to bind to server as */ 4386 while (isascii(*++p) && isspace(*p)) 4387 continue; 4388 lmap->ldap_binddn = p; 4389 break; 4390 4391 case 'M': /* Method for binding */ 4392 while (isascii(*++p) && isspace(*p)) 4393 continue; 4394 4395 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0) 4396 p += 10; 4397 4398 for (lam = LDAPAuthMethods; 4399 lam != NULL && lam->lam_name != NULL; lam++) 4400 { 4401 if (sm_strncasecmp(p, lam->lam_name, 4402 strlen(lam->lam_name)) == 0) 4403 break; 4404 } 4405 if (lam->lam_name != NULL) 4406 lmap->ldap_method = lam->lam_code; 4407 else 4408 { 4409 /* bad config line */ 4410 if (!bitset(MCF_OPTFILE, 4411 map->map_class->map_cflags)) 4412 { 4413 char *ptr; 4414 4415 if ((ptr = strchr(p, ' ')) != NULL) 4416 *ptr = '\0'; 4417 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s", 4418 p, map->map_mname); 4419 if (ptr != NULL) 4420 *ptr = ' '; 4421 return false; 4422 } 4423 } 4424 4425 break; 4426 4427 /* 4428 ** This is a string that is dependent on the 4429 ** method used defined above. 4430 */ 4431 4432 case 'P': /* Secret password for binding */ 4433 while (isascii(*++p) && isspace(*p)) 4434 continue; 4435 lmap->ldap_secret = p; 4436 secretread = false; 4437 break; 4438 4439 # if _FFR_LDAP_URI 4440 case 'H': /* Use LDAP URI */ 4441 # if !USE_LDAP_INIT 4442 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s", 4443 map->map_mname); 4444 return false; 4445 # else /* !USE_LDAP_INIT */ 4446 if (ldaphost) 4447 { 4448 syserr("Can not specify both an LDAP host and an LDAP URI in map %s", 4449 map->map_mname); 4450 return false; 4451 } 4452 while (isascii(*++p) && isspace(*p)) 4453 continue; 4454 lmap->ldap_target = p; 4455 lmap->ldap_uri = true; 4456 break; 4457 # endif /* !USE_LDAP_INIT */ 4458 # endif /* _FFR_LDAP_URI */ 4459 4460 # if _FFR_LDAP_SETVERSION 4461 case 'w': 4462 /* -w should be for passwd, -P should be for version */ 4463 while (isascii(*++p) && isspace(*p)) 4464 continue; 4465 lmap->ldap_version = atoi(p); 4466 # ifdef LDAP_VERSION_MAX 4467 if (lmap->ldap_version > LDAP_VERSION_MAX) 4468 { 4469 syserr("LDAP version %d exceeds max of %d in map %s", 4470 lmap->ldap_version, LDAP_VERSION_MAX, 4471 map->map_mname); 4472 return false; 4473 } 4474 # endif /* LDAP_VERSION_MAX */ 4475 # ifdef LDAP_VERSION_MIN 4476 if (lmap->ldap_version < LDAP_VERSION_MIN) 4477 { 4478 syserr("LDAP version %d is lower than min of %d in map %s", 4479 lmap->ldap_version, LDAP_VERSION_MIN, 4480 map->map_mname); 4481 return false; 4482 } 4483 # endif /* LDAP_VERSION_MIN */ 4484 break; 4485 # endif /* _FFR_LDAP_SETVERSION */ 4486 4487 default: 4488 syserr("Illegal option %c map %s", *p, map->map_mname); 4489 break; 4490 } 4491 4492 /* need to account for quoted strings here */ 4493 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 4494 { 4495 if (*p == '"') 4496 { 4497 while (*++p != '"' && *p != '\0') 4498 continue; 4499 if (*p != '\0') 4500 p++; 4501 } 4502 else 4503 p++; 4504 } 4505 4506 if (*p != '\0') 4507 *p++ = '\0'; 4508 } 4509 4510 if (map->map_app != NULL) 4511 map->map_app = newstr(ldapmap_dequote(map->map_app)); 4512 if (map->map_tapp != NULL) 4513 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp)); 4514 4515 /* 4516 ** We need to swallow up all the stuff into a struct 4517 ** and dump it into map->map_dbptr1 4518 */ 4519 4520 if (lmap->ldap_target != NULL && 4521 (LDAPDefaults == NULL || 4522 LDAPDefaults == lmap || 4523 LDAPDefaults->ldap_target != lmap->ldap_target)) 4524 lmap->ldap_target = newstr(ldapmap_dequote(lmap->ldap_target)); 4525 map->map_domain = lmap->ldap_target; 4526 4527 if (lmap->ldap_binddn != NULL && 4528 (LDAPDefaults == NULL || 4529 LDAPDefaults == lmap || 4530 LDAPDefaults->ldap_binddn != lmap->ldap_binddn)) 4531 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn)); 4532 4533 if (lmap->ldap_secret != NULL && 4534 (LDAPDefaults == NULL || 4535 LDAPDefaults == lmap || 4536 LDAPDefaults->ldap_secret != lmap->ldap_secret)) 4537 { 4538 SM_FILE_T *sfd; 4539 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES; 4540 4541 if (DontLockReadFiles) 4542 sff |= SFF_NOLOCK; 4543 4544 /* need to use method to map secret to passwd string */ 4545 switch (lmap->ldap_method) 4546 { 4547 case LDAP_AUTH_NONE: 4548 /* Do nothing */ 4549 break; 4550 4551 case LDAP_AUTH_SIMPLE: 4552 4553 /* 4554 ** Secret is the name of a file with 4555 ** the first line as the password. 4556 */ 4557 4558 /* Already read in the secret? */ 4559 if (secretread) 4560 break; 4561 4562 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret), 4563 O_RDONLY, 0, sff); 4564 if (sfd == NULL) 4565 { 4566 syserr("LDAP map: cannot open secret %s", 4567 ldapmap_dequote(lmap->ldap_secret)); 4568 return false; 4569 } 4570 lmap->ldap_secret = sfgets(m_tmp, sizeof m_tmp, 4571 sfd, TimeOuts.to_fileopen, 4572 "ldapmap_parseargs"); 4573 (void) sm_io_close(sfd, SM_TIME_DEFAULT); 4574 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD) 4575 { 4576 syserr("LDAP map: secret in %s too long", 4577 ldapmap_dequote(lmap->ldap_secret)); 4578 return false; 4579 } 4580 if (lmap->ldap_secret != NULL && 4581 strlen(m_tmp) > 0) 4582 { 4583 /* chomp newline */ 4584 if (m_tmp[strlen(m_tmp) - 1] == '\n') 4585 m_tmp[strlen(m_tmp) - 1] = '\0'; 4586 4587 lmap->ldap_secret = m_tmp; 4588 } 4589 break; 4590 4591 # ifdef LDAP_AUTH_KRBV4 4592 case LDAP_AUTH_KRBV4: 4593 4594 /* 4595 ** Secret is where the ticket file is 4596 ** stashed 4597 */ 4598 4599 (void) sm_snprintf(m_tmp, sizeof m_tmp, 4600 "KRBTKFILE=%s", 4601 ldapmap_dequote(lmap->ldap_secret)); 4602 lmap->ldap_secret = m_tmp; 4603 break; 4604 # endif /* LDAP_AUTH_KRBV4 */ 4605 4606 default: /* Should NEVER get here */ 4607 syserr("LDAP map: Illegal value in lmap method"); 4608 return false; 4609 /* NOTREACHED */ 4610 break; 4611 } 4612 } 4613 4614 if (lmap->ldap_secret != NULL && 4615 (LDAPDefaults == NULL || 4616 LDAPDefaults == lmap || 4617 LDAPDefaults->ldap_secret != lmap->ldap_secret)) 4618 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret)); 4619 4620 if (lmap->ldap_base != NULL && 4621 (LDAPDefaults == NULL || 4622 LDAPDefaults == lmap || 4623 LDAPDefaults->ldap_base != lmap->ldap_base)) 4624 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base)); 4625 4626 /* 4627 ** Save the server from extra work. If request is for a single 4628 ** match, tell the server to only return enough records to 4629 ** determine if there is a single match or not. This can not 4630 ** be one since the server would only return one and we wouldn't 4631 ** know if there were others available. 4632 */ 4633 4634 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 4635 lmap->ldap_sizelimit = 2; 4636 4637 /* If setting defaults, don't process ldap_filter and ldap_attr */ 4638 if (lmap == LDAPDefaults) 4639 return true; 4640 4641 if (lmap->ldap_filter != NULL) 4642 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter)); 4643 else 4644 { 4645 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 4646 { 4647 syserr("No filter given in map %s", map->map_mname); 4648 return false; 4649 } 4650 } 4651 4652 if (lmap->ldap_attr[0] != NULL) 4653 { 4654 # if _FFR_LDAP_RECURSION 4655 bool recurse = false; 4656 bool normalseen = false; 4657 # endif /* _FFR_LDAP_RECURSION */ 4658 4659 i = 0; 4660 p = ldapmap_dequote(lmap->ldap_attr[0]); 4661 lmap->ldap_attr[0] = NULL; 4662 4663 # if _FFR_LDAP_RECURSION 4664 /* Prime the attr list with the objectClass attribute */ 4665 lmap->ldap_attr[i] = "objectClass"; 4666 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS; 4667 lmap->ldap_attr_needobjclass[i] = NULL; 4668 i++; 4669 # endif /* _FFR_LDAP_RECURSION */ 4670 4671 while (p != NULL) 4672 { 4673 char *v; 4674 4675 while (isascii(*p) && isspace(*p)) 4676 p++; 4677 if (*p == '\0') 4678 break; 4679 v = p; 4680 p = strchr(v, ','); 4681 if (p != NULL) 4682 *p++ = '\0'; 4683 4684 if (i >= LDAPMAP_MAX_ATTR) 4685 { 4686 syserr("Too many return attributes in %s (max %d)", 4687 map->map_mname, LDAPMAP_MAX_ATTR); 4688 return false; 4689 } 4690 if (*v != '\0') 4691 { 4692 # if _FFR_LDAP_RECURSION 4693 int j; 4694 int use; 4695 char *type; 4696 char *needobjclass; 4697 4698 type = strchr(v, ':'); 4699 if (type != NULL) 4700 { 4701 *type++ = '\0'; 4702 needobjclass = strchr(type, ':'); 4703 if (needobjclass != NULL) 4704 *needobjclass++ = '\0'; 4705 } 4706 else 4707 { 4708 needobjclass = NULL; 4709 } 4710 4711 use = i; 4712 4713 /* allow override on "objectClass" type */ 4714 if (sm_strcasecmp(v, "objectClass") == 0 && 4715 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS) 4716 { 4717 use = 0; 4718 } 4719 else 4720 { 4721 /* 4722 ** Don't add something to attribute 4723 ** list twice. 4724 */ 4725 4726 for (j = 1; j < i; j++) 4727 { 4728 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0) 4729 { 4730 syserr("Duplicate attribute (%s) in %s", 4731 v, map->map_mname); 4732 return false; 4733 } 4734 } 4735 4736 lmap->ldap_attr[use] = newstr(v); 4737 if (needobjclass != NULL && 4738 *needobjclass != '\0' && 4739 *needobjclass != '*') 4740 { 4741 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass); 4742 } 4743 else 4744 { 4745 lmap->ldap_attr_needobjclass[use] = NULL; 4746 } 4747 4748 } 4749 4750 if (type != NULL && *type != '\0') 4751 { 4752 if (sm_strcasecmp(type, "dn") == 0) 4753 { 4754 recurse = true; 4755 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN; 4756 } 4757 else if (sm_strcasecmp(type, "filter") == 0) 4758 { 4759 recurse = true; 4760 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER; 4761 } 4762 else if (sm_strcasecmp(type, "url") == 0) 4763 { 4764 recurse = true; 4765 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL; 4766 } 4767 else if (sm_strcasecmp(type, "normal") == 0) 4768 { 4769 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL; 4770 normalseen = true; 4771 } 4772 else 4773 { 4774 syserr("Unknown attribute type (%s) in %s", 4775 type, map->map_mname); 4776 return false; 4777 } 4778 } 4779 else 4780 { 4781 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL; 4782 normalseen = true; 4783 } 4784 # else /* _FFR_LDAP_RECURSION */ 4785 lmap->ldap_attr[i] = newstr(v); 4786 # endif /* _FFR_LDAP_RECURSION */ 4787 i++; 4788 } 4789 } 4790 lmap->ldap_attr[i] = NULL; 4791 # if _FFR_LDAP_RECURSION 4792 if (recurse && !normalseen) 4793 { 4794 syserr("LDAP recursion requested in %s but no returnable attribute given", 4795 map->map_mname); 4796 return false; 4797 } 4798 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE) 4799 { 4800 syserr("LDAP recursion requested in %s can not be used with -n", 4801 map->map_mname); 4802 return false; 4803 } 4804 # endif /* _FFR_LDAP_RECURSION */ 4805 } 4806 map->map_db1 = (ARBPTR_T) lmap; 4807 return true; 4808 } 4809 4810 /* 4811 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf 4812 ** 4813 ** Parameters: 4814 ** spec -- map argument string from LDAPDefaults option 4815 ** 4816 ** Returns: 4817 ** None. 4818 */ 4819 4820 void 4821 ldapmap_set_defaults(spec) 4822 char *spec; 4823 { 4824 STAB *class; 4825 MAP map; 4826 4827 /* Allocate and set the default values */ 4828 if (LDAPDefaults == NULL) 4829 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults); 4830 sm_ldap_clear(LDAPDefaults); 4831 4832 memset(&map, '\0', sizeof map); 4833 4834 /* look up the class */ 4835 class = stab("ldap", ST_MAPCLASS, ST_FIND); 4836 if (class == NULL) 4837 { 4838 syserr("readcf: LDAPDefaultSpec: class ldap not available"); 4839 return; 4840 } 4841 map.map_class = &class->s_mapclass; 4842 map.map_db1 = (ARBPTR_T) LDAPDefaults; 4843 map.map_mname = "O LDAPDefaultSpec"; 4844 4845 (void) ldapmap_parseargs(&map, spec); 4846 4847 /* These should never be set in LDAPDefaults */ 4848 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) || 4849 map.map_spacesub != SpaceSub || 4850 map.map_app != NULL || 4851 map.map_tapp != NULL) 4852 { 4853 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags"); 4854 SM_FREE_CLR(map.map_app); 4855 SM_FREE_CLR(map.map_tapp); 4856 } 4857 4858 if (LDAPDefaults->ldap_filter != NULL) 4859 { 4860 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter"); 4861 4862 /* don't free, it isn't malloc'ed in parseargs */ 4863 LDAPDefaults->ldap_filter = NULL; 4864 } 4865 4866 if (LDAPDefaults->ldap_attr[0] != NULL) 4867 { 4868 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes"); 4869 /* don't free, they aren't malloc'ed in parseargs */ 4870 LDAPDefaults->ldap_attr[0] = NULL; 4871 } 4872 } 4873 #endif /* LDAPMAP */ 4874 /* 4875 ** PH map 4876 */ 4877 4878 #if PH_MAP 4879 4880 /* 4881 ** Support for the CCSO Nameserver (ph/qi). 4882 ** This code is intended to replace the so-called "ph mailer". 4883 ** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support. 4884 */ 4885 4886 /* what version of the ph map code we're running */ 4887 static char phmap_id[128]; 4888 4889 /* sendmail version for phmap id string */ 4890 extern const char Version[]; 4891 4892 /* assume we're using nph-1.1.x if not specified */ 4893 # ifndef NPH_VERSION 4894 # define NPH_VERSION 10100 4895 # endif 4896 4897 /* compatibility for versions older than nph-1.2.0 */ 4898 # if NPH_VERSION < 10200 4899 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN 4900 # define PH_OPEN_DONTID PH_DONTID 4901 # define PH_CLOSE_FAST PH_FASTCLOSE 4902 # define PH_ERR_DATAERR PH_DATAERR 4903 # define PH_ERR_NOMATCH PH_NOMATCH 4904 # endif /* NPH_VERSION < 10200 */ 4905 4906 /* 4907 ** PH_MAP_PARSEARGS -- parse ph map definition args. 4908 */ 4909 4910 bool 4911 ph_map_parseargs(map, args) 4912 MAP *map; 4913 char *args; 4914 { 4915 register bool done; 4916 register char *p = args; 4917 PH_MAP_STRUCT *pmap = NULL; 4918 4919 /* initialize version string */ 4920 (void) sm_snprintf(phmap_id, sizeof phmap_id, 4921 "sendmail-%s phmap-20010529 libphclient-%s", 4922 Version, libphclient_version); 4923 4924 pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap); 4925 4926 /* defaults */ 4927 pmap->ph_servers = NULL; 4928 pmap->ph_field_list = NULL; 4929 pmap->ph = NULL; 4930 pmap->ph_timeout = 0; 4931 pmap->ph_fastclose = 0; 4932 4933 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 4934 for (;;) 4935 { 4936 while (isascii(*p) && isspace(*p)) 4937 p++; 4938 if (*p != '-') 4939 break; 4940 switch (*++p) 4941 { 4942 case 'N': 4943 map->map_mflags |= MF_INCLNULL; 4944 map->map_mflags &= ~MF_TRY0NULL; 4945 break; 4946 4947 case 'O': 4948 map->map_mflags &= ~MF_TRY1NULL; 4949 break; 4950 4951 case 'o': 4952 map->map_mflags |= MF_OPTIONAL; 4953 break; 4954 4955 case 'f': 4956 map->map_mflags |= MF_NOFOLDCASE; 4957 break; 4958 4959 case 'm': 4960 map->map_mflags |= MF_MATCHONLY; 4961 break; 4962 4963 case 'A': 4964 map->map_mflags |= MF_APPEND; 4965 break; 4966 4967 case 'q': 4968 map->map_mflags |= MF_KEEPQUOTES; 4969 break; 4970 4971 case 't': 4972 map->map_mflags |= MF_NODEFER; 4973 break; 4974 4975 case 'a': 4976 map->map_app = ++p; 4977 break; 4978 4979 case 'T': 4980 map->map_tapp = ++p; 4981 break; 4982 4983 case 'l': 4984 while (isascii(*++p) && isspace(*p)) 4985 continue; 4986 pmap->ph_timeout = atoi(p); 4987 break; 4988 4989 case 'S': 4990 map->map_spacesub = *++p; 4991 break; 4992 4993 case 'D': 4994 map->map_mflags |= MF_DEFER; 4995 break; 4996 4997 case 'h': /* PH server list */ 4998 while (isascii(*++p) && isspace(*p)) 4999 continue; 5000 pmap->ph_servers = p; 5001 break; 5002 5003 case 'v': 5004 sm_syslog(LOG_WARNING, NULL, 5005 "ph_map_parseargs: WARNING: -v option will be removed in a future release - please use -k instead"); 5006 /* intentional fallthrough for backward compatibility */ 5007 /* FALLTHROUGH */ 5008 5009 case 'k': /* fields to search for */ 5010 while (isascii(*++p) && isspace(*p)) 5011 continue; 5012 pmap->ph_field_list = p; 5013 break; 5014 5015 default: 5016 syserr("ph_map_parseargs: unknown option -%c", *p); 5017 } 5018 5019 /* try to account for quoted strings */ 5020 done = isascii(*p) && isspace(*p); 5021 while (*p != '\0' && !done) 5022 { 5023 if (*p == '"') 5024 { 5025 while (*++p != '"' && *p != '\0') 5026 continue; 5027 if (*p != '\0') 5028 p++; 5029 } 5030 else 5031 p++; 5032 done = isascii(*p) && isspace(*p); 5033 } 5034 5035 if (*p != '\0') 5036 *p++ = '\0'; 5037 } 5038 5039 if (map->map_app != NULL) 5040 map->map_app = newstr(ph_map_dequote(map->map_app)); 5041 if (map->map_tapp != NULL) 5042 map->map_tapp = newstr(ph_map_dequote(map->map_tapp)); 5043 5044 if (pmap->ph_field_list != NULL) 5045 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list)); 5046 5047 if (pmap->ph_servers != NULL) 5048 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers)); 5049 else 5050 { 5051 syserr("ph_map_parseargs: -h flag is required"); 5052 return false; 5053 } 5054 5055 map->map_db1 = (ARBPTR_T) pmap; 5056 return true; 5057 } 5058 5059 /* 5060 ** PH_MAP_CLOSE -- close the connection to the ph server 5061 */ 5062 5063 void 5064 ph_map_close(map) 5065 MAP *map; 5066 { 5067 PH_MAP_STRUCT *pmap; 5068 5069 pmap = (PH_MAP_STRUCT *)map->map_db1; 5070 if (tTd(38, 9)) 5071 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n", 5072 map->map_mname, pmap->ph_fastclose); 5073 5074 5075 if (pmap->ph != NULL) 5076 { 5077 ph_set_sendhook(pmap->ph, NULL); 5078 ph_set_recvhook(pmap->ph, NULL); 5079 ph_close(pmap->ph, pmap->ph_fastclose); 5080 } 5081 5082 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 5083 } 5084 5085 static jmp_buf PHTimeout; 5086 5087 /* ARGSUSED */ 5088 static void 5089 ph_timeout(unused) 5090 int unused; 5091 { 5092 /* 5093 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 5094 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 5095 ** DOING. 5096 */ 5097 5098 errno = ETIMEDOUT; 5099 longjmp(PHTimeout, 1); 5100 } 5101 5102 static void 5103 #if NPH_VERSION >= 10200 5104 ph_map_send_debug(appdata, text) 5105 void *appdata; 5106 #else 5107 ph_map_send_debug(text) 5108 #endif 5109 char *text; 5110 { 5111 if (LogLevel > 9) 5112 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5113 "ph_map_send_debug: ==> %s", text); 5114 if (tTd(38, 20)) 5115 sm_dprintf("ph_map_send_debug: ==> %s\n", text); 5116 } 5117 5118 static void 5119 #if NPH_VERSION >= 10200 5120 ph_map_recv_debug(appdata, text) 5121 void *appdata; 5122 #else 5123 ph_map_recv_debug(text) 5124 #endif 5125 char *text; 5126 { 5127 if (LogLevel > 10) 5128 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5129 "ph_map_recv_debug: <== %s", text); 5130 if (tTd(38, 21)) 5131 sm_dprintf("ph_map_recv_debug: <== %s\n", text); 5132 } 5133 5134 /* 5135 ** PH_MAP_OPEN -- sub for opening PH map 5136 */ 5137 bool 5138 ph_map_open(map, mode) 5139 MAP *map; 5140 int mode; 5141 { 5142 PH_MAP_STRUCT *pmap; 5143 register SM_EVENT *ev = NULL; 5144 int save_errno = 0; 5145 char *hostlist, *host; 5146 5147 if (tTd(38, 2)) 5148 sm_dprintf("ph_map_open(%s)\n", map->map_mname); 5149 5150 mode &= O_ACCMODE; 5151 if (mode != O_RDONLY) 5152 { 5153 /* issue a pseudo-error message */ 5154 errno = SM_EMAPCANTWRITE; 5155 return false; 5156 } 5157 5158 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER && 5159 bitset(MF_DEFER, map->map_mflags)) 5160 { 5161 if (tTd(9, 1)) 5162 sm_dprintf("ph_map_open(%s) => DEFERRED\n", 5163 map->map_mname); 5164 5165 /* 5166 ** Unset MF_DEFER here so that map_lookup() returns 5167 ** a temporary failure using the bogus map and 5168 ** map->map_tapp instead of the default permanent error. 5169 */ 5170 5171 map->map_mflags &= ~MF_DEFER; 5172 return false; 5173 } 5174 5175 pmap = (PH_MAP_STRUCT *)map->map_db1; 5176 pmap->ph_fastclose = 0; /* refresh field for reopen */ 5177 5178 /* try each host in the list */ 5179 hostlist = newstr(pmap->ph_servers); 5180 for (host = strtok(hostlist, " "); 5181 host != NULL; 5182 host = strtok(NULL, " ")) 5183 { 5184 /* set timeout */ 5185 if (pmap->ph_timeout != 0) 5186 { 5187 if (setjmp(PHTimeout) != 0) 5188 { 5189 ev = NULL; 5190 if (LogLevel > 1) 5191 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5192 "timeout connecting to PH server %.100s", 5193 host); 5194 errno = ETIMEDOUT; 5195 goto ph_map_open_abort; 5196 } 5197 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0); 5198 } 5199 5200 /* open connection to server */ 5201 if (ph_open(&(pmap->ph), host, 5202 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID, 5203 ph_map_send_debug, ph_map_recv_debug 5204 #if NPH_VERSION >= 10200 5205 , NULL 5206 #endif 5207 ) == 0 5208 && ph_id(pmap->ph, phmap_id) == 0) 5209 { 5210 if (ev != NULL) 5211 sm_clrevent(ev); 5212 sm_free(hostlist); /* XXX */ 5213 return true; 5214 } 5215 5216 ph_map_open_abort: 5217 save_errno = errno; 5218 if (ev != NULL) 5219 sm_clrevent(ev); 5220 pmap->ph_fastclose = PH_CLOSE_FAST; 5221 ph_map_close(map); 5222 errno = save_errno; 5223 } 5224 5225 if (bitset(MF_NODEFER, map->map_mflags)) 5226 { 5227 if (errno == 0) 5228 errno = EAGAIN; 5229 syserr("ph_map_open: %s: cannot connect to PH server", 5230 map->map_mname); 5231 } 5232 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1) 5233 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5234 "ph_map_open: %s: cannot connect to PH server", 5235 map->map_mname); 5236 sm_free(hostlist); /* XXX */ 5237 return false; 5238 } 5239 5240 /* 5241 ** PH_MAP_LOOKUP -- look up key from ph server 5242 */ 5243 5244 char * 5245 ph_map_lookup(map, key, args, pstat) 5246 MAP *map; 5247 char *key; 5248 char **args; 5249 int *pstat; 5250 { 5251 int i, save_errno = 0; 5252 register SM_EVENT *ev = NULL; 5253 PH_MAP_STRUCT *pmap; 5254 char *value = NULL; 5255 5256 pmap = (PH_MAP_STRUCT *)map->map_db1; 5257 5258 *pstat = EX_OK; 5259 5260 /* set timeout */ 5261 if (pmap->ph_timeout != 0) 5262 { 5263 if (setjmp(PHTimeout) != 0) 5264 { 5265 ev = NULL; 5266 if (LogLevel > 1) 5267 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5268 "timeout during PH lookup of %.100s", 5269 key); 5270 errno = ETIMEDOUT; 5271 *pstat = EX_TEMPFAIL; 5272 goto ph_map_lookup_abort; 5273 } 5274 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0); 5275 } 5276 5277 /* perform lookup */ 5278 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value); 5279 if (i == -1) 5280 *pstat = EX_TEMPFAIL; 5281 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR) 5282 *pstat = EX_UNAVAILABLE; 5283 5284 ph_map_lookup_abort: 5285 if (ev != NULL) 5286 sm_clrevent(ev); 5287 5288 /* 5289 ** Close the connection if the timer popped 5290 ** or we got a temporary PH error 5291 */ 5292 5293 if (*pstat == EX_TEMPFAIL) 5294 { 5295 save_errno = errno; 5296 pmap->ph_fastclose = PH_CLOSE_FAST; 5297 ph_map_close(map); 5298 errno = save_errno; 5299 } 5300 5301 if (*pstat == EX_OK) 5302 { 5303 if (tTd(38,20)) 5304 sm_dprintf("ph_map_lookup: %s => %s\n", key, value); 5305 5306 if (bitset(MF_MATCHONLY, map->map_mflags)) 5307 return map_rewrite(map, key, strlen(key), NULL); 5308 else 5309 return map_rewrite(map, value, strlen(value), args); 5310 } 5311 5312 return NULL; 5313 } 5314 #endif /* PH_MAP */ 5315 /* 5316 ** syslog map 5317 */ 5318 5319 #define map_prio map_lockfd /* overload field */ 5320 5321 /* 5322 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages. 5323 */ 5324 5325 bool 5326 syslog_map_parseargs(map, args) 5327 MAP *map; 5328 char *args; 5329 { 5330 char *p = args; 5331 char *priority = NULL; 5332 5333 /* there is no check whether there is really an argument */ 5334 while (*p != '\0') 5335 { 5336 while (isascii(*p) && isspace(*p)) 5337 p++; 5338 if (*p != '-') 5339 break; 5340 ++p; 5341 if (*p == 'D') 5342 { 5343 map->map_mflags |= MF_DEFER; 5344 ++p; 5345 } 5346 else if (*p == 'S') 5347 { 5348 map->map_spacesub = *++p; 5349 if (*p != '\0') 5350 p++; 5351 } 5352 else if (*p == 'L') 5353 { 5354 while (*++p != '\0' && isascii(*p) && isspace(*p)) 5355 continue; 5356 if (*p == '\0') 5357 break; 5358 priority = p; 5359 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 5360 p++; 5361 if (*p != '\0') 5362 *p++ = '\0'; 5363 } 5364 else 5365 { 5366 syserr("Illegal option %c map syslog", *p); 5367 ++p; 5368 } 5369 } 5370 5371 if (priority == NULL) 5372 map->map_prio = LOG_INFO; 5373 else 5374 { 5375 if (sm_strncasecmp("LOG_", priority, 4) == 0) 5376 priority += 4; 5377 5378 #ifdef LOG_EMERG 5379 if (sm_strcasecmp("EMERG", priority) == 0) 5380 map->map_prio = LOG_EMERG; 5381 else 5382 #endif /* LOG_EMERG */ 5383 #ifdef LOG_ALERT 5384 if (sm_strcasecmp("ALERT", priority) == 0) 5385 map->map_prio = LOG_ALERT; 5386 else 5387 #endif /* LOG_ALERT */ 5388 #ifdef LOG_CRIT 5389 if (sm_strcasecmp("CRIT", priority) == 0) 5390 map->map_prio = LOG_CRIT; 5391 else 5392 #endif /* LOG_CRIT */ 5393 #ifdef LOG_ERR 5394 if (sm_strcasecmp("ERR", priority) == 0) 5395 map->map_prio = LOG_ERR; 5396 else 5397 #endif /* LOG_ERR */ 5398 #ifdef LOG_WARNING 5399 if (sm_strcasecmp("WARNING", priority) == 0) 5400 map->map_prio = LOG_WARNING; 5401 else 5402 #endif /* LOG_WARNING */ 5403 #ifdef LOG_NOTICE 5404 if (sm_strcasecmp("NOTICE", priority) == 0) 5405 map->map_prio = LOG_NOTICE; 5406 else 5407 #endif /* LOG_NOTICE */ 5408 #ifdef LOG_INFO 5409 if (sm_strcasecmp("INFO", priority) == 0) 5410 map->map_prio = LOG_INFO; 5411 else 5412 #endif /* LOG_INFO */ 5413 #ifdef LOG_DEBUG 5414 if (sm_strcasecmp("DEBUG", priority) == 0) 5415 map->map_prio = LOG_DEBUG; 5416 else 5417 #endif /* LOG_DEBUG */ 5418 { 5419 syserr("syslog_map_parseargs: Unknown priority %s", 5420 priority); 5421 return false; 5422 } 5423 } 5424 return true; 5425 } 5426 5427 /* 5428 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string 5429 */ 5430 5431 char * 5432 syslog_map_lookup(map, string, args, statp) 5433 MAP *map; 5434 char *string; 5435 char **args; 5436 int *statp; 5437 { 5438 char *ptr = map_rewrite(map, string, strlen(string), args); 5439 5440 if (ptr != NULL) 5441 { 5442 if (tTd(38, 20)) 5443 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n", 5444 map->map_mname, map->map_prio, ptr); 5445 5446 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr); 5447 } 5448 5449 *statp = EX_OK; 5450 return ""; 5451 } 5452 5453 /* 5454 ** HESIOD Modules 5455 */ 5456 5457 #if HESIOD 5458 5459 bool 5460 hes_map_open(map, mode) 5461 MAP *map; 5462 int mode; 5463 { 5464 if (tTd(38, 2)) 5465 sm_dprintf("hes_map_open(%s, %s, %d)\n", 5466 map->map_mname, map->map_file, mode); 5467 5468 if (mode != O_RDONLY) 5469 { 5470 /* issue a pseudo-error message */ 5471 errno = SM_EMAPCANTWRITE; 5472 return false; 5473 } 5474 5475 # ifdef HESIOD_INIT 5476 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0) 5477 return true; 5478 5479 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5480 syserr("451 4.3.5 cannot initialize Hesiod map (%s)", 5481 sm_errstring(errno)); 5482 return false; 5483 # else /* HESIOD_INIT */ 5484 if (hes_error() == HES_ER_UNINIT) 5485 hes_init(); 5486 switch (hes_error()) 5487 { 5488 case HES_ER_OK: 5489 case HES_ER_NOTFOUND: 5490 return true; 5491 } 5492 5493 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5494 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error()); 5495 5496 return false; 5497 # endif /* HESIOD_INIT */ 5498 } 5499 5500 char * 5501 hes_map_lookup(map, name, av, statp) 5502 MAP *map; 5503 char *name; 5504 char **av; 5505 int *statp; 5506 { 5507 char **hp; 5508 5509 if (tTd(38, 20)) 5510 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name); 5511 5512 if (name[0] == '\\') 5513 { 5514 char *np; 5515 int nl; 5516 int save_errno; 5517 char nbuf[MAXNAME]; 5518 5519 nl = strlen(name); 5520 if (nl < sizeof nbuf - 1) 5521 np = nbuf; 5522 else 5523 np = xalloc(strlen(name) + 2); 5524 np[0] = '\\'; 5525 (void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1); 5526 # ifdef HESIOD_INIT 5527 hp = hesiod_resolve(HesiodContext, np, map->map_file); 5528 # else /* HESIOD_INIT */ 5529 hp = hes_resolve(np, map->map_file); 5530 # endif /* HESIOD_INIT */ 5531 save_errno = errno; 5532 if (np != nbuf) 5533 sm_free(np); /* XXX */ 5534 errno = save_errno; 5535 } 5536 else 5537 { 5538 # ifdef HESIOD_INIT 5539 hp = hesiod_resolve(HesiodContext, name, map->map_file); 5540 # else /* HESIOD_INIT */ 5541 hp = hes_resolve(name, map->map_file); 5542 # endif /* HESIOD_INIT */ 5543 } 5544 # ifdef HESIOD_INIT 5545 if (hp == NULL || *hp == NULL) 5546 { 5547 switch (errno) 5548 { 5549 case ENOENT: 5550 *statp = EX_NOTFOUND; 5551 break; 5552 case ECONNREFUSED: 5553 *statp = EX_TEMPFAIL; 5554 break; 5555 case EMSGSIZE: 5556 case ENOMEM: 5557 default: 5558 *statp = EX_UNAVAILABLE; 5559 break; 5560 } 5561 if (hp != NULL) 5562 hesiod_free_list(HesiodContext, hp); 5563 return NULL; 5564 } 5565 # else /* HESIOD_INIT */ 5566 if (hp == NULL || hp[0] == NULL) 5567 { 5568 switch (hes_error()) 5569 { 5570 case HES_ER_OK: 5571 *statp = EX_OK; 5572 break; 5573 5574 case HES_ER_NOTFOUND: 5575 *statp = EX_NOTFOUND; 5576 break; 5577 5578 case HES_ER_CONFIG: 5579 *statp = EX_UNAVAILABLE; 5580 break; 5581 5582 case HES_ER_NET: 5583 *statp = EX_TEMPFAIL; 5584 break; 5585 } 5586 return NULL; 5587 } 5588 # endif /* HESIOD_INIT */ 5589 5590 if (bitset(MF_MATCHONLY, map->map_mflags)) 5591 return map_rewrite(map, name, strlen(name), NULL); 5592 else 5593 return map_rewrite(map, hp[0], strlen(hp[0]), av); 5594 } 5595 5596 /* 5597 ** HES_MAP_CLOSE -- free the Hesiod context 5598 */ 5599 5600 void 5601 hes_map_close(map) 5602 MAP *map; 5603 { 5604 if (tTd(38, 20)) 5605 sm_dprintf("hes_map_close(%s)\n", map->map_file); 5606 5607 # ifdef HESIOD_INIT 5608 /* Free the hesiod context */ 5609 if (HesiodContext != NULL) 5610 { 5611 hesiod_end(HesiodContext); 5612 HesiodContext = NULL; 5613 } 5614 # endif /* HESIOD_INIT */ 5615 } 5616 5617 #endif /* HESIOD */ 5618 /* 5619 ** NeXT NETINFO Modules 5620 */ 5621 5622 #if NETINFO 5623 5624 # define NETINFO_DEFAULT_DIR "/aliases" 5625 # define NETINFO_DEFAULT_PROPERTY "members" 5626 5627 /* 5628 ** NI_MAP_OPEN -- open NetInfo Aliases 5629 */ 5630 5631 bool 5632 ni_map_open(map, mode) 5633 MAP *map; 5634 int mode; 5635 { 5636 if (tTd(38, 2)) 5637 sm_dprintf("ni_map_open(%s, %s, %d)\n", 5638 map->map_mname, map->map_file, mode); 5639 mode &= O_ACCMODE; 5640 5641 if (*map->map_file == '\0') 5642 map->map_file = NETINFO_DEFAULT_DIR; 5643 5644 if (map->map_valcolnm == NULL) 5645 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY; 5646 5647 if (map->map_coldelim == '\0') 5648 { 5649 if (bitset(MF_ALIAS, map->map_mflags)) 5650 map->map_coldelim = ','; 5651 else if (bitset(MF_FILECLASS, map->map_mflags)) 5652 map->map_coldelim = ' '; 5653 } 5654 return true; 5655 } 5656 5657 5658 /* 5659 ** NI_MAP_LOOKUP -- look up a datum in NetInfo 5660 */ 5661 5662 char * 5663 ni_map_lookup(map, name, av, statp) 5664 MAP *map; 5665 char *name; 5666 char **av; 5667 int *statp; 5668 { 5669 char *res; 5670 char *propval; 5671 5672 if (tTd(38, 20)) 5673 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name); 5674 5675 propval = ni_propval(map->map_file, map->map_keycolnm, name, 5676 map->map_valcolnm, map->map_coldelim); 5677 5678 if (propval == NULL) 5679 return NULL; 5680 5681 SM_TRY 5682 if (bitset(MF_MATCHONLY, map->map_mflags)) 5683 res = map_rewrite(map, name, strlen(name), NULL); 5684 else 5685 res = map_rewrite(map, propval, strlen(propval), av); 5686 SM_FINALLY 5687 sm_free(propval); 5688 SM_END_TRY 5689 return res; 5690 } 5691 5692 5693 static bool 5694 ni_getcanonname(name, hbsize, statp) 5695 char *name; 5696 int hbsize; 5697 int *statp; 5698 { 5699 char *vptr; 5700 char *ptr; 5701 char nbuf[MAXNAME + 1]; 5702 5703 if (tTd(38, 20)) 5704 sm_dprintf("ni_getcanonname(%s)\n", name); 5705 5706 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) 5707 { 5708 *statp = EX_UNAVAILABLE; 5709 return false; 5710 } 5711 (void) shorten_hostname(nbuf); 5712 5713 /* we only accept single token search key */ 5714 if (strchr(nbuf, '.')) 5715 { 5716 *statp = EX_NOHOST; 5717 return false; 5718 } 5719 5720 /* Do the search */ 5721 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n'); 5722 5723 if (vptr == NULL) 5724 { 5725 *statp = EX_NOHOST; 5726 return false; 5727 } 5728 5729 /* Only want the first machine name */ 5730 if ((ptr = strchr(vptr, '\n')) != NULL) 5731 *ptr = '\0'; 5732 5733 if (sm_strlcpy(name, vptr, hbsize) >= hbsize) 5734 { 5735 sm_free(vptr); 5736 *statp = EX_UNAVAILABLE; 5737 return true; 5738 } 5739 sm_free(vptr); 5740 *statp = EX_OK; 5741 return false; 5742 } 5743 #endif /* NETINFO */ 5744 /* 5745 ** TEXT (unindexed text file) Modules 5746 ** 5747 ** This code donated by Sun Microsystems. 5748 */ 5749 5750 #define map_sff map_lockfd /* overload field */ 5751 5752 5753 /* 5754 ** TEXT_MAP_OPEN -- open text table 5755 */ 5756 5757 bool 5758 text_map_open(map, mode) 5759 MAP *map; 5760 int mode; 5761 { 5762 long sff; 5763 int i; 5764 5765 if (tTd(38, 2)) 5766 sm_dprintf("text_map_open(%s, %s, %d)\n", 5767 map->map_mname, map->map_file, mode); 5768 5769 mode &= O_ACCMODE; 5770 if (mode != O_RDONLY) 5771 { 5772 errno = EPERM; 5773 return false; 5774 } 5775 5776 if (*map->map_file == '\0') 5777 { 5778 syserr("text map \"%s\": file name required", 5779 map->map_mname); 5780 return false; 5781 } 5782 5783 if (map->map_file[0] != '/') 5784 { 5785 syserr("text map \"%s\": file name must be fully qualified", 5786 map->map_mname); 5787 return false; 5788 } 5789 5790 sff = SFF_ROOTOK|SFF_REGONLY; 5791 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 5792 sff |= SFF_NOWLINK; 5793 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 5794 sff |= SFF_SAFEDIRPATH; 5795 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName, 5796 sff, S_IRUSR, NULL)) != 0) 5797 { 5798 int save_errno = errno; 5799 5800 /* cannot open this map */ 5801 if (tTd(38, 2)) 5802 sm_dprintf("\tunsafe map file: %d\n", i); 5803 errno = save_errno; 5804 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5805 syserr("text map \"%s\": unsafe map file %s", 5806 map->map_mname, map->map_file); 5807 return false; 5808 } 5809 5810 if (map->map_keycolnm == NULL) 5811 map->map_keycolno = 0; 5812 else 5813 { 5814 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm))) 5815 { 5816 syserr("text map \"%s\", file %s: -k should specify a number, not %s", 5817 map->map_mname, map->map_file, 5818 map->map_keycolnm); 5819 return false; 5820 } 5821 map->map_keycolno = atoi(map->map_keycolnm); 5822 } 5823 5824 if (map->map_valcolnm == NULL) 5825 map->map_valcolno = 0; 5826 else 5827 { 5828 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm))) 5829 { 5830 syserr("text map \"%s\", file %s: -v should specify a number, not %s", 5831 map->map_mname, map->map_file, 5832 map->map_valcolnm); 5833 return false; 5834 } 5835 map->map_valcolno = atoi(map->map_valcolnm); 5836 } 5837 5838 if (tTd(38, 2)) 5839 { 5840 sm_dprintf("text_map_open(%s, %s): delimiter = ", 5841 map->map_mname, map->map_file); 5842 if (map->map_coldelim == '\0') 5843 sm_dprintf("(white space)\n"); 5844 else 5845 sm_dprintf("%c\n", map->map_coldelim); 5846 } 5847 5848 map->map_sff = sff; 5849 return true; 5850 } 5851 5852 5853 /* 5854 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table 5855 */ 5856 5857 char * 5858 text_map_lookup(map, name, av, statp) 5859 MAP *map; 5860 char *name; 5861 char **av; 5862 int *statp; 5863 { 5864 char *vp; 5865 auto int vsize; 5866 int buflen; 5867 SM_FILE_T *f; 5868 char delim; 5869 int key_idx; 5870 bool found_it; 5871 long sff = map->map_sff; 5872 char search_key[MAXNAME + 1]; 5873 char linebuf[MAXLINE]; 5874 char buf[MAXNAME + 1]; 5875 5876 found_it = false; 5877 if (tTd(38, 20)) 5878 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name); 5879 5880 buflen = strlen(name); 5881 if (buflen > sizeof search_key - 1) 5882 buflen = sizeof search_key - 1; /* XXX just cut if off? */ 5883 memmove(search_key, name, buflen); 5884 search_key[buflen] = '\0'; 5885 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 5886 makelower(search_key); 5887 5888 f = safefopen(map->map_file, O_RDONLY, FileMode, sff); 5889 if (f == NULL) 5890 { 5891 map->map_mflags &= ~(MF_VALID|MF_OPEN); 5892 *statp = EX_UNAVAILABLE; 5893 return NULL; 5894 } 5895 key_idx = map->map_keycolno; 5896 delim = map->map_coldelim; 5897 while (sm_io_fgets(f, SM_TIME_DEFAULT, 5898 linebuf, sizeof linebuf) != NULL) 5899 { 5900 char *p; 5901 5902 /* skip comment line */ 5903 if (linebuf[0] == '#') 5904 continue; 5905 p = strchr(linebuf, '\n'); 5906 if (p != NULL) 5907 *p = '\0'; 5908 p = get_column(linebuf, key_idx, delim, buf, sizeof buf); 5909 if (p != NULL && sm_strcasecmp(search_key, p) == 0) 5910 { 5911 found_it = true; 5912 break; 5913 } 5914 } 5915 (void) sm_io_close(f, SM_TIME_DEFAULT); 5916 if (!found_it) 5917 { 5918 *statp = EX_NOTFOUND; 5919 return NULL; 5920 } 5921 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf); 5922 if (vp == NULL) 5923 { 5924 *statp = EX_NOTFOUND; 5925 return NULL; 5926 } 5927 vsize = strlen(vp); 5928 *statp = EX_OK; 5929 if (bitset(MF_MATCHONLY, map->map_mflags)) 5930 return map_rewrite(map, name, strlen(name), NULL); 5931 else 5932 return map_rewrite(map, vp, vsize, av); 5933 } 5934 5935 /* 5936 ** TEXT_GETCANONNAME -- look up canonical name in hosts file 5937 */ 5938 5939 static bool 5940 text_getcanonname(name, hbsize, statp) 5941 char *name; 5942 int hbsize; 5943 int *statp; 5944 { 5945 bool found; 5946 char *dot; 5947 SM_FILE_T *f; 5948 char linebuf[MAXLINE]; 5949 char cbuf[MAXNAME + 1]; 5950 char nbuf[MAXNAME + 1]; 5951 5952 if (tTd(38, 20)) 5953 sm_dprintf("text_getcanonname(%s)\n", name); 5954 5955 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) 5956 { 5957 *statp = EX_UNAVAILABLE; 5958 return false; 5959 } 5960 dot = shorten_hostname(nbuf); 5961 5962 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY, 5963 NULL); 5964 if (f == NULL) 5965 { 5966 *statp = EX_UNAVAILABLE; 5967 return false; 5968 } 5969 found = false; 5970 while (!found && 5971 sm_io_fgets(f, SM_TIME_DEFAULT, 5972 linebuf, sizeof linebuf) != NULL) 5973 { 5974 char *p = strpbrk(linebuf, "#\n"); 5975 5976 if (p != NULL) 5977 *p = '\0'; 5978 if (linebuf[0] != '\0') 5979 found = extract_canonname(nbuf, dot, linebuf, 5980 cbuf, sizeof cbuf); 5981 } 5982 (void) sm_io_close(f, SM_TIME_DEFAULT); 5983 if (!found) 5984 { 5985 *statp = EX_NOHOST; 5986 return false; 5987 } 5988 5989 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize) 5990 { 5991 *statp = EX_UNAVAILABLE; 5992 return false; 5993 } 5994 *statp = EX_OK; 5995 return true; 5996 } 5997 /* 5998 ** STAB (Symbol Table) Modules 5999 */ 6000 6001 6002 /* 6003 ** STAB_MAP_LOOKUP -- look up alias in symbol table 6004 */ 6005 6006 /* ARGSUSED2 */ 6007 char * 6008 stab_map_lookup(map, name, av, pstat) 6009 register MAP *map; 6010 char *name; 6011 char **av; 6012 int *pstat; 6013 { 6014 register STAB *s; 6015 6016 if (tTd(38, 20)) 6017 sm_dprintf("stab_lookup(%s, %s)\n", 6018 map->map_mname, name); 6019 6020 s = stab(name, ST_ALIAS, ST_FIND); 6021 if (s != NULL) 6022 return s->s_alias; 6023 return NULL; 6024 } 6025 6026 6027 /* 6028 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild) 6029 */ 6030 6031 void 6032 stab_map_store(map, lhs, rhs) 6033 register MAP *map; 6034 char *lhs; 6035 char *rhs; 6036 { 6037 register STAB *s; 6038 6039 s = stab(lhs, ST_ALIAS, ST_ENTER); 6040 s->s_alias = newstr(rhs); 6041 } 6042 6043 6044 /* 6045 ** STAB_MAP_OPEN -- initialize (reads data file) 6046 ** 6047 ** This is a wierd case -- it is only intended as a fallback for 6048 ** aliases. For this reason, opens for write (only during a 6049 ** "newaliases") always fails, and opens for read open the 6050 ** actual underlying text file instead of the database. 6051 */ 6052 6053 bool 6054 stab_map_open(map, mode) 6055 register MAP *map; 6056 int mode; 6057 { 6058 SM_FILE_T *af; 6059 long sff; 6060 struct stat st; 6061 6062 if (tTd(38, 2)) 6063 sm_dprintf("stab_map_open(%s, %s, %d)\n", 6064 map->map_mname, map->map_file, mode); 6065 6066 mode &= O_ACCMODE; 6067 if (mode != O_RDONLY) 6068 { 6069 errno = EPERM; 6070 return false; 6071 } 6072 6073 sff = SFF_ROOTOK|SFF_REGONLY; 6074 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 6075 sff |= SFF_NOWLINK; 6076 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 6077 sff |= SFF_SAFEDIRPATH; 6078 af = safefopen(map->map_file, O_RDONLY, 0444, sff); 6079 if (af == NULL) 6080 return false; 6081 readaliases(map, af, false, false); 6082 6083 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0) 6084 map->map_mtime = st.st_mtime; 6085 (void) sm_io_close(af, SM_TIME_DEFAULT); 6086 6087 return true; 6088 } 6089 /* 6090 ** Implicit Modules 6091 ** 6092 ** Tries several types. For back compatibility of aliases. 6093 */ 6094 6095 6096 /* 6097 ** IMPL_MAP_LOOKUP -- lookup in best open database 6098 */ 6099 6100 char * 6101 impl_map_lookup(map, name, av, pstat) 6102 MAP *map; 6103 char *name; 6104 char **av; 6105 int *pstat; 6106 { 6107 if (tTd(38, 20)) 6108 sm_dprintf("impl_map_lookup(%s, %s)\n", 6109 map->map_mname, name); 6110 6111 #if NEWDB 6112 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6113 return db_map_lookup(map, name, av, pstat); 6114 #endif /* NEWDB */ 6115 #if NDBM 6116 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6117 return ndbm_map_lookup(map, name, av, pstat); 6118 #endif /* NDBM */ 6119 return stab_map_lookup(map, name, av, pstat); 6120 } 6121 6122 /* 6123 ** IMPL_MAP_STORE -- store in open databases 6124 */ 6125 6126 void 6127 impl_map_store(map, lhs, rhs) 6128 MAP *map; 6129 char *lhs; 6130 char *rhs; 6131 { 6132 if (tTd(38, 12)) 6133 sm_dprintf("impl_map_store(%s, %s, %s)\n", 6134 map->map_mname, lhs, rhs); 6135 #if NEWDB 6136 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6137 db_map_store(map, lhs, rhs); 6138 #endif /* NEWDB */ 6139 #if NDBM 6140 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6141 ndbm_map_store(map, lhs, rhs); 6142 #endif /* NDBM */ 6143 stab_map_store(map, lhs, rhs); 6144 } 6145 6146 /* 6147 ** IMPL_MAP_OPEN -- implicit database open 6148 */ 6149 6150 bool 6151 impl_map_open(map, mode) 6152 MAP *map; 6153 int mode; 6154 { 6155 if (tTd(38, 2)) 6156 sm_dprintf("impl_map_open(%s, %s, %d)\n", 6157 map->map_mname, map->map_file, mode); 6158 6159 mode &= O_ACCMODE; 6160 #if NEWDB 6161 map->map_mflags |= MF_IMPL_HASH; 6162 if (hash_map_open(map, mode)) 6163 { 6164 # ifdef NDBM_YP_COMPAT 6165 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL) 6166 # endif /* NDBM_YP_COMPAT */ 6167 return true; 6168 } 6169 else 6170 map->map_mflags &= ~MF_IMPL_HASH; 6171 #endif /* NEWDB */ 6172 #if NDBM 6173 map->map_mflags |= MF_IMPL_NDBM; 6174 if (ndbm_map_open(map, mode)) 6175 { 6176 return true; 6177 } 6178 else 6179 map->map_mflags &= ~MF_IMPL_NDBM; 6180 #endif /* NDBM */ 6181 6182 #if defined(NEWDB) || defined(NDBM) 6183 if (Verbose) 6184 message("WARNING: cannot open alias database %s%s", 6185 map->map_file, 6186 mode == O_RDONLY ? "; reading text version" : ""); 6187 #else /* defined(NEWDB) || defined(NDBM) */ 6188 if (mode != O_RDONLY) 6189 usrerr("Cannot rebuild aliases: no database format defined"); 6190 #endif /* defined(NEWDB) || defined(NDBM) */ 6191 6192 if (mode == O_RDONLY) 6193 return stab_map_open(map, mode); 6194 else 6195 return false; 6196 } 6197 6198 6199 /* 6200 ** IMPL_MAP_CLOSE -- close any open database(s) 6201 */ 6202 6203 void 6204 impl_map_close(map) 6205 MAP *map; 6206 { 6207 if (tTd(38, 9)) 6208 sm_dprintf("impl_map_close(%s, %s, %lx)\n", 6209 map->map_mname, map->map_file, map->map_mflags); 6210 #if NEWDB 6211 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6212 { 6213 db_map_close(map); 6214 map->map_mflags &= ~MF_IMPL_HASH; 6215 } 6216 #endif /* NEWDB */ 6217 6218 #if NDBM 6219 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6220 { 6221 ndbm_map_close(map); 6222 map->map_mflags &= ~MF_IMPL_NDBM; 6223 } 6224 #endif /* NDBM */ 6225 } 6226 /* 6227 ** User map class. 6228 ** 6229 ** Provides access to the system password file. 6230 */ 6231 6232 /* 6233 ** USER_MAP_OPEN -- open user map 6234 ** 6235 ** Really just binds field names to field numbers. 6236 */ 6237 6238 bool 6239 user_map_open(map, mode) 6240 MAP *map; 6241 int mode; 6242 { 6243 if (tTd(38, 2)) 6244 sm_dprintf("user_map_open(%s, %d)\n", 6245 map->map_mname, mode); 6246 6247 mode &= O_ACCMODE; 6248 if (mode != O_RDONLY) 6249 { 6250 /* issue a pseudo-error message */ 6251 errno = SM_EMAPCANTWRITE; 6252 return false; 6253 } 6254 if (map->map_valcolnm == NULL) 6255 /* EMPTY */ 6256 /* nothing */ ; 6257 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0) 6258 map->map_valcolno = 1; 6259 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0) 6260 map->map_valcolno = 2; 6261 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0) 6262 map->map_valcolno = 3; 6263 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0) 6264 map->map_valcolno = 4; 6265 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0) 6266 map->map_valcolno = 5; 6267 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0) 6268 map->map_valcolno = 6; 6269 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0) 6270 map->map_valcolno = 7; 6271 else 6272 { 6273 syserr("User map %s: unknown column name %s", 6274 map->map_mname, map->map_valcolnm); 6275 return false; 6276 } 6277 return true; 6278 } 6279 6280 6281 /* 6282 ** USER_MAP_LOOKUP -- look up a user in the passwd file. 6283 */ 6284 6285 /* ARGSUSED3 */ 6286 char * 6287 user_map_lookup(map, key, av, statp) 6288 MAP *map; 6289 char *key; 6290 char **av; 6291 int *statp; 6292 { 6293 auto bool fuzzy; 6294 SM_MBDB_T user; 6295 6296 if (tTd(38, 20)) 6297 sm_dprintf("user_map_lookup(%s, %s)\n", 6298 map->map_mname, key); 6299 6300 *statp = finduser(key, &fuzzy, &user); 6301 if (*statp != EX_OK) 6302 return NULL; 6303 if (bitset(MF_MATCHONLY, map->map_mflags)) 6304 return map_rewrite(map, key, strlen(key), NULL); 6305 else 6306 { 6307 char *rwval = NULL; 6308 char buf[30]; 6309 6310 switch (map->map_valcolno) 6311 { 6312 case 0: 6313 case 1: 6314 rwval = user.mbdb_name; 6315 break; 6316 6317 case 2: 6318 rwval = "x"; /* passwd no longer supported */ 6319 break; 6320 6321 case 3: 6322 (void) sm_snprintf(buf, sizeof buf, "%d", 6323 (int) user.mbdb_uid); 6324 rwval = buf; 6325 break; 6326 6327 case 4: 6328 (void) sm_snprintf(buf, sizeof buf, "%d", 6329 (int) user.mbdb_gid); 6330 rwval = buf; 6331 break; 6332 6333 case 5: 6334 rwval = user.mbdb_fullname; 6335 break; 6336 6337 case 6: 6338 rwval = user.mbdb_homedir; 6339 break; 6340 6341 case 7: 6342 rwval = user.mbdb_shell; 6343 break; 6344 } 6345 return map_rewrite(map, rwval, strlen(rwval), av); 6346 } 6347 } 6348 /* 6349 ** Program map type. 6350 ** 6351 ** This provides access to arbitrary programs. It should be used 6352 ** only very sparingly, since there is no way to bound the cost 6353 ** of invoking an arbitrary program. 6354 */ 6355 6356 char * 6357 prog_map_lookup(map, name, av, statp) 6358 MAP *map; 6359 char *name; 6360 char **av; 6361 int *statp; 6362 { 6363 int i; 6364 int save_errno; 6365 int fd; 6366 int status; 6367 auto pid_t pid; 6368 register char *p; 6369 char *rval; 6370 char *argv[MAXPV + 1]; 6371 char buf[MAXLINE]; 6372 6373 if (tTd(38, 20)) 6374 sm_dprintf("prog_map_lookup(%s, %s) %s\n", 6375 map->map_mname, name, map->map_file); 6376 6377 i = 0; 6378 argv[i++] = map->map_file; 6379 if (map->map_rebuild != NULL) 6380 { 6381 (void) sm_strlcpy(buf, map->map_rebuild, sizeof buf); 6382 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) 6383 { 6384 if (i >= MAXPV - 1) 6385 break; 6386 argv[i++] = p; 6387 } 6388 } 6389 argv[i++] = name; 6390 argv[i] = NULL; 6391 if (tTd(38, 21)) 6392 { 6393 sm_dprintf("prog_open:"); 6394 for (i = 0; argv[i] != NULL; i++) 6395 sm_dprintf(" %s", argv[i]); 6396 sm_dprintf("\n"); 6397 } 6398 (void) sm_blocksignal(SIGCHLD); 6399 pid = prog_open(argv, &fd, CurEnv); 6400 if (pid < 0) 6401 { 6402 if (!bitset(MF_OPTIONAL, map->map_mflags)) 6403 syserr("prog_map_lookup(%s) failed (%s) -- closing", 6404 map->map_mname, sm_errstring(errno)); 6405 else if (tTd(38, 9)) 6406 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing", 6407 map->map_mname, sm_errstring(errno)); 6408 map->map_mflags &= ~(MF_VALID|MF_OPEN); 6409 *statp = EX_OSFILE; 6410 return NULL; 6411 } 6412 i = read(fd, buf, sizeof buf - 1); 6413 if (i < 0) 6414 { 6415 syserr("prog_map_lookup(%s): read error %s", 6416 map->map_mname, sm_errstring(errno)); 6417 rval = NULL; 6418 } 6419 else if (i == 0) 6420 { 6421 if (tTd(38, 20)) 6422 sm_dprintf("prog_map_lookup(%s): empty answer\n", 6423 map->map_mname); 6424 rval = NULL; 6425 } 6426 else 6427 { 6428 buf[i] = '\0'; 6429 p = strchr(buf, '\n'); 6430 if (p != NULL) 6431 *p = '\0'; 6432 6433 /* collect the return value */ 6434 if (bitset(MF_MATCHONLY, map->map_mflags)) 6435 rval = map_rewrite(map, name, strlen(name), NULL); 6436 else 6437 rval = map_rewrite(map, buf, strlen(buf), av); 6438 6439 /* now flush any additional output */ 6440 while ((i = read(fd, buf, sizeof buf)) > 0) 6441 continue; 6442 } 6443 6444 /* wait for the process to terminate */ 6445 (void) close(fd); 6446 status = waitfor(pid); 6447 save_errno = errno; 6448 (void) sm_releasesignal(SIGCHLD); 6449 errno = save_errno; 6450 6451 if (status == -1) 6452 { 6453 syserr("prog_map_lookup(%s): wait error %s", 6454 map->map_mname, sm_errstring(errno)); 6455 *statp = EX_SOFTWARE; 6456 rval = NULL; 6457 } 6458 else if (WIFEXITED(status)) 6459 { 6460 if ((*statp = WEXITSTATUS(status)) != EX_OK) 6461 rval = NULL; 6462 } 6463 else 6464 { 6465 syserr("prog_map_lookup(%s): child died on signal %d", 6466 map->map_mname, status); 6467 *statp = EX_UNAVAILABLE; 6468 rval = NULL; 6469 } 6470 return rval; 6471 } 6472 /* 6473 ** Sequenced map type. 6474 ** 6475 ** Tries each map in order until something matches, much like 6476 ** implicit. Stores go to the first map in the list that can 6477 ** support storing. 6478 ** 6479 ** This is slightly unusual in that there are two interfaces. 6480 ** The "sequence" interface lets you stack maps arbitrarily. 6481 ** The "switch" interface builds a sequence map by looking 6482 ** at a system-dependent configuration file such as 6483 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. 6484 ** 6485 ** We don't need an explicit open, since all maps are 6486 ** opened on demand. 6487 */ 6488 6489 /* 6490 ** SEQ_MAP_PARSE -- Sequenced map parsing 6491 */ 6492 6493 bool 6494 seq_map_parse(map, ap) 6495 MAP *map; 6496 char *ap; 6497 { 6498 int maxmap; 6499 6500 if (tTd(38, 2)) 6501 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap); 6502 maxmap = 0; 6503 while (*ap != '\0') 6504 { 6505 register char *p; 6506 STAB *s; 6507 6508 /* find beginning of map name */ 6509 while (isascii(*ap) && isspace(*ap)) 6510 ap++; 6511 for (p = ap; 6512 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.'; 6513 p++) 6514 continue; 6515 if (*p != '\0') 6516 *p++ = '\0'; 6517 while (*p != '\0' && (!isascii(*p) || !isalnum(*p))) 6518 p++; 6519 if (*ap == '\0') 6520 { 6521 ap = p; 6522 continue; 6523 } 6524 s = stab(ap, ST_MAP, ST_FIND); 6525 if (s == NULL) 6526 { 6527 syserr("Sequence map %s: unknown member map %s", 6528 map->map_mname, ap); 6529 } 6530 else if (maxmap >= MAXMAPSTACK) 6531 { 6532 syserr("Sequence map %s: too many member maps (%d max)", 6533 map->map_mname, MAXMAPSTACK); 6534 maxmap++; 6535 } 6536 else if (maxmap < MAXMAPSTACK) 6537 { 6538 map->map_stack[maxmap++] = &s->s_map; 6539 } 6540 ap = p; 6541 } 6542 return true; 6543 } 6544 6545 /* 6546 ** SWITCH_MAP_OPEN -- open a switched map 6547 ** 6548 ** This looks at the system-dependent configuration and builds 6549 ** a sequence map that does the same thing. 6550 ** 6551 ** Every system must define a switch_map_find routine in conf.c 6552 ** that will return the list of service types associated with a 6553 ** given service class. 6554 */ 6555 6556 bool 6557 switch_map_open(map, mode) 6558 MAP *map; 6559 int mode; 6560 { 6561 int mapno; 6562 int nmaps; 6563 char *maptype[MAXMAPSTACK]; 6564 6565 if (tTd(38, 2)) 6566 sm_dprintf("switch_map_open(%s, %s, %d)\n", 6567 map->map_mname, map->map_file, mode); 6568 6569 mode &= O_ACCMODE; 6570 nmaps = switch_map_find(map->map_file, maptype, map->map_return); 6571 if (tTd(38, 19)) 6572 { 6573 sm_dprintf("\tswitch_map_find => %d\n", nmaps); 6574 for (mapno = 0; mapno < nmaps; mapno++) 6575 sm_dprintf("\t\t%s\n", maptype[mapno]); 6576 } 6577 if (nmaps <= 0 || nmaps > MAXMAPSTACK) 6578 return false; 6579 6580 for (mapno = 0; mapno < nmaps; mapno++) 6581 { 6582 register STAB *s; 6583 char nbuf[MAXNAME + 1]; 6584 6585 if (maptype[mapno] == NULL) 6586 continue; 6587 (void) sm_strlcpyn(nbuf, sizeof nbuf, 3, 6588 map->map_mname, ".", maptype[mapno]); 6589 s = stab(nbuf, ST_MAP, ST_FIND); 6590 if (s == NULL) 6591 { 6592 syserr("Switch map %s: unknown member map %s", 6593 map->map_mname, nbuf); 6594 } 6595 else 6596 { 6597 map->map_stack[mapno] = &s->s_map; 6598 if (tTd(38, 4)) 6599 sm_dprintf("\tmap_stack[%d] = %s:%s\n", 6600 mapno, 6601 s->s_map.map_class->map_cname, 6602 nbuf); 6603 } 6604 } 6605 return true; 6606 } 6607 6608 #if 0 6609 /* 6610 ** SEQ_MAP_CLOSE -- close all underlying maps 6611 */ 6612 6613 void 6614 seq_map_close(map) 6615 MAP *map; 6616 { 6617 int mapno; 6618 6619 if (tTd(38, 9)) 6620 sm_dprintf("seq_map_close(%s)\n", map->map_mname); 6621 6622 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6623 { 6624 MAP *mm = map->map_stack[mapno]; 6625 6626 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags)) 6627 continue; 6628 mm->map_mflags |= MF_CLOSING; 6629 mm->map_class->map_close(mm); 6630 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 6631 } 6632 } 6633 #endif /* 0 */ 6634 6635 /* 6636 ** SEQ_MAP_LOOKUP -- sequenced map lookup 6637 */ 6638 6639 char * 6640 seq_map_lookup(map, key, args, pstat) 6641 MAP *map; 6642 char *key; 6643 char **args; 6644 int *pstat; 6645 { 6646 int mapno; 6647 int mapbit = 0x01; 6648 bool tempfail = false; 6649 6650 if (tTd(38, 20)) 6651 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key); 6652 6653 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) 6654 { 6655 MAP *mm = map->map_stack[mapno]; 6656 char *rv; 6657 6658 if (mm == NULL) 6659 continue; 6660 if (!bitset(MF_OPEN, mm->map_mflags) && 6661 !openmap(mm)) 6662 { 6663 if (bitset(mapbit, map->map_return[MA_UNAVAIL])) 6664 { 6665 *pstat = EX_UNAVAILABLE; 6666 return NULL; 6667 } 6668 continue; 6669 } 6670 *pstat = EX_OK; 6671 rv = mm->map_class->map_lookup(mm, key, args, pstat); 6672 if (rv != NULL) 6673 return rv; 6674 if (*pstat == EX_TEMPFAIL) 6675 { 6676 if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) 6677 return NULL; 6678 tempfail = true; 6679 } 6680 else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) 6681 break; 6682 } 6683 if (tempfail) 6684 *pstat = EX_TEMPFAIL; 6685 else if (*pstat == EX_OK) 6686 *pstat = EX_NOTFOUND; 6687 return NULL; 6688 } 6689 6690 /* 6691 ** SEQ_MAP_STORE -- sequenced map store 6692 */ 6693 6694 void 6695 seq_map_store(map, key, val) 6696 MAP *map; 6697 char *key; 6698 char *val; 6699 { 6700 int mapno; 6701 6702 if (tTd(38, 12)) 6703 sm_dprintf("seq_map_store(%s, %s, %s)\n", 6704 map->map_mname, key, val); 6705 6706 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6707 { 6708 MAP *mm = map->map_stack[mapno]; 6709 6710 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags)) 6711 continue; 6712 6713 mm->map_class->map_store(mm, key, val); 6714 return; 6715 } 6716 syserr("seq_map_store(%s, %s, %s): no writable map", 6717 map->map_mname, key, val); 6718 } 6719 /* 6720 ** NULL stubs 6721 */ 6722 6723 /* ARGSUSED */ 6724 bool 6725 null_map_open(map, mode) 6726 MAP *map; 6727 int mode; 6728 { 6729 return true; 6730 } 6731 6732 /* ARGSUSED */ 6733 void 6734 null_map_close(map) 6735 MAP *map; 6736 { 6737 return; 6738 } 6739 6740 char * 6741 null_map_lookup(map, key, args, pstat) 6742 MAP *map; 6743 char *key; 6744 char **args; 6745 int *pstat; 6746 { 6747 *pstat = EX_NOTFOUND; 6748 return NULL; 6749 } 6750 6751 /* ARGSUSED */ 6752 void 6753 null_map_store(map, key, val) 6754 MAP *map; 6755 char *key; 6756 char *val; 6757 { 6758 return; 6759 } 6760 6761 /* 6762 ** BOGUS stubs 6763 */ 6764 6765 char * 6766 bogus_map_lookup(map, key, args, pstat) 6767 MAP *map; 6768 char *key; 6769 char **args; 6770 int *pstat; 6771 { 6772 *pstat = EX_TEMPFAIL; 6773 return NULL; 6774 } 6775 6776 MAPCLASS BogusMapClass = 6777 { 6778 "bogus-map", NULL, 0, 6779 NULL, bogus_map_lookup, null_map_store, 6780 null_map_open, null_map_close, 6781 }; 6782 /* 6783 ** MACRO modules 6784 */ 6785 6786 char * 6787 macro_map_lookup(map, name, av, statp) 6788 MAP *map; 6789 char *name; 6790 char **av; 6791 int *statp; 6792 { 6793 int mid; 6794 6795 if (tTd(38, 20)) 6796 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname, 6797 name == NULL ? "NULL" : name); 6798 6799 if (name == NULL || 6800 *name == '\0' || 6801 (mid = macid(name)) == 0) 6802 { 6803 *statp = EX_CONFIG; 6804 return NULL; 6805 } 6806 6807 if (av[1] == NULL) 6808 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL); 6809 else 6810 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]); 6811 6812 *statp = EX_OK; 6813 return ""; 6814 } 6815 /* 6816 ** REGEX modules 6817 */ 6818 6819 #if MAP_REGEX 6820 6821 # include <regex.h> 6822 6823 # define DEFAULT_DELIM CONDELSE 6824 # define END_OF_FIELDS -1 6825 # define ERRBUF_SIZE 80 6826 # define MAX_MATCH 32 6827 6828 # define xnalloc(s) memset(xalloc(s), '\0', s); 6829 6830 struct regex_map 6831 { 6832 regex_t *regex_pattern_buf; /* xalloc it */ 6833 int *regex_subfields; /* move to type MAP */ 6834 char *regex_delim; /* move to type MAP */ 6835 }; 6836 6837 static int 6838 parse_fields(s, ibuf, blen, nr_substrings) 6839 char *s; 6840 int *ibuf; /* array */ 6841 int blen; /* number of elements in ibuf */ 6842 int nr_substrings; /* number of substrings in the pattern */ 6843 { 6844 register char *cp; 6845 int i = 0; 6846 bool lastone = false; 6847 6848 blen--; /* for terminating END_OF_FIELDS */ 6849 cp = s; 6850 do 6851 { 6852 for (;; cp++) 6853 { 6854 if (*cp == ',') 6855 { 6856 *cp = '\0'; 6857 break; 6858 } 6859 if (*cp == '\0') 6860 { 6861 lastone = true; 6862 break; 6863 } 6864 } 6865 if (i < blen) 6866 { 6867 int val = atoi(s); 6868 6869 if (val < 0 || val >= nr_substrings) 6870 { 6871 syserr("field (%d) out of range, only %d substrings in pattern", 6872 val, nr_substrings); 6873 return -1; 6874 } 6875 ibuf[i++] = val; 6876 } 6877 else 6878 { 6879 syserr("too many fields, %d max", blen); 6880 return -1; 6881 } 6882 s = ++cp; 6883 } while (!lastone); 6884 ibuf[i] = END_OF_FIELDS; 6885 return i; 6886 } 6887 6888 bool 6889 regex_map_init(map, ap) 6890 MAP *map; 6891 char *ap; 6892 { 6893 int regerr; 6894 struct regex_map *map_p; 6895 register char *p; 6896 char *sub_param = NULL; 6897 int pflags; 6898 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' }; 6899 6900 if (tTd(38, 2)) 6901 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n", 6902 map->map_mname, ap); 6903 6904 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; 6905 p = ap; 6906 map_p = (struct regex_map *) xnalloc(sizeof *map_p); 6907 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t)); 6908 6909 for (;;) 6910 { 6911 while (isascii(*p) && isspace(*p)) 6912 p++; 6913 if (*p != '-') 6914 break; 6915 switch (*++p) 6916 { 6917 case 'n': /* not */ 6918 map->map_mflags |= MF_REGEX_NOT; 6919 break; 6920 6921 case 'f': /* case sensitive */ 6922 map->map_mflags |= MF_NOFOLDCASE; 6923 pflags &= ~REG_ICASE; 6924 break; 6925 6926 case 'b': /* basic regular expressions */ 6927 pflags &= ~REG_EXTENDED; 6928 break; 6929 6930 case 's': /* substring match () syntax */ 6931 sub_param = ++p; 6932 pflags &= ~REG_NOSUB; 6933 break; 6934 6935 case 'd': /* delimiter */ 6936 map_p->regex_delim = ++p; 6937 break; 6938 6939 case 'a': /* map append */ 6940 map->map_app = ++p; 6941 break; 6942 6943 case 'm': /* matchonly */ 6944 map->map_mflags |= MF_MATCHONLY; 6945 break; 6946 6947 case 'q': 6948 map->map_mflags |= MF_KEEPQUOTES; 6949 break; 6950 6951 case 'S': 6952 map->map_spacesub = *++p; 6953 break; 6954 6955 case 'D': 6956 map->map_mflags |= MF_DEFER; 6957 break; 6958 6959 } 6960 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 6961 p++; 6962 if (*p != '\0') 6963 *p++ = '\0'; 6964 } 6965 if (tTd(38, 3)) 6966 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags); 6967 6968 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0) 6969 { 6970 /* Errorhandling */ 6971 char errbuf[ERRBUF_SIZE]; 6972 6973 (void) regerror(regerr, map_p->regex_pattern_buf, 6974 errbuf, sizeof errbuf); 6975 syserr("pattern-compile-error: %s", errbuf); 6976 sm_free(map_p->regex_pattern_buf); /* XXX */ 6977 sm_free(map_p); /* XXX */ 6978 return false; 6979 } 6980 6981 if (map->map_app != NULL) 6982 map->map_app = newstr(map->map_app); 6983 if (map_p->regex_delim != NULL) 6984 map_p->regex_delim = newstr(map_p->regex_delim); 6985 else 6986 map_p->regex_delim = defdstr; 6987 6988 if (!bitset(REG_NOSUB, pflags)) 6989 { 6990 /* substring matching */ 6991 int substrings; 6992 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1)); 6993 6994 substrings = map_p->regex_pattern_buf->re_nsub + 1; 6995 6996 if (tTd(38, 3)) 6997 sm_dprintf("regex_map_init: nr of substrings %d\n", 6998 substrings); 6999 7000 if (substrings >= MAX_MATCH) 7001 { 7002 syserr("too many substrings, %d max", MAX_MATCH); 7003 sm_free(map_p->regex_pattern_buf); /* XXX */ 7004 sm_free(map_p); /* XXX */ 7005 return false; 7006 } 7007 if (sub_param != NULL && sub_param[0] != '\0') 7008 { 7009 /* optional parameter -sfields */ 7010 if (parse_fields(sub_param, fields, 7011 MAX_MATCH + 1, substrings) == -1) 7012 return false; 7013 } 7014 else 7015 { 7016 int i; 7017 7018 /* set default fields */ 7019 for (i = 0; i < substrings; i++) 7020 fields[i] = i; 7021 fields[i] = END_OF_FIELDS; 7022 } 7023 map_p->regex_subfields = fields; 7024 if (tTd(38, 3)) 7025 { 7026 int *ip; 7027 7028 sm_dprintf("regex_map_init: subfields"); 7029 for (ip = fields; *ip != END_OF_FIELDS; ip++) 7030 sm_dprintf(" %d", *ip); 7031 sm_dprintf("\n"); 7032 } 7033 } 7034 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */ 7035 return true; 7036 } 7037 7038 static char * 7039 regex_map_rewrite(map, s, slen, av) 7040 MAP *map; 7041 const char *s; 7042 size_t slen; 7043 char **av; 7044 { 7045 if (bitset(MF_MATCHONLY, map->map_mflags)) 7046 return map_rewrite(map, av[0], strlen(av[0]), NULL); 7047 else 7048 return map_rewrite(map, s, slen, av); 7049 } 7050 7051 char * 7052 regex_map_lookup(map, name, av, statp) 7053 MAP *map; 7054 char *name; 7055 char **av; 7056 int *statp; 7057 { 7058 int reg_res; 7059 struct regex_map *map_p; 7060 regmatch_t pmatch[MAX_MATCH]; 7061 7062 if (tTd(38, 20)) 7063 { 7064 char **cpp; 7065 7066 sm_dprintf("regex_map_lookup: key '%s'\n", name); 7067 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7068 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp); 7069 } 7070 7071 map_p = (struct regex_map *)(map->map_db1); 7072 reg_res = regexec(map_p->regex_pattern_buf, 7073 name, MAX_MATCH, pmatch, 0); 7074 7075 if (bitset(MF_REGEX_NOT, map->map_mflags)) 7076 { 7077 /* option -n */ 7078 if (reg_res == REG_NOMATCH) 7079 return regex_map_rewrite(map, "", (size_t) 0, av); 7080 else 7081 return NULL; 7082 } 7083 if (reg_res == REG_NOMATCH) 7084 return NULL; 7085 7086 if (map_p->regex_subfields != NULL) 7087 { 7088 /* option -s */ 7089 static char retbuf[MAXNAME]; 7090 int fields[MAX_MATCH + 1]; 7091 bool first = true; 7092 int anglecnt = 0, cmntcnt = 0, spacecnt = 0; 7093 bool quotemode = false, bslashmode = false; 7094 register char *dp, *sp; 7095 char *endp, *ldp; 7096 int *ip; 7097 7098 dp = retbuf; 7099 ldp = retbuf + sizeof(retbuf) - 1; 7100 7101 if (av[1] != NULL) 7102 { 7103 if (parse_fields(av[1], fields, MAX_MATCH + 1, 7104 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1) 7105 { 7106 *statp = EX_CONFIG; 7107 return NULL; 7108 } 7109 ip = fields; 7110 } 7111 else 7112 ip = map_p->regex_subfields; 7113 7114 for ( ; *ip != END_OF_FIELDS; ip++) 7115 { 7116 if (!first) 7117 { 7118 for (sp = map_p->regex_delim; *sp; sp++) 7119 { 7120 if (dp < ldp) 7121 *dp++ = *sp; 7122 } 7123 } 7124 else 7125 first = false; 7126 7127 if (*ip >= MAX_MATCH || 7128 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0) 7129 continue; 7130 7131 sp = name + pmatch[*ip].rm_so; 7132 endp = name + pmatch[*ip].rm_eo; 7133 for (; endp > sp; sp++) 7134 { 7135 if (dp < ldp) 7136 { 7137 if (bslashmode) 7138 { 7139 *dp++ = *sp; 7140 bslashmode = false; 7141 } 7142 else if (quotemode && *sp != '"' && 7143 *sp != '\\') 7144 { 7145 *dp++ = *sp; 7146 } 7147 else switch (*dp++ = *sp) 7148 { 7149 case '\\': 7150 bslashmode = true; 7151 break; 7152 7153 case '(': 7154 cmntcnt++; 7155 break; 7156 7157 case ')': 7158 cmntcnt--; 7159 break; 7160 7161 case '<': 7162 anglecnt++; 7163 break; 7164 7165 case '>': 7166 anglecnt--; 7167 break; 7168 7169 case ' ': 7170 spacecnt++; 7171 break; 7172 7173 case '"': 7174 quotemode = !quotemode; 7175 break; 7176 } 7177 } 7178 } 7179 } 7180 if (anglecnt != 0 || cmntcnt != 0 || quotemode || 7181 bslashmode || spacecnt != 0) 7182 { 7183 sm_syslog(LOG_WARNING, NOQID, 7184 "Warning: regex may cause prescan() failure map=%s lookup=%s", 7185 map->map_mname, name); 7186 return NULL; 7187 } 7188 7189 *dp = '\0'; 7190 7191 return regex_map_rewrite(map, retbuf, strlen(retbuf), av); 7192 } 7193 return regex_map_rewrite(map, "", (size_t)0, av); 7194 } 7195 #endif /* MAP_REGEX */ 7196 /* 7197 ** NSD modules 7198 */ 7199 #if MAP_NSD 7200 7201 # include <ndbm.h> 7202 # define _DATUM_DEFINED 7203 # include <ns_api.h> 7204 7205 typedef struct ns_map_list 7206 { 7207 ns_map_t *map; /* XXX ns_ ? */ 7208 char *mapname; 7209 struct ns_map_list *next; 7210 } ns_map_list_t; 7211 7212 static ns_map_t * 7213 ns_map_t_find(mapname) 7214 char *mapname; 7215 { 7216 static ns_map_list_t *ns_maps = NULL; 7217 ns_map_list_t *ns_map; 7218 7219 /* walk the list of maps looking for the correctly named map */ 7220 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next) 7221 { 7222 if (strcmp(ns_map->mapname, mapname) == 0) 7223 break; 7224 } 7225 7226 /* if we are looking at a NULL ns_map_list_t, then create a new one */ 7227 if (ns_map == NULL) 7228 { 7229 ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map); 7230 ns_map->mapname = newstr(mapname); 7231 ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map); 7232 memset(ns_map->map, '\0', sizeof *ns_map->map); 7233 ns_map->next = ns_maps; 7234 ns_maps = ns_map; 7235 } 7236 return ns_map->map; 7237 } 7238 7239 char * 7240 nsd_map_lookup(map, name, av, statp) 7241 MAP *map; 7242 char *name; 7243 char **av; 7244 int *statp; 7245 { 7246 int buflen, r; 7247 char *p; 7248 ns_map_t *ns_map; 7249 char keybuf[MAXNAME + 1]; 7250 char buf[MAXLINE]; 7251 7252 if (tTd(38, 20)) 7253 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name); 7254 7255 buflen = strlen(name); 7256 if (buflen > sizeof keybuf - 1) 7257 buflen = sizeof keybuf - 1; /* XXX simply cut off? */ 7258 memmove(keybuf, name, buflen); 7259 keybuf[buflen] = '\0'; 7260 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 7261 makelower(keybuf); 7262 7263 ns_map = ns_map_t_find(map->map_file); 7264 if (ns_map == NULL) 7265 { 7266 if (tTd(38, 20)) 7267 sm_dprintf("nsd_map_t_find failed\n"); 7268 *statp = EX_UNAVAILABLE; 7269 return NULL; 7270 } 7271 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL, 7272 buf, sizeof buf); 7273 if (r == NS_UNAVAIL || r == NS_TRYAGAIN) 7274 { 7275 *statp = EX_TEMPFAIL; 7276 return NULL; 7277 } 7278 if (r == NS_BADREQ 7279 # ifdef NS_NOPERM 7280 || r == NS_NOPERM 7281 # endif /* NS_NOPERM */ 7282 ) 7283 { 7284 *statp = EX_CONFIG; 7285 return NULL; 7286 } 7287 if (r != NS_SUCCESS) 7288 { 7289 *statp = EX_NOTFOUND; 7290 return NULL; 7291 } 7292 7293 *statp = EX_OK; 7294 7295 /* Null out trailing \n */ 7296 if ((p = strchr(buf, '\n')) != NULL) 7297 *p = '\0'; 7298 7299 return map_rewrite(map, buf, strlen(buf), av); 7300 } 7301 #endif /* MAP_NSD */ 7302 7303 char * 7304 arith_map_lookup(map, name, av, statp) 7305 MAP *map; 7306 char *name; 7307 char **av; 7308 int *statp; 7309 { 7310 long r; 7311 long v[2]; 7312 bool res = false; 7313 bool boolres; 7314 static char result[16]; 7315 char **cpp; 7316 7317 if (tTd(38, 2)) 7318 { 7319 sm_dprintf("arith_map_lookup: key '%s'\n", name); 7320 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7321 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp); 7322 } 7323 r = 0; 7324 boolres = false; 7325 cpp = av; 7326 *statp = EX_OK; 7327 7328 /* 7329 ** read arguments for arith map 7330 ** - no check is made whether they are really numbers 7331 ** - just ignores args after the second 7332 */ 7333 7334 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++) 7335 v[r++] = strtol(*cpp, NULL, 0); 7336 7337 /* operator and (at least) two operands given? */ 7338 if (name != NULL && r == 2) 7339 { 7340 switch (*name) 7341 { 7342 case '|': 7343 r = v[0] | v[1]; 7344 break; 7345 7346 case '&': 7347 r = v[0] & v[1]; 7348 break; 7349 7350 case '%': 7351 if (v[1] == 0) 7352 return NULL; 7353 r = v[0] % v[1]; 7354 break; 7355 case '+': 7356 r = v[0] + v[1]; 7357 break; 7358 7359 case '-': 7360 r = v[0] - v[1]; 7361 break; 7362 7363 case '*': 7364 r = v[0] * v[1]; 7365 break; 7366 7367 case '/': 7368 if (v[1] == 0) 7369 return NULL; 7370 r = v[0] / v[1]; 7371 break; 7372 7373 case 'l': 7374 res = v[0] < v[1]; 7375 boolres = true; 7376 break; 7377 7378 case '=': 7379 res = v[0] == v[1]; 7380 boolres = true; 7381 break; 7382 7383 default: 7384 /* XXX */ 7385 *statp = EX_CONFIG; 7386 if (LogLevel > 10) 7387 sm_syslog(LOG_WARNING, NOQID, 7388 "arith_map: unknown operator %c", 7389 isprint(*name) ? *name : '?'); 7390 return NULL; 7391 } 7392 if (boolres) 7393 (void) sm_snprintf(result, sizeof result, 7394 res ? "TRUE" : "FALSE"); 7395 else 7396 (void) sm_snprintf(result, sizeof result, "%ld", r); 7397 return result; 7398 } 7399 *statp = EX_CONFIG; 7400 return NULL; 7401 } 7402