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