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.1 2002/06/21 20:25:23 ca Exp $") 17 18 #if LDAPMAP 19 # include <sm/ldap.h> 20 #endif /* LDAPMAP */ 21 22 #if NDBM 23 # include <ndbm.h> 24 # ifdef R_FIRST 25 ERROR README: You are running the Berkeley DB version of ndbm.h. See 26 ERROR README: the README file about tweaking Berkeley DB so it can 27 ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile 28 ERROR README: and use -DNEWDB instead. 29 # endif /* R_FIRST */ 30 #endif /* NDBM */ 31 #if NEWDB 32 # include <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 *p++ = lmap->ldap_attrsep; 3815 } 3816 p += sm_strlcpy(p, vals[i], 3817 vsize - (p - vp_tmp)); 3818 if (p >= vp_tmp + vsize) 3819 syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values"); 3820 if (vals[i + 1] != NULL) 3821 *p++ = map->map_coldelim; 3822 } 3823 3824 ldap_value_free(vals); 3825 ldap_memfree(attr); 3826 if (vp == NULL) 3827 { 3828 vp = vp_tmp; 3829 continue; 3830 } 3831 vsize = strlen(vp) + strlen(vp_tmp) + 2; 3832 tmp = xalloc(vsize); 3833 (void) sm_snprintf(tmp, vsize, "%s%c%s", 3834 vp, map->map_coldelim, vp_tmp); 3835 3836 sm_free(vp); /* XXX */ 3837 sm_free(vp_tmp); /* XXX */ 3838 vp = tmp; 3839 } 3840 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 3841 3842 /* 3843 ** We check errno != LDAP_DECODING_ERROR since 3844 ** OpenLDAP 1.X has a very ugly *undocumented* 3845 ** hack of returning this error code from 3846 ** ldap_next_attribute() if the library freed the 3847 ** ber attribute. See: 3848 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html 3849 */ 3850 3851 if (save_errno != LDAP_SUCCESS && 3852 save_errno != LDAP_DECODING_ERROR) 3853 { 3854 /* Must be an error */ 3855 save_errno += E_LDAPBASE; 3856 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3857 { 3858 errno = save_errno; 3859 if (bitset(MF_NODEFER, map->map_mflags)) 3860 syserr("Error getting LDAP attributes in map %s", 3861 map->map_mname); 3862 else 3863 syserr("451 4.3.5 Error getting LDAP attributes in map %s", 3864 map->map_mname); 3865 } 3866 *statp = EX_TEMPFAIL; 3867 if (lmap->ldap_res != NULL) 3868 { 3869 ldap_msgfree(lmap->ldap_res); 3870 lmap->ldap_res = NULL; 3871 } 3872 (void) ldap_abandon(lmap->ldap_ld, msgid); 3873 if (vp != NULL) 3874 sm_free(vp); /* XXX */ 3875 errno = save_errno; 3876 return NULL; 3877 } 3878 3879 /* We don't want multiple values and we have one */ 3880 if (map->map_coldelim == '\0' && vp != NULL) 3881 break; 3882 } 3883 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 3884 if (save_errno != LDAP_SUCCESS && 3885 save_errno != LDAP_DECODING_ERROR) 3886 { 3887 /* Must be an error */ 3888 save_errno += E_LDAPBASE; 3889 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3890 { 3891 errno = save_errno; 3892 if (bitset(MF_NODEFER, map->map_mflags)) 3893 syserr("Error getting LDAP entries in map %s", 3894 map->map_mname); 3895 else 3896 syserr("451 4.3.5 Error getting LDAP entries in map %s", 3897 map->map_mname); 3898 } 3899 *statp = EX_TEMPFAIL; 3900 if (lmap->ldap_res != NULL) 3901 { 3902 ldap_msgfree(lmap->ldap_res); 3903 lmap->ldap_res = NULL; 3904 } 3905 (void) ldap_abandon(lmap->ldap_ld, msgid); 3906 if (vp != NULL) 3907 sm_free(vp); /* XXX */ 3908 errno = save_errno; 3909 return NULL; 3910 } 3911 ldap_msgfree(lmap->ldap_res); 3912 lmap->ldap_res = NULL; 3913 } 3914 3915 if (ret == 0) 3916 save_errno = ETIMEDOUT; 3917 else 3918 save_errno = sm_ldap_geterrno(lmap->ldap_ld); 3919 if (save_errno != LDAP_SUCCESS) 3920 { 3921 if (ret != 0) 3922 save_errno += E_LDAPBASE; 3923 3924 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3925 { 3926 errno = save_errno; 3927 if (bitset(MF_NODEFER, map->map_mflags)) 3928 syserr("Error getting LDAP results in map %s", 3929 map->map_mname); 3930 else 3931 syserr("451 4.3.5 Error getting LDAP results in map %s", 3932 map->map_mname); 3933 } 3934 *statp = EX_TEMPFAIL; 3935 if (vp != NULL) 3936 sm_free(vp); /* XXX */ 3937 3938 switch (save_errno - E_LDAPBASE) 3939 { 3940 # ifdef LDAP_SERVER_DOWN 3941 case LDAP_SERVER_DOWN: 3942 # endif /* LDAP_SERVER_DOWN */ 3943 case LDAP_TIMEOUT: 3944 case LDAP_UNAVAILABLE: 3945 /* server disappeared, try reopen on next search */ 3946 ldapmap_close(map); 3947 break; 3948 } 3949 errno = save_errno; 3950 return NULL; 3951 } 3952 # endif /* _FFR_LDAP_RECURSION */ 3953 3954 /* Did we match anything? */ 3955 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags)) 3956 return NULL; 3957 3958 if (*statp == EX_OK) 3959 { 3960 if (LogLevel > 9) 3961 sm_syslog(LOG_INFO, CurEnv->e_id, 3962 "ldap %.100s => %s", name, 3963 vp == NULL ? "<NULL>" : vp); 3964 if (bitset(MF_MATCHONLY, map->map_mflags)) 3965 result = map_rewrite(map, name, strlen(name), NULL); 3966 else 3967 { 3968 /* vp != NULL according to test above */ 3969 result = map_rewrite(map, vp, strlen(vp), av); 3970 } 3971 if (vp != NULL) 3972 sm_free(vp); /* XXX */ 3973 } 3974 return result; 3975 } 3976 3977 /* 3978 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server 3979 ** 3980 ** Cache LDAP connections based on the host, port, bind DN, 3981 ** secret, and PID so we don't have multiple connections open to 3982 ** the same server for different maps. Need a separate connection 3983 ** per PID since a parent process may close the map before the 3984 ** child is done with it. 3985 ** 3986 ** Parameters: 3987 ** lmap -- LDAP map information 3988 ** 3989 ** Returns: 3990 ** Symbol table entry for the LDAP connection. 3991 */ 3992 3993 static STAB * 3994 ldapmap_findconn(lmap) 3995 SM_LDAP_STRUCT *lmap; 3996 { 3997 char *format; 3998 char *nbuf; 3999 STAB *SM_NONVOLATILE s = NULL; 4000 4001 # if _FFR_LDAP_SETVERSION 4002 format = "%s%c%d%c%d%c%s%c%s%d"; 4003 # else /* _FFR_LDAP_SETVERSION */ 4004 format = "%s%c%d%c%s%c%s%d"; 4005 # endif /* _FFR_LDAP_SETVERSION */ 4006 nbuf = sm_stringf_x(format, 4007 (lmap->ldap_target == NULL ? "localhost" 4008 : lmap->ldap_target), 4009 CONDELSE, 4010 lmap->ldap_port, 4011 CONDELSE, 4012 # if _FFR_LDAP_SETVERSION 4013 lmap->ldap_version, 4014 CONDELSE, 4015 # endif /* _FFR_LDAP_SETVERSION */ 4016 (lmap->ldap_binddn == NULL ? "" 4017 : lmap->ldap_binddn), 4018 CONDELSE, 4019 (lmap->ldap_secret == NULL ? "" 4020 : lmap->ldap_secret), 4021 (int) CurrentPid); 4022 SM_TRY 4023 s = stab(nbuf, ST_LMAP, ST_ENTER); 4024 SM_FINALLY 4025 sm_free(nbuf); 4026 SM_END_TRY 4027 return s; 4028 } 4029 /* 4030 ** LDAPMAP_PARSEARGS -- parse ldap map definition args. 4031 */ 4032 4033 static struct lamvalues LDAPAuthMethods[] = 4034 { 4035 { "none", LDAP_AUTH_NONE }, 4036 { "simple", LDAP_AUTH_SIMPLE }, 4037 # ifdef LDAP_AUTH_KRBV4 4038 { "krbv4", LDAP_AUTH_KRBV4 }, 4039 # endif /* LDAP_AUTH_KRBV4 */ 4040 { NULL, 0 } 4041 }; 4042 4043 static struct ladvalues LDAPAliasDereference[] = 4044 { 4045 { "never", LDAP_DEREF_NEVER }, 4046 { "always", LDAP_DEREF_ALWAYS }, 4047 { "search", LDAP_DEREF_SEARCHING }, 4048 { "find", LDAP_DEREF_FINDING }, 4049 { NULL, 0 } 4050 }; 4051 4052 static struct lssvalues LDAPSearchScope[] = 4053 { 4054 { "base", LDAP_SCOPE_BASE }, 4055 { "one", LDAP_SCOPE_ONELEVEL }, 4056 { "sub", LDAP_SCOPE_SUBTREE }, 4057 { NULL, 0 } 4058 }; 4059 4060 bool 4061 ldapmap_parseargs(map, args) 4062 MAP *map; 4063 char *args; 4064 { 4065 bool secretread = true; 4066 # if _FFR_LDAP_URI 4067 bool ldaphost = false; 4068 # endif /* _FFR_LDAP_URI */ 4069 int i; 4070 register char *p = args; 4071 SM_LDAP_STRUCT *lmap; 4072 struct lamvalues *lam; 4073 struct ladvalues *lad; 4074 struct lssvalues *lss; 4075 char ldapfilt[MAXLINE]; 4076 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD]; 4077 4078 /* Get ldap struct pointer from map */ 4079 lmap = (SM_LDAP_STRUCT *) map->map_db1; 4080 4081 /* Check if setting the initial LDAP defaults */ 4082 if (lmap == NULL || lmap != LDAPDefaults) 4083 { 4084 /* We need to alloc an SM_LDAP_STRUCT struct */ 4085 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof *lmap); 4086 if (LDAPDefaults == NULL) 4087 sm_ldap_clear(lmap); 4088 else 4089 STRUCTCOPY(*LDAPDefaults, *lmap); 4090 } 4091 4092 /* there is no check whether there is really an argument */ 4093 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 4094 map->map_spacesub = SpaceSub; /* default value */ 4095 4096 /* Check if setting up an alias or file class LDAP map */ 4097 if (bitset(MF_ALIAS, map->map_mflags)) 4098 { 4099 /* Comma separate if used as an alias file */ 4100 map->map_coldelim = ','; 4101 if (*args == '\0') 4102 { 4103 int n; 4104 char *lc; 4105 char jbuf[MAXHOSTNAMELEN]; 4106 char lcbuf[MAXLINE]; 4107 4108 /* Get $j */ 4109 expand("\201j", jbuf, sizeof jbuf, &BlankEnvelope); 4110 if (jbuf[0] == '\0') 4111 { 4112 (void) sm_strlcpy(jbuf, "localhost", 4113 sizeof jbuf); 4114 } 4115 4116 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv); 4117 if (lc == NULL) 4118 lc = ""; 4119 else 4120 { 4121 expand(lc, lcbuf, sizeof lcbuf, CurEnv); 4122 lc = lcbuf; 4123 } 4124 4125 n = sm_snprintf(ldapfilt, sizeof ldapfilt, 4126 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))", 4127 lc, jbuf); 4128 if (n >= sizeof ldapfilt) 4129 { 4130 syserr("%s: Default LDAP string too long", 4131 map->map_mname); 4132 return false; 4133 } 4134 4135 /* default args for an alias LDAP entry */ 4136 lmap->ldap_filter = ldapfilt; 4137 lmap->ldap_attr[0] = "sendmailMTAAliasValue"; 4138 lmap->ldap_attr[1] = NULL; 4139 } 4140 } 4141 else if (bitset(MF_FILECLASS, map->map_mflags)) 4142 { 4143 /* Space separate if used as a file class file */ 4144 map->map_coldelim = ' '; 4145 } 4146 4147 for (;;) 4148 { 4149 while (isascii(*p) && isspace(*p)) 4150 p++; 4151 if (*p != '-') 4152 break; 4153 switch (*++p) 4154 { 4155 case 'N': 4156 map->map_mflags |= MF_INCLNULL; 4157 map->map_mflags &= ~MF_TRY0NULL; 4158 break; 4159 4160 case 'O': 4161 map->map_mflags &= ~MF_TRY1NULL; 4162 break; 4163 4164 case 'o': 4165 map->map_mflags |= MF_OPTIONAL; 4166 break; 4167 4168 case 'f': 4169 map->map_mflags |= MF_NOFOLDCASE; 4170 break; 4171 4172 case 'm': 4173 map->map_mflags |= MF_MATCHONLY; 4174 break; 4175 4176 case 'A': 4177 map->map_mflags |= MF_APPEND; 4178 break; 4179 4180 case 'q': 4181 map->map_mflags |= MF_KEEPQUOTES; 4182 break; 4183 4184 case 'a': 4185 map->map_app = ++p; 4186 break; 4187 4188 case 'T': 4189 map->map_tapp = ++p; 4190 break; 4191 4192 case 't': 4193 map->map_mflags |= MF_NODEFER; 4194 break; 4195 4196 case 'S': 4197 map->map_spacesub = *++p; 4198 break; 4199 4200 case 'D': 4201 map->map_mflags |= MF_DEFER; 4202 break; 4203 4204 case 'z': 4205 if (*++p != '\\') 4206 map->map_coldelim = *p; 4207 else 4208 { 4209 switch (*++p) 4210 { 4211 case 'n': 4212 map->map_coldelim = '\n'; 4213 break; 4214 4215 case 't': 4216 map->map_coldelim = '\t'; 4217 break; 4218 4219 default: 4220 map->map_coldelim = '\\'; 4221 } 4222 } 4223 break; 4224 4225 /* Start of ldapmap specific args */ 4226 case 'V': 4227 if (*++p != '\\') 4228 lmap->ldap_attrsep = *p; 4229 else 4230 { 4231 switch (*++p) 4232 { 4233 case 'n': 4234 lmap->ldap_attrsep = '\n'; 4235 break; 4236 4237 case 't': 4238 lmap->ldap_attrsep = '\t'; 4239 break; 4240 4241 default: 4242 lmap->ldap_attrsep = '\\'; 4243 } 4244 } 4245 break; 4246 4247 case 'k': /* search field */ 4248 while (isascii(*++p) && isspace(*p)) 4249 continue; 4250 lmap->ldap_filter = p; 4251 break; 4252 4253 case 'v': /* attr to return */ 4254 while (isascii(*++p) && isspace(*p)) 4255 continue; 4256 lmap->ldap_attr[0] = p; 4257 lmap->ldap_attr[1] = NULL; 4258 break; 4259 4260 case '1': 4261 map->map_mflags |= MF_SINGLEMATCH; 4262 break; 4263 4264 /* args stolen from ldapsearch.c */ 4265 case 'R': /* don't auto chase referrals */ 4266 # ifdef LDAP_REFERRALS 4267 lmap->ldap_options &= ~LDAP_OPT_REFERRALS; 4268 # else /* LDAP_REFERRALS */ 4269 syserr("compile with -DLDAP_REFERRALS for referral support"); 4270 # endif /* LDAP_REFERRALS */ 4271 break; 4272 4273 case 'n': /* retrieve attribute names only */ 4274 lmap->ldap_attrsonly = LDAPMAP_TRUE; 4275 break; 4276 4277 case 'r': /* alias dereferencing */ 4278 while (isascii(*++p) && isspace(*p)) 4279 continue; 4280 4281 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0) 4282 p += 11; 4283 4284 for (lad = LDAPAliasDereference; 4285 lad != NULL && lad->lad_name != NULL; lad++) 4286 { 4287 if (sm_strncasecmp(p, lad->lad_name, 4288 strlen(lad->lad_name)) == 0) 4289 break; 4290 } 4291 if (lad->lad_name != NULL) 4292 lmap->ldap_deref = lad->lad_code; 4293 else 4294 { 4295 /* bad config line */ 4296 if (!bitset(MCF_OPTFILE, 4297 map->map_class->map_cflags)) 4298 { 4299 char *ptr; 4300 4301 if ((ptr = strchr(p, ' ')) != NULL) 4302 *ptr = '\0'; 4303 syserr("Deref must be [never|always|search|find] (not %s) in map %s", 4304 p, map->map_mname); 4305 if (ptr != NULL) 4306 *ptr = ' '; 4307 return false; 4308 } 4309 } 4310 break; 4311 4312 case 's': /* search scope */ 4313 while (isascii(*++p) && isspace(*p)) 4314 continue; 4315 4316 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0) 4317 p += 11; 4318 4319 for (lss = LDAPSearchScope; 4320 lss != NULL && lss->lss_name != NULL; lss++) 4321 { 4322 if (sm_strncasecmp(p, lss->lss_name, 4323 strlen(lss->lss_name)) == 0) 4324 break; 4325 } 4326 if (lss->lss_name != NULL) 4327 lmap->ldap_scope = lss->lss_code; 4328 else 4329 { 4330 /* bad config line */ 4331 if (!bitset(MCF_OPTFILE, 4332 map->map_class->map_cflags)) 4333 { 4334 char *ptr; 4335 4336 if ((ptr = strchr(p, ' ')) != NULL) 4337 *ptr = '\0'; 4338 syserr("Scope must be [base|one|sub] (not %s) in map %s", 4339 p, map->map_mname); 4340 if (ptr != NULL) 4341 *ptr = ' '; 4342 return false; 4343 } 4344 } 4345 break; 4346 4347 case 'h': /* ldap host */ 4348 while (isascii(*++p) && isspace(*p)) 4349 continue; 4350 # if _FFR_LDAP_URI 4351 if (lmap->ldap_uri) 4352 { 4353 syserr("Can not specify both an LDAP host and an LDAP URI in map %s", 4354 map->map_mname); 4355 return false; 4356 } 4357 ldaphost = true; 4358 # endif /* _FFR_LDAP_URI */ 4359 lmap->ldap_target = p; 4360 break; 4361 4362 case 'b': /* search base */ 4363 while (isascii(*++p) && isspace(*p)) 4364 continue; 4365 lmap->ldap_base = p; 4366 break; 4367 4368 case 'p': /* ldap port */ 4369 while (isascii(*++p) && isspace(*p)) 4370 continue; 4371 lmap->ldap_port = atoi(p); 4372 break; 4373 4374 case 'l': /* time limit */ 4375 while (isascii(*++p) && isspace(*p)) 4376 continue; 4377 lmap->ldap_timelimit = atoi(p); 4378 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit; 4379 break; 4380 4381 case 'Z': 4382 while (isascii(*++p) && isspace(*p)) 4383 continue; 4384 lmap->ldap_sizelimit = atoi(p); 4385 break; 4386 4387 case 'd': /* Dn to bind to server as */ 4388 while (isascii(*++p) && isspace(*p)) 4389 continue; 4390 lmap->ldap_binddn = p; 4391 break; 4392 4393 case 'M': /* Method for binding */ 4394 while (isascii(*++p) && isspace(*p)) 4395 continue; 4396 4397 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0) 4398 p += 10; 4399 4400 for (lam = LDAPAuthMethods; 4401 lam != NULL && lam->lam_name != NULL; lam++) 4402 { 4403 if (sm_strncasecmp(p, lam->lam_name, 4404 strlen(lam->lam_name)) == 0) 4405 break; 4406 } 4407 if (lam->lam_name != NULL) 4408 lmap->ldap_method = lam->lam_code; 4409 else 4410 { 4411 /* bad config line */ 4412 if (!bitset(MCF_OPTFILE, 4413 map->map_class->map_cflags)) 4414 { 4415 char *ptr; 4416 4417 if ((ptr = strchr(p, ' ')) != NULL) 4418 *ptr = '\0'; 4419 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s", 4420 p, map->map_mname); 4421 if (ptr != NULL) 4422 *ptr = ' '; 4423 return false; 4424 } 4425 } 4426 4427 break; 4428 4429 /* 4430 ** This is a string that is dependent on the 4431 ** method used defined above. 4432 */ 4433 4434 case 'P': /* Secret password for binding */ 4435 while (isascii(*++p) && isspace(*p)) 4436 continue; 4437 lmap->ldap_secret = p; 4438 secretread = false; 4439 break; 4440 4441 # if _FFR_LDAP_URI 4442 case 'H': /* Use LDAP URI */ 4443 # if !USE_LDAP_INIT 4444 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s", 4445 map->map_mname); 4446 return false; 4447 # else /* !USE_LDAP_INIT */ 4448 if (ldaphost) 4449 { 4450 syserr("Can not specify both an LDAP host and an LDAP URI in map %s", 4451 map->map_mname); 4452 return false; 4453 } 4454 while (isascii(*++p) && isspace(*p)) 4455 continue; 4456 lmap->ldap_target = p; 4457 lmap->ldap_uri = true; 4458 break; 4459 # endif /* !USE_LDAP_INIT */ 4460 # endif /* _FFR_LDAP_URI */ 4461 4462 # if _FFR_LDAP_SETVERSION 4463 case 'w': 4464 /* -w should be for passwd, -P should be for version */ 4465 while (isascii(*++p) && isspace(*p)) 4466 continue; 4467 lmap->ldap_version = atoi(p); 4468 # ifdef LDAP_VERSION_MAX 4469 if (lmap->ldap_version > LDAP_VERSION_MAX) 4470 { 4471 syserr("LDAP version %d exceeds max of %d in map %s", 4472 lmap->ldap_version, LDAP_VERSION_MAX, 4473 map->map_mname); 4474 return false; 4475 } 4476 # endif /* LDAP_VERSION_MAX */ 4477 # ifdef LDAP_VERSION_MIN 4478 if (lmap->ldap_version < LDAP_VERSION_MIN) 4479 { 4480 syserr("LDAP version %d is lower than min of %d in map %s", 4481 lmap->ldap_version, LDAP_VERSION_MIN, 4482 map->map_mname); 4483 return false; 4484 } 4485 # endif /* LDAP_VERSION_MIN */ 4486 break; 4487 # endif /* _FFR_LDAP_SETVERSION */ 4488 4489 default: 4490 syserr("Illegal option %c map %s", *p, map->map_mname); 4491 break; 4492 } 4493 4494 /* need to account for quoted strings here */ 4495 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 4496 { 4497 if (*p == '"') 4498 { 4499 while (*++p != '"' && *p != '\0') 4500 continue; 4501 if (*p != '\0') 4502 p++; 4503 } 4504 else 4505 p++; 4506 } 4507 4508 if (*p != '\0') 4509 *p++ = '\0'; 4510 } 4511 4512 if (map->map_app != NULL) 4513 map->map_app = newstr(ldapmap_dequote(map->map_app)); 4514 if (map->map_tapp != NULL) 4515 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp)); 4516 4517 /* 4518 ** We need to swallow up all the stuff into a struct 4519 ** and dump it into map->map_dbptr1 4520 */ 4521 4522 if (lmap->ldap_target != NULL && 4523 (LDAPDefaults == NULL || 4524 LDAPDefaults == lmap || 4525 LDAPDefaults->ldap_target != lmap->ldap_target)) 4526 lmap->ldap_target = newstr(ldapmap_dequote(lmap->ldap_target)); 4527 map->map_domain = lmap->ldap_target; 4528 4529 if (lmap->ldap_binddn != NULL && 4530 (LDAPDefaults == NULL || 4531 LDAPDefaults == lmap || 4532 LDAPDefaults->ldap_binddn != lmap->ldap_binddn)) 4533 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn)); 4534 4535 if (lmap->ldap_secret != NULL && 4536 (LDAPDefaults == NULL || 4537 LDAPDefaults == lmap || 4538 LDAPDefaults->ldap_secret != lmap->ldap_secret)) 4539 { 4540 SM_FILE_T *sfd; 4541 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES; 4542 4543 if (DontLockReadFiles) 4544 sff |= SFF_NOLOCK; 4545 4546 /* need to use method to map secret to passwd string */ 4547 switch (lmap->ldap_method) 4548 { 4549 case LDAP_AUTH_NONE: 4550 /* Do nothing */ 4551 break; 4552 4553 case LDAP_AUTH_SIMPLE: 4554 4555 /* 4556 ** Secret is the name of a file with 4557 ** the first line as the password. 4558 */ 4559 4560 /* Already read in the secret? */ 4561 if (secretread) 4562 break; 4563 4564 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret), 4565 O_RDONLY, 0, sff); 4566 if (sfd == NULL) 4567 { 4568 syserr("LDAP map: cannot open secret %s", 4569 ldapmap_dequote(lmap->ldap_secret)); 4570 return false; 4571 } 4572 lmap->ldap_secret = sfgets(m_tmp, sizeof m_tmp, 4573 sfd, TimeOuts.to_fileopen, 4574 "ldapmap_parseargs"); 4575 (void) sm_io_close(sfd, SM_TIME_DEFAULT); 4576 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD) 4577 { 4578 syserr("LDAP map: secret in %s too long", 4579 ldapmap_dequote(lmap->ldap_secret)); 4580 return false; 4581 } 4582 if (lmap->ldap_secret != NULL && 4583 strlen(m_tmp) > 0) 4584 { 4585 /* chomp newline */ 4586 if (m_tmp[strlen(m_tmp) - 1] == '\n') 4587 m_tmp[strlen(m_tmp) - 1] = '\0'; 4588 4589 lmap->ldap_secret = m_tmp; 4590 } 4591 break; 4592 4593 # ifdef LDAP_AUTH_KRBV4 4594 case LDAP_AUTH_KRBV4: 4595 4596 /* 4597 ** Secret is where the ticket file is 4598 ** stashed 4599 */ 4600 4601 (void) sm_snprintf(m_tmp, sizeof m_tmp, 4602 "KRBTKFILE=%s", 4603 ldapmap_dequote(lmap->ldap_secret)); 4604 lmap->ldap_secret = m_tmp; 4605 break; 4606 # endif /* LDAP_AUTH_KRBV4 */ 4607 4608 default: /* Should NEVER get here */ 4609 syserr("LDAP map: Illegal value in lmap method"); 4610 return false; 4611 /* NOTREACHED */ 4612 break; 4613 } 4614 } 4615 4616 if (lmap->ldap_secret != NULL && 4617 (LDAPDefaults == NULL || 4618 LDAPDefaults == lmap || 4619 LDAPDefaults->ldap_secret != lmap->ldap_secret)) 4620 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret)); 4621 4622 if (lmap->ldap_base != NULL && 4623 (LDAPDefaults == NULL || 4624 LDAPDefaults == lmap || 4625 LDAPDefaults->ldap_base != lmap->ldap_base)) 4626 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base)); 4627 4628 /* 4629 ** Save the server from extra work. If request is for a single 4630 ** match, tell the server to only return enough records to 4631 ** determine if there is a single match or not. This can not 4632 ** be one since the server would only return one and we wouldn't 4633 ** know if there were others available. 4634 */ 4635 4636 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 4637 lmap->ldap_sizelimit = 2; 4638 4639 /* If setting defaults, don't process ldap_filter and ldap_attr */ 4640 if (lmap == LDAPDefaults) 4641 return true; 4642 4643 if (lmap->ldap_filter != NULL) 4644 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter)); 4645 else 4646 { 4647 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 4648 { 4649 syserr("No filter given in map %s", map->map_mname); 4650 return false; 4651 } 4652 } 4653 4654 if (lmap->ldap_attr[0] != NULL) 4655 { 4656 # if _FFR_LDAP_RECURSION 4657 bool recurse = false; 4658 bool normalseen = false; 4659 # endif /* _FFR_LDAP_RECURSION */ 4660 4661 i = 0; 4662 p = ldapmap_dequote(lmap->ldap_attr[0]); 4663 lmap->ldap_attr[0] = NULL; 4664 4665 # if _FFR_LDAP_RECURSION 4666 /* Prime the attr list with the objectClass attribute */ 4667 lmap->ldap_attr[i] = "objectClass"; 4668 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS; 4669 lmap->ldap_attr_needobjclass[i] = NULL; 4670 i++; 4671 # endif /* _FFR_LDAP_RECURSION */ 4672 4673 while (p != NULL) 4674 { 4675 char *v; 4676 4677 while (isascii(*p) && isspace(*p)) 4678 p++; 4679 if (*p == '\0') 4680 break; 4681 v = p; 4682 p = strchr(v, ','); 4683 if (p != NULL) 4684 *p++ = '\0'; 4685 4686 if (i >= LDAPMAP_MAX_ATTR) 4687 { 4688 syserr("Too many return attributes in %s (max %d)", 4689 map->map_mname, LDAPMAP_MAX_ATTR); 4690 return false; 4691 } 4692 if (*v != '\0') 4693 { 4694 # if _FFR_LDAP_RECURSION 4695 int j; 4696 int use; 4697 char *type; 4698 char *needobjclass; 4699 4700 type = strchr(v, ':'); 4701 if (type != NULL) 4702 { 4703 *type++ = '\0'; 4704 needobjclass = strchr(type, ':'); 4705 if (needobjclass != NULL) 4706 *needobjclass++ = '\0'; 4707 } 4708 else 4709 { 4710 needobjclass = NULL; 4711 } 4712 4713 use = i; 4714 4715 /* allow override on "objectClass" type */ 4716 if (sm_strcasecmp(v, "objectClass") == 0 && 4717 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS) 4718 { 4719 use = 0; 4720 } 4721 else 4722 { 4723 /* 4724 ** Don't add something to attribute 4725 ** list twice. 4726 */ 4727 4728 for (j = 1; j < i; j++) 4729 { 4730 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0) 4731 { 4732 syserr("Duplicate attribute (%s) in %s", 4733 v, map->map_mname); 4734 return false; 4735 } 4736 } 4737 4738 lmap->ldap_attr[use] = newstr(v); 4739 if (needobjclass != NULL && 4740 *needobjclass != '\0' && 4741 *needobjclass != '*') 4742 { 4743 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass); 4744 } 4745 else 4746 { 4747 lmap->ldap_attr_needobjclass[use] = NULL; 4748 } 4749 4750 } 4751 4752 if (type != NULL && *type != '\0') 4753 { 4754 if (sm_strcasecmp(type, "dn") == 0) 4755 { 4756 recurse = true; 4757 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN; 4758 } 4759 else if (sm_strcasecmp(type, "filter") == 0) 4760 { 4761 recurse = true; 4762 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER; 4763 } 4764 else if (sm_strcasecmp(type, "url") == 0) 4765 { 4766 recurse = true; 4767 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL; 4768 } 4769 else if (sm_strcasecmp(type, "normal") == 0) 4770 { 4771 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL; 4772 normalseen = true; 4773 } 4774 else 4775 { 4776 syserr("Unknown attribute type (%s) in %s", 4777 type, map->map_mname); 4778 return false; 4779 } 4780 } 4781 else 4782 { 4783 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL; 4784 normalseen = true; 4785 } 4786 # else /* _FFR_LDAP_RECURSION */ 4787 lmap->ldap_attr[i] = newstr(v); 4788 # endif /* _FFR_LDAP_RECURSION */ 4789 i++; 4790 } 4791 } 4792 lmap->ldap_attr[i] = NULL; 4793 # if _FFR_LDAP_RECURSION 4794 if (recurse && !normalseen) 4795 { 4796 syserr("LDAP recursion requested in %s but no returnable attribute given", 4797 map->map_mname); 4798 return false; 4799 } 4800 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE) 4801 { 4802 syserr("LDAP recursion requested in %s can not be used with -n", 4803 map->map_mname); 4804 return false; 4805 } 4806 # endif /* _FFR_LDAP_RECURSION */ 4807 } 4808 map->map_db1 = (ARBPTR_T) lmap; 4809 return true; 4810 } 4811 4812 /* 4813 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf 4814 ** 4815 ** Parameters: 4816 ** spec -- map argument string from LDAPDefaults option 4817 ** 4818 ** Returns: 4819 ** None. 4820 */ 4821 4822 void 4823 ldapmap_set_defaults(spec) 4824 char *spec; 4825 { 4826 STAB *class; 4827 MAP map; 4828 4829 /* Allocate and set the default values */ 4830 if (LDAPDefaults == NULL) 4831 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof *LDAPDefaults); 4832 sm_ldap_clear(LDAPDefaults); 4833 4834 memset(&map, '\0', sizeof map); 4835 4836 /* look up the class */ 4837 class = stab("ldap", ST_MAPCLASS, ST_FIND); 4838 if (class == NULL) 4839 { 4840 syserr("readcf: LDAPDefaultSpec: class ldap not available"); 4841 return; 4842 } 4843 map.map_class = &class->s_mapclass; 4844 map.map_db1 = (ARBPTR_T) LDAPDefaults; 4845 map.map_mname = "O LDAPDefaultSpec"; 4846 4847 (void) ldapmap_parseargs(&map, spec); 4848 4849 /* These should never be set in LDAPDefaults */ 4850 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) || 4851 map.map_spacesub != SpaceSub || 4852 map.map_app != NULL || 4853 map.map_tapp != NULL) 4854 { 4855 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags"); 4856 SM_FREE_CLR(map.map_app); 4857 SM_FREE_CLR(map.map_tapp); 4858 } 4859 4860 if (LDAPDefaults->ldap_filter != NULL) 4861 { 4862 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter"); 4863 4864 /* don't free, it isn't malloc'ed in parseargs */ 4865 LDAPDefaults->ldap_filter = NULL; 4866 } 4867 4868 if (LDAPDefaults->ldap_attr[0] != NULL) 4869 { 4870 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes"); 4871 /* don't free, they aren't malloc'ed in parseargs */ 4872 LDAPDefaults->ldap_attr[0] = NULL; 4873 } 4874 } 4875 #endif /* LDAPMAP */ 4876 /* 4877 ** PH map 4878 */ 4879 4880 #if PH_MAP 4881 4882 /* 4883 ** Support for the CCSO Nameserver (ph/qi). 4884 ** This code is intended to replace the so-called "ph mailer". 4885 ** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support. 4886 */ 4887 4888 /* what version of the ph map code we're running */ 4889 static char phmap_id[PH_BUF_SIZE]; 4890 4891 /* sendmail version for phmap id string */ 4892 extern const char Version[]; 4893 4894 /* 4895 ** PH_MAP_PARSEARGS -- parse ph map definition args. 4896 */ 4897 4898 bool 4899 ph_map_parseargs(map, args) 4900 MAP *map; 4901 char *args; 4902 { 4903 register bool done; 4904 register char *p = args; 4905 PH_MAP_STRUCT *pmap = NULL; 4906 4907 /* initialize version string */ 4908 (void) sm_snprintf(phmap_id, sizeof phmap_id, 4909 "sendmail-%s phmap-20010529 libphclient-%s", 4910 Version, libphclient_version); 4911 4912 pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap); 4913 4914 /* defaults */ 4915 pmap->ph_servers = NULL; 4916 pmap->ph_field_list = NULL; 4917 pmap->ph = NULL; 4918 pmap->ph_timeout = 0; 4919 pmap->ph_fastclose = 0; 4920 4921 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 4922 for (;;) 4923 { 4924 while (isascii(*p) && isspace(*p)) 4925 p++; 4926 if (*p != '-') 4927 break; 4928 switch (*++p) 4929 { 4930 case 'N': 4931 map->map_mflags |= MF_INCLNULL; 4932 map->map_mflags &= ~MF_TRY0NULL; 4933 break; 4934 4935 case 'O': 4936 map->map_mflags &= ~MF_TRY1NULL; 4937 break; 4938 4939 case 'o': 4940 map->map_mflags |= MF_OPTIONAL; 4941 break; 4942 4943 case 'f': 4944 map->map_mflags |= MF_NOFOLDCASE; 4945 break; 4946 4947 case 'm': 4948 map->map_mflags |= MF_MATCHONLY; 4949 break; 4950 4951 case 'A': 4952 map->map_mflags |= MF_APPEND; 4953 break; 4954 4955 case 'q': 4956 map->map_mflags |= MF_KEEPQUOTES; 4957 break; 4958 4959 case 't': 4960 map->map_mflags |= MF_NODEFER; 4961 break; 4962 4963 case 'a': 4964 map->map_app = ++p; 4965 break; 4966 4967 case 'T': 4968 map->map_tapp = ++p; 4969 break; 4970 4971 case 'l': 4972 while (isascii(*++p) && isspace(*p)) 4973 continue; 4974 pmap->ph_timeout = atoi(p); 4975 break; 4976 4977 case 'S': 4978 map->map_spacesub = *++p; 4979 break; 4980 4981 case 'D': 4982 map->map_mflags |= MF_DEFER; 4983 break; 4984 4985 case 'h': /* PH server list */ 4986 while (isascii(*++p) && isspace(*p)) 4987 continue; 4988 pmap->ph_servers = p; 4989 break; 4990 4991 case 'v': 4992 sm_syslog(LOG_WARNING, NULL, 4993 "ph_map_parseargs: WARNING: -v option will be removed in a future release - please use -k instead"); 4994 /* intentional fallthrough for backward compatibility */ 4995 /* FALLTHROUGH */ 4996 4997 case 'k': /* fields to search for */ 4998 while (isascii(*++p) && isspace(*p)) 4999 continue; 5000 pmap->ph_field_list = p; 5001 break; 5002 5003 default: 5004 syserr("ph_map_parseargs: unknown option -%c", *p); 5005 } 5006 5007 /* try to account for quoted strings */ 5008 done = isascii(*p) && isspace(*p); 5009 while (*p != '\0' && !done) 5010 { 5011 if (*p == '"') 5012 { 5013 while (*++p != '"' && *p != '\0') 5014 continue; 5015 if (*p != '\0') 5016 p++; 5017 } 5018 else 5019 p++; 5020 done = isascii(*p) && isspace(*p); 5021 } 5022 5023 if (*p != '\0') 5024 *p++ = '\0'; 5025 } 5026 5027 if (map->map_app != NULL) 5028 map->map_app = newstr(ph_map_dequote(map->map_app)); 5029 if (map->map_tapp != NULL) 5030 map->map_tapp = newstr(ph_map_dequote(map->map_tapp)); 5031 5032 if (pmap->ph_field_list != NULL) 5033 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list)); 5034 5035 if (pmap->ph_servers != NULL) 5036 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers)); 5037 else 5038 { 5039 syserr("ph_map_parseargs: -h flag is required"); 5040 return false; 5041 } 5042 5043 map->map_db1 = (ARBPTR_T) pmap; 5044 return true; 5045 } 5046 5047 /* 5048 ** PH_MAP_CLOSE -- close the connection to the ph server 5049 */ 5050 5051 void 5052 ph_map_close(map) 5053 MAP *map; 5054 { 5055 PH_MAP_STRUCT *pmap; 5056 5057 pmap = (PH_MAP_STRUCT *)map->map_db1; 5058 if (tTd(38, 9)) 5059 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n", 5060 map->map_mname, pmap->ph_fastclose); 5061 5062 5063 if (pmap->ph != NULL) 5064 { 5065 ph_set_sendhook(pmap->ph, NULL); 5066 ph_set_recvhook(pmap->ph, NULL); 5067 ph_close(pmap->ph, pmap->ph_fastclose); 5068 } 5069 5070 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 5071 } 5072 5073 static jmp_buf PHTimeout; 5074 5075 /* ARGSUSED */ 5076 static void 5077 ph_timeout(unused) 5078 int unused; 5079 { 5080 /* 5081 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 5082 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 5083 ** DOING. 5084 */ 5085 5086 errno = ETIMEDOUT; 5087 longjmp(PHTimeout, 1); 5088 } 5089 5090 static void 5091 ph_map_send_debug(text) 5092 char *text; 5093 { 5094 if (LogLevel > 9) 5095 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5096 "ph_map_send_debug: ==> %s", text); 5097 if (tTd(38, 20)) 5098 sm_dprintf("ph_map_send_debug: ==> %s\n", text); 5099 } 5100 5101 static void 5102 ph_map_recv_debug(text) 5103 char *text; 5104 { 5105 if (LogLevel > 10) 5106 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5107 "ph_map_recv_debug: <== %s", text); 5108 if (tTd(38, 21)) 5109 sm_dprintf("ph_map_recv_debug: <== %s\n", text); 5110 } 5111 5112 /* 5113 ** PH_MAP_OPEN -- sub for opening PH map 5114 */ 5115 bool 5116 ph_map_open(map, mode) 5117 MAP *map; 5118 int mode; 5119 { 5120 PH_MAP_STRUCT *pmap; 5121 register SM_EVENT *ev = NULL; 5122 int save_errno = 0; 5123 char *hostlist, *host; 5124 5125 if (tTd(38, 2)) 5126 sm_dprintf("ph_map_open(%s)\n", map->map_mname); 5127 5128 mode &= O_ACCMODE; 5129 if (mode != O_RDONLY) 5130 { 5131 /* issue a pseudo-error message */ 5132 errno = SM_EMAPCANTWRITE; 5133 return false; 5134 } 5135 5136 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER && 5137 bitset(MF_DEFER, map->map_mflags)) 5138 { 5139 if (tTd(9, 1)) 5140 sm_dprintf("ph_map_open(%s) => DEFERRED\n", 5141 map->map_mname); 5142 5143 /* 5144 ** Unset MF_DEFER here so that map_lookup() returns 5145 ** a temporary failure using the bogus map and 5146 ** map->map_tapp instead of the default permanent error. 5147 */ 5148 5149 map->map_mflags &= ~MF_DEFER; 5150 return false; 5151 } 5152 5153 pmap = (PH_MAP_STRUCT *)map->map_db1; 5154 pmap->ph_fastclose = 0; /* refresh field for reopen */ 5155 5156 /* try each host in the list */ 5157 hostlist = newstr(pmap->ph_servers); 5158 for (host = strtok(hostlist, " "); 5159 host != NULL; 5160 host = strtok(NULL, " ")) 5161 { 5162 /* set timeout */ 5163 if (pmap->ph_timeout != 0) 5164 { 5165 if (setjmp(PHTimeout) != 0) 5166 { 5167 ev = NULL; 5168 if (LogLevel > 1) 5169 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5170 "timeout connecting to PH server %.100s", 5171 host); 5172 errno = ETIMEDOUT; 5173 goto ph_map_open_abort; 5174 } 5175 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0); 5176 } 5177 5178 /* open connection to server */ 5179 if (!ph_open(&(pmap->ph), host, PH_ROUNDROBIN|PH_DONTID, 5180 ph_map_send_debug, ph_map_recv_debug) && 5181 !ph_id(pmap->ph, phmap_id)) 5182 { 5183 if (ev != NULL) 5184 sm_clrevent(ev); 5185 sm_free(hostlist); /* XXX */ 5186 return true; 5187 } 5188 5189 ph_map_open_abort: 5190 save_errno = errno; 5191 if (ev != NULL) 5192 sm_clrevent(ev); 5193 pmap->ph_fastclose = PH_FASTCLOSE; 5194 ph_map_close(map); 5195 errno = save_errno; 5196 } 5197 5198 if (bitset(MF_NODEFER, map->map_mflags)) 5199 { 5200 if (errno == 0) 5201 errno = EAGAIN; 5202 syserr("ph_map_open: %s: cannot connect to PH server", 5203 map->map_mname); 5204 } 5205 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1) 5206 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5207 "ph_map_open: %s: cannot connect to PH server", 5208 map->map_mname); 5209 sm_free(hostlist); /* XXX */ 5210 return false; 5211 } 5212 5213 /* 5214 ** PH_MAP_LOOKUP -- look up key from ph server 5215 */ 5216 5217 char * 5218 ph_map_lookup(map, key, args, pstat) 5219 MAP *map; 5220 char *key; 5221 char **args; 5222 int *pstat; 5223 { 5224 int i, save_errno = 0; 5225 register SM_EVENT *ev = NULL; 5226 PH_MAP_STRUCT *pmap; 5227 char *value = NULL; 5228 5229 pmap = (PH_MAP_STRUCT *)map->map_db1; 5230 5231 *pstat = EX_OK; 5232 5233 /* set timeout */ 5234 if (pmap->ph_timeout != 0) 5235 { 5236 if (setjmp(PHTimeout) != 0) 5237 { 5238 ev = NULL; 5239 if (LogLevel > 1) 5240 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5241 "timeout during PH lookup of %.100s", 5242 key); 5243 errno = ETIMEDOUT; 5244 *pstat = EX_TEMPFAIL; 5245 goto ph_map_lookup_abort; 5246 } 5247 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0); 5248 } 5249 5250 /* perform lookup */ 5251 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value); 5252 if (i == -1) 5253 *pstat = EX_TEMPFAIL; 5254 else if (i == PH_NOMATCH || i == PH_DATAERR) 5255 *pstat = EX_UNAVAILABLE; 5256 5257 ph_map_lookup_abort: 5258 if (ev != NULL) 5259 sm_clrevent(ev); 5260 5261 /* 5262 ** Close the connection if the timer popped 5263 ** or we got a temporary PH error 5264 */ 5265 5266 if (*pstat == EX_TEMPFAIL) 5267 { 5268 save_errno = errno; 5269 pmap->ph_fastclose = PH_FASTCLOSE; 5270 ph_map_close(map); 5271 errno = save_errno; 5272 } 5273 5274 if (*pstat == EX_OK) 5275 { 5276 if (tTd(38,20)) 5277 sm_dprintf("ph_map_lookup: %s => %s\n", key, value); 5278 5279 if (bitset(MF_MATCHONLY, map->map_mflags)) 5280 return map_rewrite(map, key, strlen(key), NULL); 5281 else 5282 return map_rewrite(map, value, strlen(value), args); 5283 } 5284 5285 return NULL; 5286 } 5287 #endif /* PH_MAP */ 5288 /* 5289 ** syslog map 5290 */ 5291 5292 #define map_prio map_lockfd /* overload field */ 5293 5294 /* 5295 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages. 5296 */ 5297 5298 bool 5299 syslog_map_parseargs(map, args) 5300 MAP *map; 5301 char *args; 5302 { 5303 char *p = args; 5304 char *priority = NULL; 5305 5306 /* there is no check whether there is really an argument */ 5307 while (*p != '\0') 5308 { 5309 while (isascii(*p) && isspace(*p)) 5310 p++; 5311 if (*p != '-') 5312 break; 5313 ++p; 5314 if (*p == 'D') 5315 { 5316 map->map_mflags |= MF_DEFER; 5317 ++p; 5318 } 5319 else if (*p == 'S') 5320 { 5321 map->map_spacesub = *++p; 5322 if (*p != '\0') 5323 p++; 5324 } 5325 else if (*p == 'L') 5326 { 5327 while (*++p != '\0' && isascii(*p) && isspace(*p)) 5328 continue; 5329 if (*p == '\0') 5330 break; 5331 priority = p; 5332 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 5333 p++; 5334 if (*p != '\0') 5335 *p++ = '\0'; 5336 } 5337 else 5338 { 5339 syserr("Illegal option %c map syslog", *p); 5340 ++p; 5341 } 5342 } 5343 5344 if (priority == NULL) 5345 map->map_prio = LOG_INFO; 5346 else 5347 { 5348 if (sm_strncasecmp("LOG_", priority, 4) == 0) 5349 priority += 4; 5350 5351 #ifdef LOG_EMERG 5352 if (sm_strcasecmp("EMERG", priority) == 0) 5353 map->map_prio = LOG_EMERG; 5354 else 5355 #endif /* LOG_EMERG */ 5356 #ifdef LOG_ALERT 5357 if (sm_strcasecmp("ALERT", priority) == 0) 5358 map->map_prio = LOG_ALERT; 5359 else 5360 #endif /* LOG_ALERT */ 5361 #ifdef LOG_CRIT 5362 if (sm_strcasecmp("CRIT", priority) == 0) 5363 map->map_prio = LOG_CRIT; 5364 else 5365 #endif /* LOG_CRIT */ 5366 #ifdef LOG_ERR 5367 if (sm_strcasecmp("ERR", priority) == 0) 5368 map->map_prio = LOG_ERR; 5369 else 5370 #endif /* LOG_ERR */ 5371 #ifdef LOG_WARNING 5372 if (sm_strcasecmp("WARNING", priority) == 0) 5373 map->map_prio = LOG_WARNING; 5374 else 5375 #endif /* LOG_WARNING */ 5376 #ifdef LOG_NOTICE 5377 if (sm_strcasecmp("NOTICE", priority) == 0) 5378 map->map_prio = LOG_NOTICE; 5379 else 5380 #endif /* LOG_NOTICE */ 5381 #ifdef LOG_INFO 5382 if (sm_strcasecmp("INFO", priority) == 0) 5383 map->map_prio = LOG_INFO; 5384 else 5385 #endif /* LOG_INFO */ 5386 #ifdef LOG_DEBUG 5387 if (sm_strcasecmp("DEBUG", priority) == 0) 5388 map->map_prio = LOG_DEBUG; 5389 else 5390 #endif /* LOG_DEBUG */ 5391 { 5392 syserr("syslog_map_parseargs: Unknown priority %s", 5393 priority); 5394 return false; 5395 } 5396 } 5397 return true; 5398 } 5399 5400 /* 5401 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string 5402 */ 5403 5404 char * 5405 syslog_map_lookup(map, string, args, statp) 5406 MAP *map; 5407 char *string; 5408 char **args; 5409 int *statp; 5410 { 5411 char *ptr = map_rewrite(map, string, strlen(string), args); 5412 5413 if (ptr != NULL) 5414 { 5415 if (tTd(38, 20)) 5416 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n", 5417 map->map_mname, map->map_prio, ptr); 5418 5419 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr); 5420 } 5421 5422 *statp = EX_OK; 5423 return ""; 5424 } 5425 5426 /* 5427 ** HESIOD Modules 5428 */ 5429 5430 #if HESIOD 5431 5432 bool 5433 hes_map_open(map, mode) 5434 MAP *map; 5435 int mode; 5436 { 5437 if (tTd(38, 2)) 5438 sm_dprintf("hes_map_open(%s, %s, %d)\n", 5439 map->map_mname, map->map_file, mode); 5440 5441 if (mode != O_RDONLY) 5442 { 5443 /* issue a pseudo-error message */ 5444 errno = SM_EMAPCANTWRITE; 5445 return false; 5446 } 5447 5448 # ifdef HESIOD_INIT 5449 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0) 5450 return true; 5451 5452 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5453 syserr("451 4.3.5 cannot initialize Hesiod map (%s)", 5454 sm_errstring(errno)); 5455 return false; 5456 # else /* HESIOD_INIT */ 5457 if (hes_error() == HES_ER_UNINIT) 5458 hes_init(); 5459 switch (hes_error()) 5460 { 5461 case HES_ER_OK: 5462 case HES_ER_NOTFOUND: 5463 return true; 5464 } 5465 5466 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5467 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error()); 5468 5469 return false; 5470 # endif /* HESIOD_INIT */ 5471 } 5472 5473 char * 5474 hes_map_lookup(map, name, av, statp) 5475 MAP *map; 5476 char *name; 5477 char **av; 5478 int *statp; 5479 { 5480 char **hp; 5481 5482 if (tTd(38, 20)) 5483 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name); 5484 5485 if (name[0] == '\\') 5486 { 5487 char *np; 5488 int nl; 5489 int save_errno; 5490 char nbuf[MAXNAME]; 5491 5492 nl = strlen(name); 5493 if (nl < sizeof nbuf - 1) 5494 np = nbuf; 5495 else 5496 np = xalloc(strlen(name) + 2); 5497 np[0] = '\\'; 5498 (void) sm_strlcpy(&np[1], name, (sizeof nbuf) - 1); 5499 # ifdef HESIOD_INIT 5500 hp = hesiod_resolve(HesiodContext, np, map->map_file); 5501 # else /* HESIOD_INIT */ 5502 hp = hes_resolve(np, map->map_file); 5503 # endif /* HESIOD_INIT */ 5504 save_errno = errno; 5505 if (np != nbuf) 5506 sm_free(np); /* XXX */ 5507 errno = save_errno; 5508 } 5509 else 5510 { 5511 # ifdef HESIOD_INIT 5512 hp = hesiod_resolve(HesiodContext, name, map->map_file); 5513 # else /* HESIOD_INIT */ 5514 hp = hes_resolve(name, map->map_file); 5515 # endif /* HESIOD_INIT */ 5516 } 5517 # ifdef HESIOD_INIT 5518 if (hp == NULL || *hp == NULL) 5519 { 5520 switch (errno) 5521 { 5522 case ENOENT: 5523 *statp = EX_NOTFOUND; 5524 break; 5525 case ECONNREFUSED: 5526 *statp = EX_TEMPFAIL; 5527 break; 5528 case EMSGSIZE: 5529 case ENOMEM: 5530 default: 5531 *statp = EX_UNAVAILABLE; 5532 break; 5533 } 5534 if (hp != NULL) 5535 hesiod_free_list(HesiodContext, hp); 5536 return NULL; 5537 } 5538 # else /* HESIOD_INIT */ 5539 if (hp == NULL || hp[0] == NULL) 5540 { 5541 switch (hes_error()) 5542 { 5543 case HES_ER_OK: 5544 *statp = EX_OK; 5545 break; 5546 5547 case HES_ER_NOTFOUND: 5548 *statp = EX_NOTFOUND; 5549 break; 5550 5551 case HES_ER_CONFIG: 5552 *statp = EX_UNAVAILABLE; 5553 break; 5554 5555 case HES_ER_NET: 5556 *statp = EX_TEMPFAIL; 5557 break; 5558 } 5559 return NULL; 5560 } 5561 # endif /* HESIOD_INIT */ 5562 5563 if (bitset(MF_MATCHONLY, map->map_mflags)) 5564 return map_rewrite(map, name, strlen(name), NULL); 5565 else 5566 return map_rewrite(map, hp[0], strlen(hp[0]), av); 5567 } 5568 5569 /* 5570 ** HES_MAP_CLOSE -- free the Hesiod context 5571 */ 5572 5573 void 5574 hes_map_close(map) 5575 MAP *map; 5576 { 5577 if (tTd(38, 20)) 5578 sm_dprintf("hes_map_close(%s)\n", map->map_file); 5579 5580 # ifdef HESIOD_INIT 5581 /* Free the hesiod context */ 5582 if (HesiodContext != NULL) 5583 { 5584 hesiod_end(HesiodContext); 5585 HesiodContext = NULL; 5586 } 5587 # endif /* HESIOD_INIT */ 5588 } 5589 5590 #endif /* HESIOD */ 5591 /* 5592 ** NeXT NETINFO Modules 5593 */ 5594 5595 #if NETINFO 5596 5597 # define NETINFO_DEFAULT_DIR "/aliases" 5598 # define NETINFO_DEFAULT_PROPERTY "members" 5599 5600 /* 5601 ** NI_MAP_OPEN -- open NetInfo Aliases 5602 */ 5603 5604 bool 5605 ni_map_open(map, mode) 5606 MAP *map; 5607 int mode; 5608 { 5609 if (tTd(38, 2)) 5610 sm_dprintf("ni_map_open(%s, %s, %d)\n", 5611 map->map_mname, map->map_file, mode); 5612 mode &= O_ACCMODE; 5613 5614 if (*map->map_file == '\0') 5615 map->map_file = NETINFO_DEFAULT_DIR; 5616 5617 if (map->map_valcolnm == NULL) 5618 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY; 5619 5620 if (map->map_coldelim == '\0') 5621 { 5622 if (bitset(MF_ALIAS, map->map_mflags)) 5623 map->map_coldelim = ','; 5624 else if (bitset(MF_FILECLASS, map->map_mflags)) 5625 map->map_coldelim = ' '; 5626 } 5627 return true; 5628 } 5629 5630 5631 /* 5632 ** NI_MAP_LOOKUP -- look up a datum in NetInfo 5633 */ 5634 5635 char * 5636 ni_map_lookup(map, name, av, statp) 5637 MAP *map; 5638 char *name; 5639 char **av; 5640 int *statp; 5641 { 5642 char *res; 5643 char *propval; 5644 5645 if (tTd(38, 20)) 5646 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name); 5647 5648 propval = ni_propval(map->map_file, map->map_keycolnm, name, 5649 map->map_valcolnm, map->map_coldelim); 5650 5651 if (propval == NULL) 5652 return NULL; 5653 5654 SM_TRY 5655 if (bitset(MF_MATCHONLY, map->map_mflags)) 5656 res = map_rewrite(map, name, strlen(name), NULL); 5657 else 5658 res = map_rewrite(map, propval, strlen(propval), av); 5659 SM_FINALLY 5660 sm_free(propval); 5661 SM_END_TRY 5662 return res; 5663 } 5664 5665 5666 static bool 5667 ni_getcanonname(name, hbsize, statp) 5668 char *name; 5669 int hbsize; 5670 int *statp; 5671 { 5672 char *vptr; 5673 char *ptr; 5674 char nbuf[MAXNAME + 1]; 5675 5676 if (tTd(38, 20)) 5677 sm_dprintf("ni_getcanonname(%s)\n", name); 5678 5679 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) 5680 { 5681 *statp = EX_UNAVAILABLE; 5682 return false; 5683 } 5684 (void) shorten_hostname(nbuf); 5685 5686 /* we only accept single token search key */ 5687 if (strchr(nbuf, '.')) 5688 { 5689 *statp = EX_NOHOST; 5690 return false; 5691 } 5692 5693 /* Do the search */ 5694 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n'); 5695 5696 if (vptr == NULL) 5697 { 5698 *statp = EX_NOHOST; 5699 return false; 5700 } 5701 5702 /* Only want the first machine name */ 5703 if ((ptr = strchr(vptr, '\n')) != NULL) 5704 *ptr = '\0'; 5705 5706 if (sm_strlcpy(name, vptr, hbsize) >= hbsize) 5707 { 5708 sm_free(vptr); 5709 *statp = EX_UNAVAILABLE; 5710 return true; 5711 } 5712 sm_free(vptr); 5713 *statp = EX_OK; 5714 return false; 5715 } 5716 #endif /* NETINFO */ 5717 /* 5718 ** TEXT (unindexed text file) Modules 5719 ** 5720 ** This code donated by Sun Microsystems. 5721 */ 5722 5723 #define map_sff map_lockfd /* overload field */ 5724 5725 5726 /* 5727 ** TEXT_MAP_OPEN -- open text table 5728 */ 5729 5730 bool 5731 text_map_open(map, mode) 5732 MAP *map; 5733 int mode; 5734 { 5735 long sff; 5736 int i; 5737 5738 if (tTd(38, 2)) 5739 sm_dprintf("text_map_open(%s, %s, %d)\n", 5740 map->map_mname, map->map_file, mode); 5741 5742 mode &= O_ACCMODE; 5743 if (mode != O_RDONLY) 5744 { 5745 errno = EPERM; 5746 return false; 5747 } 5748 5749 if (*map->map_file == '\0') 5750 { 5751 syserr("text map \"%s\": file name required", 5752 map->map_mname); 5753 return false; 5754 } 5755 5756 if (map->map_file[0] != '/') 5757 { 5758 syserr("text map \"%s\": file name must be fully qualified", 5759 map->map_mname); 5760 return false; 5761 } 5762 5763 sff = SFF_ROOTOK|SFF_REGONLY; 5764 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 5765 sff |= SFF_NOWLINK; 5766 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 5767 sff |= SFF_SAFEDIRPATH; 5768 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName, 5769 sff, S_IRUSR, NULL)) != 0) 5770 { 5771 int save_errno = errno; 5772 5773 /* cannot open this map */ 5774 if (tTd(38, 2)) 5775 sm_dprintf("\tunsafe map file: %d\n", i); 5776 errno = save_errno; 5777 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5778 syserr("text map \"%s\": unsafe map file %s", 5779 map->map_mname, map->map_file); 5780 return false; 5781 } 5782 5783 if (map->map_keycolnm == NULL) 5784 map->map_keycolno = 0; 5785 else 5786 { 5787 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm))) 5788 { 5789 syserr("text map \"%s\", file %s: -k should specify a number, not %s", 5790 map->map_mname, map->map_file, 5791 map->map_keycolnm); 5792 return false; 5793 } 5794 map->map_keycolno = atoi(map->map_keycolnm); 5795 } 5796 5797 if (map->map_valcolnm == NULL) 5798 map->map_valcolno = 0; 5799 else 5800 { 5801 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm))) 5802 { 5803 syserr("text map \"%s\", file %s: -v should specify a number, not %s", 5804 map->map_mname, map->map_file, 5805 map->map_valcolnm); 5806 return false; 5807 } 5808 map->map_valcolno = atoi(map->map_valcolnm); 5809 } 5810 5811 if (tTd(38, 2)) 5812 { 5813 sm_dprintf("text_map_open(%s, %s): delimiter = ", 5814 map->map_mname, map->map_file); 5815 if (map->map_coldelim == '\0') 5816 sm_dprintf("(white space)\n"); 5817 else 5818 sm_dprintf("%c\n", map->map_coldelim); 5819 } 5820 5821 map->map_sff = sff; 5822 return true; 5823 } 5824 5825 5826 /* 5827 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table 5828 */ 5829 5830 char * 5831 text_map_lookup(map, name, av, statp) 5832 MAP *map; 5833 char *name; 5834 char **av; 5835 int *statp; 5836 { 5837 char *vp; 5838 auto int vsize; 5839 int buflen; 5840 SM_FILE_T *f; 5841 char delim; 5842 int key_idx; 5843 bool found_it; 5844 long sff = map->map_sff; 5845 char search_key[MAXNAME + 1]; 5846 char linebuf[MAXLINE]; 5847 char buf[MAXNAME + 1]; 5848 5849 found_it = false; 5850 if (tTd(38, 20)) 5851 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name); 5852 5853 buflen = strlen(name); 5854 if (buflen > sizeof search_key - 1) 5855 buflen = sizeof search_key - 1; /* XXX just cut if off? */ 5856 memmove(search_key, name, buflen); 5857 search_key[buflen] = '\0'; 5858 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 5859 makelower(search_key); 5860 5861 f = safefopen(map->map_file, O_RDONLY, FileMode, sff); 5862 if (f == NULL) 5863 { 5864 map->map_mflags &= ~(MF_VALID|MF_OPEN); 5865 *statp = EX_UNAVAILABLE; 5866 return NULL; 5867 } 5868 key_idx = map->map_keycolno; 5869 delim = map->map_coldelim; 5870 while (sm_io_fgets(f, SM_TIME_DEFAULT, 5871 linebuf, sizeof linebuf) != NULL) 5872 { 5873 char *p; 5874 5875 /* skip comment line */ 5876 if (linebuf[0] == '#') 5877 continue; 5878 p = strchr(linebuf, '\n'); 5879 if (p != NULL) 5880 *p = '\0'; 5881 p = get_column(linebuf, key_idx, delim, buf, sizeof buf); 5882 if (p != NULL && sm_strcasecmp(search_key, p) == 0) 5883 { 5884 found_it = true; 5885 break; 5886 } 5887 } 5888 (void) sm_io_close(f, SM_TIME_DEFAULT); 5889 if (!found_it) 5890 { 5891 *statp = EX_NOTFOUND; 5892 return NULL; 5893 } 5894 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf); 5895 if (vp == NULL) 5896 { 5897 *statp = EX_NOTFOUND; 5898 return NULL; 5899 } 5900 vsize = strlen(vp); 5901 *statp = EX_OK; 5902 if (bitset(MF_MATCHONLY, map->map_mflags)) 5903 return map_rewrite(map, name, strlen(name), NULL); 5904 else 5905 return map_rewrite(map, vp, vsize, av); 5906 } 5907 5908 /* 5909 ** TEXT_GETCANONNAME -- look up canonical name in hosts file 5910 */ 5911 5912 static bool 5913 text_getcanonname(name, hbsize, statp) 5914 char *name; 5915 int hbsize; 5916 int *statp; 5917 { 5918 bool found; 5919 char *dot; 5920 SM_FILE_T *f; 5921 char linebuf[MAXLINE]; 5922 char cbuf[MAXNAME + 1]; 5923 char nbuf[MAXNAME + 1]; 5924 5925 if (tTd(38, 20)) 5926 sm_dprintf("text_getcanonname(%s)\n", name); 5927 5928 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) 5929 { 5930 *statp = EX_UNAVAILABLE; 5931 return false; 5932 } 5933 dot = shorten_hostname(nbuf); 5934 5935 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY, 5936 NULL); 5937 if (f == NULL) 5938 { 5939 *statp = EX_UNAVAILABLE; 5940 return false; 5941 } 5942 found = false; 5943 while (!found && 5944 sm_io_fgets(f, SM_TIME_DEFAULT, 5945 linebuf, sizeof linebuf) != NULL) 5946 { 5947 char *p = strpbrk(linebuf, "#\n"); 5948 5949 if (p != NULL) 5950 *p = '\0'; 5951 if (linebuf[0] != '\0') 5952 found = extract_canonname(nbuf, dot, linebuf, 5953 cbuf, sizeof cbuf); 5954 } 5955 (void) sm_io_close(f, SM_TIME_DEFAULT); 5956 if (!found) 5957 { 5958 *statp = EX_NOHOST; 5959 return false; 5960 } 5961 5962 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize) 5963 { 5964 *statp = EX_UNAVAILABLE; 5965 return false; 5966 } 5967 *statp = EX_OK; 5968 return true; 5969 } 5970 /* 5971 ** STAB (Symbol Table) Modules 5972 */ 5973 5974 5975 /* 5976 ** STAB_MAP_LOOKUP -- look up alias in symbol table 5977 */ 5978 5979 /* ARGSUSED2 */ 5980 char * 5981 stab_map_lookup(map, name, av, pstat) 5982 register MAP *map; 5983 char *name; 5984 char **av; 5985 int *pstat; 5986 { 5987 register STAB *s; 5988 5989 if (tTd(38, 20)) 5990 sm_dprintf("stab_lookup(%s, %s)\n", 5991 map->map_mname, name); 5992 5993 s = stab(name, ST_ALIAS, ST_FIND); 5994 if (s != NULL) 5995 return s->s_alias; 5996 return NULL; 5997 } 5998 5999 6000 /* 6001 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild) 6002 */ 6003 6004 void 6005 stab_map_store(map, lhs, rhs) 6006 register MAP *map; 6007 char *lhs; 6008 char *rhs; 6009 { 6010 register STAB *s; 6011 6012 s = stab(lhs, ST_ALIAS, ST_ENTER); 6013 s->s_alias = newstr(rhs); 6014 } 6015 6016 6017 /* 6018 ** STAB_MAP_OPEN -- initialize (reads data file) 6019 ** 6020 ** This is a wierd case -- it is only intended as a fallback for 6021 ** aliases. For this reason, opens for write (only during a 6022 ** "newaliases") always fails, and opens for read open the 6023 ** actual underlying text file instead of the database. 6024 */ 6025 6026 bool 6027 stab_map_open(map, mode) 6028 register MAP *map; 6029 int mode; 6030 { 6031 SM_FILE_T *af; 6032 long sff; 6033 struct stat st; 6034 6035 if (tTd(38, 2)) 6036 sm_dprintf("stab_map_open(%s, %s, %d)\n", 6037 map->map_mname, map->map_file, mode); 6038 6039 mode &= O_ACCMODE; 6040 if (mode != O_RDONLY) 6041 { 6042 errno = EPERM; 6043 return false; 6044 } 6045 6046 sff = SFF_ROOTOK|SFF_REGONLY; 6047 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 6048 sff |= SFF_NOWLINK; 6049 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 6050 sff |= SFF_SAFEDIRPATH; 6051 af = safefopen(map->map_file, O_RDONLY, 0444, sff); 6052 if (af == NULL) 6053 return false; 6054 readaliases(map, af, false, false); 6055 6056 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0) 6057 map->map_mtime = st.st_mtime; 6058 (void) sm_io_close(af, SM_TIME_DEFAULT); 6059 6060 return true; 6061 } 6062 /* 6063 ** Implicit Modules 6064 ** 6065 ** Tries several types. For back compatibility of aliases. 6066 */ 6067 6068 6069 /* 6070 ** IMPL_MAP_LOOKUP -- lookup in best open database 6071 */ 6072 6073 char * 6074 impl_map_lookup(map, name, av, pstat) 6075 MAP *map; 6076 char *name; 6077 char **av; 6078 int *pstat; 6079 { 6080 if (tTd(38, 20)) 6081 sm_dprintf("impl_map_lookup(%s, %s)\n", 6082 map->map_mname, name); 6083 6084 #if NEWDB 6085 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6086 return db_map_lookup(map, name, av, pstat); 6087 #endif /* NEWDB */ 6088 #if NDBM 6089 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6090 return ndbm_map_lookup(map, name, av, pstat); 6091 #endif /* NDBM */ 6092 return stab_map_lookup(map, name, av, pstat); 6093 } 6094 6095 /* 6096 ** IMPL_MAP_STORE -- store in open databases 6097 */ 6098 6099 void 6100 impl_map_store(map, lhs, rhs) 6101 MAP *map; 6102 char *lhs; 6103 char *rhs; 6104 { 6105 if (tTd(38, 12)) 6106 sm_dprintf("impl_map_store(%s, %s, %s)\n", 6107 map->map_mname, lhs, rhs); 6108 #if NEWDB 6109 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6110 db_map_store(map, lhs, rhs); 6111 #endif /* NEWDB */ 6112 #if NDBM 6113 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6114 ndbm_map_store(map, lhs, rhs); 6115 #endif /* NDBM */ 6116 stab_map_store(map, lhs, rhs); 6117 } 6118 6119 /* 6120 ** IMPL_MAP_OPEN -- implicit database open 6121 */ 6122 6123 bool 6124 impl_map_open(map, mode) 6125 MAP *map; 6126 int mode; 6127 { 6128 if (tTd(38, 2)) 6129 sm_dprintf("impl_map_open(%s, %s, %d)\n", 6130 map->map_mname, map->map_file, mode); 6131 6132 mode &= O_ACCMODE; 6133 #if NEWDB 6134 map->map_mflags |= MF_IMPL_HASH; 6135 if (hash_map_open(map, mode)) 6136 { 6137 # ifdef NDBM_YP_COMPAT 6138 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL) 6139 # endif /* NDBM_YP_COMPAT */ 6140 return true; 6141 } 6142 else 6143 map->map_mflags &= ~MF_IMPL_HASH; 6144 #endif /* NEWDB */ 6145 #if NDBM 6146 map->map_mflags |= MF_IMPL_NDBM; 6147 if (ndbm_map_open(map, mode)) 6148 { 6149 return true; 6150 } 6151 else 6152 map->map_mflags &= ~MF_IMPL_NDBM; 6153 #endif /* NDBM */ 6154 6155 #if defined(NEWDB) || defined(NDBM) 6156 if (Verbose) 6157 message("WARNING: cannot open alias database %s%s", 6158 map->map_file, 6159 mode == O_RDONLY ? "; reading text version" : ""); 6160 #else /* defined(NEWDB) || defined(NDBM) */ 6161 if (mode != O_RDONLY) 6162 usrerr("Cannot rebuild aliases: no database format defined"); 6163 #endif /* defined(NEWDB) || defined(NDBM) */ 6164 6165 if (mode == O_RDONLY) 6166 return stab_map_open(map, mode); 6167 else 6168 return false; 6169 } 6170 6171 6172 /* 6173 ** IMPL_MAP_CLOSE -- close any open database(s) 6174 */ 6175 6176 void 6177 impl_map_close(map) 6178 MAP *map; 6179 { 6180 if (tTd(38, 9)) 6181 sm_dprintf("impl_map_close(%s, %s, %lx)\n", 6182 map->map_mname, map->map_file, map->map_mflags); 6183 #if NEWDB 6184 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6185 { 6186 db_map_close(map); 6187 map->map_mflags &= ~MF_IMPL_HASH; 6188 } 6189 #endif /* NEWDB */ 6190 6191 #if NDBM 6192 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6193 { 6194 ndbm_map_close(map); 6195 map->map_mflags &= ~MF_IMPL_NDBM; 6196 } 6197 #endif /* NDBM */ 6198 } 6199 /* 6200 ** User map class. 6201 ** 6202 ** Provides access to the system password file. 6203 */ 6204 6205 /* 6206 ** USER_MAP_OPEN -- open user map 6207 ** 6208 ** Really just binds field names to field numbers. 6209 */ 6210 6211 bool 6212 user_map_open(map, mode) 6213 MAP *map; 6214 int mode; 6215 { 6216 if (tTd(38, 2)) 6217 sm_dprintf("user_map_open(%s, %d)\n", 6218 map->map_mname, mode); 6219 6220 mode &= O_ACCMODE; 6221 if (mode != O_RDONLY) 6222 { 6223 /* issue a pseudo-error message */ 6224 errno = SM_EMAPCANTWRITE; 6225 return false; 6226 } 6227 if (map->map_valcolnm == NULL) 6228 /* EMPTY */ 6229 /* nothing */ ; 6230 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0) 6231 map->map_valcolno = 1; 6232 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0) 6233 map->map_valcolno = 2; 6234 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0) 6235 map->map_valcolno = 3; 6236 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0) 6237 map->map_valcolno = 4; 6238 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0) 6239 map->map_valcolno = 5; 6240 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0) 6241 map->map_valcolno = 6; 6242 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0) 6243 map->map_valcolno = 7; 6244 else 6245 { 6246 syserr("User map %s: unknown column name %s", 6247 map->map_mname, map->map_valcolnm); 6248 return false; 6249 } 6250 return true; 6251 } 6252 6253 6254 /* 6255 ** USER_MAP_LOOKUP -- look up a user in the passwd file. 6256 */ 6257 6258 /* ARGSUSED3 */ 6259 char * 6260 user_map_lookup(map, key, av, statp) 6261 MAP *map; 6262 char *key; 6263 char **av; 6264 int *statp; 6265 { 6266 auto bool fuzzy; 6267 SM_MBDB_T user; 6268 6269 if (tTd(38, 20)) 6270 sm_dprintf("user_map_lookup(%s, %s)\n", 6271 map->map_mname, key); 6272 6273 *statp = finduser(key, &fuzzy, &user); 6274 if (*statp != EX_OK) 6275 return NULL; 6276 if (bitset(MF_MATCHONLY, map->map_mflags)) 6277 return map_rewrite(map, key, strlen(key), NULL); 6278 else 6279 { 6280 char *rwval = NULL; 6281 char buf[30]; 6282 6283 switch (map->map_valcolno) 6284 { 6285 case 0: 6286 case 1: 6287 rwval = user.mbdb_name; 6288 break; 6289 6290 case 2: 6291 rwval = "x"; /* passwd no longer supported */ 6292 break; 6293 6294 case 3: 6295 (void) sm_snprintf(buf, sizeof buf, "%d", 6296 (int) user.mbdb_uid); 6297 rwval = buf; 6298 break; 6299 6300 case 4: 6301 (void) sm_snprintf(buf, sizeof buf, "%d", 6302 (int) user.mbdb_gid); 6303 rwval = buf; 6304 break; 6305 6306 case 5: 6307 rwval = user.mbdb_fullname; 6308 break; 6309 6310 case 6: 6311 rwval = user.mbdb_homedir; 6312 break; 6313 6314 case 7: 6315 rwval = user.mbdb_shell; 6316 break; 6317 } 6318 return map_rewrite(map, rwval, strlen(rwval), av); 6319 } 6320 } 6321 /* 6322 ** Program map type. 6323 ** 6324 ** This provides access to arbitrary programs. It should be used 6325 ** only very sparingly, since there is no way to bound the cost 6326 ** of invoking an arbitrary program. 6327 */ 6328 6329 char * 6330 prog_map_lookup(map, name, av, statp) 6331 MAP *map; 6332 char *name; 6333 char **av; 6334 int *statp; 6335 { 6336 int i; 6337 int save_errno; 6338 int fd; 6339 int status; 6340 auto pid_t pid; 6341 register char *p; 6342 char *rval; 6343 char *argv[MAXPV + 1]; 6344 char buf[MAXLINE]; 6345 6346 if (tTd(38, 20)) 6347 sm_dprintf("prog_map_lookup(%s, %s) %s\n", 6348 map->map_mname, name, map->map_file); 6349 6350 i = 0; 6351 argv[i++] = map->map_file; 6352 if (map->map_rebuild != NULL) 6353 { 6354 (void) sm_strlcpy(buf, map->map_rebuild, sizeof buf); 6355 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) 6356 { 6357 if (i >= MAXPV - 1) 6358 break; 6359 argv[i++] = p; 6360 } 6361 } 6362 argv[i++] = name; 6363 argv[i] = NULL; 6364 if (tTd(38, 21)) 6365 { 6366 sm_dprintf("prog_open:"); 6367 for (i = 0; argv[i] != NULL; i++) 6368 sm_dprintf(" %s", argv[i]); 6369 sm_dprintf("\n"); 6370 } 6371 (void) sm_blocksignal(SIGCHLD); 6372 pid = prog_open(argv, &fd, CurEnv); 6373 if (pid < 0) 6374 { 6375 if (!bitset(MF_OPTIONAL, map->map_mflags)) 6376 syserr("prog_map_lookup(%s) failed (%s) -- closing", 6377 map->map_mname, sm_errstring(errno)); 6378 else if (tTd(38, 9)) 6379 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing", 6380 map->map_mname, sm_errstring(errno)); 6381 map->map_mflags &= ~(MF_VALID|MF_OPEN); 6382 *statp = EX_OSFILE; 6383 return NULL; 6384 } 6385 i = read(fd, buf, sizeof buf - 1); 6386 if (i < 0) 6387 { 6388 syserr("prog_map_lookup(%s): read error %s", 6389 map->map_mname, sm_errstring(errno)); 6390 rval = NULL; 6391 } 6392 else if (i == 0) 6393 { 6394 if (tTd(38, 20)) 6395 sm_dprintf("prog_map_lookup(%s): empty answer\n", 6396 map->map_mname); 6397 rval = NULL; 6398 } 6399 else 6400 { 6401 buf[i] = '\0'; 6402 p = strchr(buf, '\n'); 6403 if (p != NULL) 6404 *p = '\0'; 6405 6406 /* collect the return value */ 6407 if (bitset(MF_MATCHONLY, map->map_mflags)) 6408 rval = map_rewrite(map, name, strlen(name), NULL); 6409 else 6410 rval = map_rewrite(map, buf, strlen(buf), av); 6411 6412 /* now flush any additional output */ 6413 while ((i = read(fd, buf, sizeof buf)) > 0) 6414 continue; 6415 } 6416 6417 /* wait for the process to terminate */ 6418 (void) close(fd); 6419 status = waitfor(pid); 6420 save_errno = errno; 6421 (void) sm_releasesignal(SIGCHLD); 6422 errno = save_errno; 6423 6424 if (status == -1) 6425 { 6426 syserr("prog_map_lookup(%s): wait error %s", 6427 map->map_mname, sm_errstring(errno)); 6428 *statp = EX_SOFTWARE; 6429 rval = NULL; 6430 } 6431 else if (WIFEXITED(status)) 6432 { 6433 if ((*statp = WEXITSTATUS(status)) != EX_OK) 6434 rval = NULL; 6435 } 6436 else 6437 { 6438 syserr("prog_map_lookup(%s): child died on signal %d", 6439 map->map_mname, status); 6440 *statp = EX_UNAVAILABLE; 6441 rval = NULL; 6442 } 6443 return rval; 6444 } 6445 /* 6446 ** Sequenced map type. 6447 ** 6448 ** Tries each map in order until something matches, much like 6449 ** implicit. Stores go to the first map in the list that can 6450 ** support storing. 6451 ** 6452 ** This is slightly unusual in that there are two interfaces. 6453 ** The "sequence" interface lets you stack maps arbitrarily. 6454 ** The "switch" interface builds a sequence map by looking 6455 ** at a system-dependent configuration file such as 6456 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. 6457 ** 6458 ** We don't need an explicit open, since all maps are 6459 ** opened on demand. 6460 */ 6461 6462 /* 6463 ** SEQ_MAP_PARSE -- Sequenced map parsing 6464 */ 6465 6466 bool 6467 seq_map_parse(map, ap) 6468 MAP *map; 6469 char *ap; 6470 { 6471 int maxmap; 6472 6473 if (tTd(38, 2)) 6474 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap); 6475 maxmap = 0; 6476 while (*ap != '\0') 6477 { 6478 register char *p; 6479 STAB *s; 6480 6481 /* find beginning of map name */ 6482 while (isascii(*ap) && isspace(*ap)) 6483 ap++; 6484 for (p = ap; 6485 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.'; 6486 p++) 6487 continue; 6488 if (*p != '\0') 6489 *p++ = '\0'; 6490 while (*p != '\0' && (!isascii(*p) || !isalnum(*p))) 6491 p++; 6492 if (*ap == '\0') 6493 { 6494 ap = p; 6495 continue; 6496 } 6497 s = stab(ap, ST_MAP, ST_FIND); 6498 if (s == NULL) 6499 { 6500 syserr("Sequence map %s: unknown member map %s", 6501 map->map_mname, ap); 6502 } 6503 else if (maxmap >= MAXMAPSTACK) 6504 { 6505 syserr("Sequence map %s: too many member maps (%d max)", 6506 map->map_mname, MAXMAPSTACK); 6507 maxmap++; 6508 } 6509 else if (maxmap < MAXMAPSTACK) 6510 { 6511 map->map_stack[maxmap++] = &s->s_map; 6512 } 6513 ap = p; 6514 } 6515 return true; 6516 } 6517 6518 /* 6519 ** SWITCH_MAP_OPEN -- open a switched map 6520 ** 6521 ** This looks at the system-dependent configuration and builds 6522 ** a sequence map that does the same thing. 6523 ** 6524 ** Every system must define a switch_map_find routine in conf.c 6525 ** that will return the list of service types associated with a 6526 ** given service class. 6527 */ 6528 6529 bool 6530 switch_map_open(map, mode) 6531 MAP *map; 6532 int mode; 6533 { 6534 int mapno; 6535 int nmaps; 6536 char *maptype[MAXMAPSTACK]; 6537 6538 if (tTd(38, 2)) 6539 sm_dprintf("switch_map_open(%s, %s, %d)\n", 6540 map->map_mname, map->map_file, mode); 6541 6542 mode &= O_ACCMODE; 6543 nmaps = switch_map_find(map->map_file, maptype, map->map_return); 6544 if (tTd(38, 19)) 6545 { 6546 sm_dprintf("\tswitch_map_find => %d\n", nmaps); 6547 for (mapno = 0; mapno < nmaps; mapno++) 6548 sm_dprintf("\t\t%s\n", maptype[mapno]); 6549 } 6550 if (nmaps <= 0 || nmaps > MAXMAPSTACK) 6551 return false; 6552 6553 for (mapno = 0; mapno < nmaps; mapno++) 6554 { 6555 register STAB *s; 6556 char nbuf[MAXNAME + 1]; 6557 6558 if (maptype[mapno] == NULL) 6559 continue; 6560 (void) sm_strlcpyn(nbuf, sizeof nbuf, 3, 6561 map->map_mname, ".", maptype[mapno]); 6562 s = stab(nbuf, ST_MAP, ST_FIND); 6563 if (s == NULL) 6564 { 6565 syserr("Switch map %s: unknown member map %s", 6566 map->map_mname, nbuf); 6567 } 6568 else 6569 { 6570 map->map_stack[mapno] = &s->s_map; 6571 if (tTd(38, 4)) 6572 sm_dprintf("\tmap_stack[%d] = %s:%s\n", 6573 mapno, 6574 s->s_map.map_class->map_cname, 6575 nbuf); 6576 } 6577 } 6578 return true; 6579 } 6580 6581 #if 0 6582 /* 6583 ** SEQ_MAP_CLOSE -- close all underlying maps 6584 */ 6585 6586 void 6587 seq_map_close(map) 6588 MAP *map; 6589 { 6590 int mapno; 6591 6592 if (tTd(38, 9)) 6593 sm_dprintf("seq_map_close(%s)\n", map->map_mname); 6594 6595 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6596 { 6597 MAP *mm = map->map_stack[mapno]; 6598 6599 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags)) 6600 continue; 6601 mm->map_mflags |= MF_CLOSING; 6602 mm->map_class->map_close(mm); 6603 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 6604 } 6605 } 6606 #endif /* 0 */ 6607 6608 /* 6609 ** SEQ_MAP_LOOKUP -- sequenced map lookup 6610 */ 6611 6612 char * 6613 seq_map_lookup(map, key, args, pstat) 6614 MAP *map; 6615 char *key; 6616 char **args; 6617 int *pstat; 6618 { 6619 int mapno; 6620 int mapbit = 0x01; 6621 bool tempfail = false; 6622 6623 if (tTd(38, 20)) 6624 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key); 6625 6626 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) 6627 { 6628 MAP *mm = map->map_stack[mapno]; 6629 char *rv; 6630 6631 if (mm == NULL) 6632 continue; 6633 if (!bitset(MF_OPEN, mm->map_mflags) && 6634 !openmap(mm)) 6635 { 6636 if (bitset(mapbit, map->map_return[MA_UNAVAIL])) 6637 { 6638 *pstat = EX_UNAVAILABLE; 6639 return NULL; 6640 } 6641 continue; 6642 } 6643 *pstat = EX_OK; 6644 rv = mm->map_class->map_lookup(mm, key, args, pstat); 6645 if (rv != NULL) 6646 return rv; 6647 if (*pstat == EX_TEMPFAIL) 6648 { 6649 if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) 6650 return NULL; 6651 tempfail = true; 6652 } 6653 else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) 6654 break; 6655 } 6656 if (tempfail) 6657 *pstat = EX_TEMPFAIL; 6658 else if (*pstat == EX_OK) 6659 *pstat = EX_NOTFOUND; 6660 return NULL; 6661 } 6662 6663 /* 6664 ** SEQ_MAP_STORE -- sequenced map store 6665 */ 6666 6667 void 6668 seq_map_store(map, key, val) 6669 MAP *map; 6670 char *key; 6671 char *val; 6672 { 6673 int mapno; 6674 6675 if (tTd(38, 12)) 6676 sm_dprintf("seq_map_store(%s, %s, %s)\n", 6677 map->map_mname, key, val); 6678 6679 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6680 { 6681 MAP *mm = map->map_stack[mapno]; 6682 6683 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags)) 6684 continue; 6685 6686 mm->map_class->map_store(mm, key, val); 6687 return; 6688 } 6689 syserr("seq_map_store(%s, %s, %s): no writable map", 6690 map->map_mname, key, val); 6691 } 6692 /* 6693 ** NULL stubs 6694 */ 6695 6696 /* ARGSUSED */ 6697 bool 6698 null_map_open(map, mode) 6699 MAP *map; 6700 int mode; 6701 { 6702 return true; 6703 } 6704 6705 /* ARGSUSED */ 6706 void 6707 null_map_close(map) 6708 MAP *map; 6709 { 6710 return; 6711 } 6712 6713 char * 6714 null_map_lookup(map, key, args, pstat) 6715 MAP *map; 6716 char *key; 6717 char **args; 6718 int *pstat; 6719 { 6720 *pstat = EX_NOTFOUND; 6721 return NULL; 6722 } 6723 6724 /* ARGSUSED */ 6725 void 6726 null_map_store(map, key, val) 6727 MAP *map; 6728 char *key; 6729 char *val; 6730 { 6731 return; 6732 } 6733 6734 /* 6735 ** BOGUS stubs 6736 */ 6737 6738 char * 6739 bogus_map_lookup(map, key, args, pstat) 6740 MAP *map; 6741 char *key; 6742 char **args; 6743 int *pstat; 6744 { 6745 *pstat = EX_TEMPFAIL; 6746 return NULL; 6747 } 6748 6749 MAPCLASS BogusMapClass = 6750 { 6751 "bogus-map", NULL, 0, 6752 NULL, bogus_map_lookup, null_map_store, 6753 null_map_open, null_map_close, 6754 }; 6755 /* 6756 ** MACRO modules 6757 */ 6758 6759 char * 6760 macro_map_lookup(map, name, av, statp) 6761 MAP *map; 6762 char *name; 6763 char **av; 6764 int *statp; 6765 { 6766 int mid; 6767 6768 if (tTd(38, 20)) 6769 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname, 6770 name == NULL ? "NULL" : name); 6771 6772 if (name == NULL || 6773 *name == '\0' || 6774 (mid = macid(name)) == 0) 6775 { 6776 *statp = EX_CONFIG; 6777 return NULL; 6778 } 6779 6780 if (av[1] == NULL) 6781 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL); 6782 else 6783 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]); 6784 6785 *statp = EX_OK; 6786 return ""; 6787 } 6788 /* 6789 ** REGEX modules 6790 */ 6791 6792 #if MAP_REGEX 6793 6794 # include <regex.h> 6795 6796 # define DEFAULT_DELIM CONDELSE 6797 # define END_OF_FIELDS -1 6798 # define ERRBUF_SIZE 80 6799 # define MAX_MATCH 32 6800 6801 # define xnalloc(s) memset(xalloc(s), '\0', s); 6802 6803 struct regex_map 6804 { 6805 regex_t *regex_pattern_buf; /* xalloc it */ 6806 int *regex_subfields; /* move to type MAP */ 6807 char *regex_delim; /* move to type MAP */ 6808 }; 6809 6810 static int 6811 parse_fields(s, ibuf, blen, nr_substrings) 6812 char *s; 6813 int *ibuf; /* array */ 6814 int blen; /* number of elements in ibuf */ 6815 int nr_substrings; /* number of substrings in the pattern */ 6816 { 6817 register char *cp; 6818 int i = 0; 6819 bool lastone = false; 6820 6821 blen--; /* for terminating END_OF_FIELDS */ 6822 cp = s; 6823 do 6824 { 6825 for (;; cp++) 6826 { 6827 if (*cp == ',') 6828 { 6829 *cp = '\0'; 6830 break; 6831 } 6832 if (*cp == '\0') 6833 { 6834 lastone = true; 6835 break; 6836 } 6837 } 6838 if (i < blen) 6839 { 6840 int val = atoi(s); 6841 6842 if (val < 0 || val >= nr_substrings) 6843 { 6844 syserr("field (%d) out of range, only %d substrings in pattern", 6845 val, nr_substrings); 6846 return -1; 6847 } 6848 ibuf[i++] = val; 6849 } 6850 else 6851 { 6852 syserr("too many fields, %d max", blen); 6853 return -1; 6854 } 6855 s = ++cp; 6856 } while (!lastone); 6857 ibuf[i] = END_OF_FIELDS; 6858 return i; 6859 } 6860 6861 bool 6862 regex_map_init(map, ap) 6863 MAP *map; 6864 char *ap; 6865 { 6866 int regerr; 6867 struct regex_map *map_p; 6868 register char *p; 6869 char *sub_param = NULL; 6870 int pflags; 6871 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' }; 6872 6873 if (tTd(38, 2)) 6874 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n", 6875 map->map_mname, ap); 6876 6877 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; 6878 p = ap; 6879 map_p = (struct regex_map *) xnalloc(sizeof *map_p); 6880 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t)); 6881 6882 for (;;) 6883 { 6884 while (isascii(*p) && isspace(*p)) 6885 p++; 6886 if (*p != '-') 6887 break; 6888 switch (*++p) 6889 { 6890 case 'n': /* not */ 6891 map->map_mflags |= MF_REGEX_NOT; 6892 break; 6893 6894 case 'f': /* case sensitive */ 6895 map->map_mflags |= MF_NOFOLDCASE; 6896 pflags &= ~REG_ICASE; 6897 break; 6898 6899 case 'b': /* basic regular expressions */ 6900 pflags &= ~REG_EXTENDED; 6901 break; 6902 6903 case 's': /* substring match () syntax */ 6904 sub_param = ++p; 6905 pflags &= ~REG_NOSUB; 6906 break; 6907 6908 case 'd': /* delimiter */ 6909 map_p->regex_delim = ++p; 6910 break; 6911 6912 case 'a': /* map append */ 6913 map->map_app = ++p; 6914 break; 6915 6916 case 'm': /* matchonly */ 6917 map->map_mflags |= MF_MATCHONLY; 6918 break; 6919 6920 case 'S': 6921 map->map_spacesub = *++p; 6922 break; 6923 6924 case 'D': 6925 map->map_mflags |= MF_DEFER; 6926 break; 6927 6928 } 6929 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 6930 p++; 6931 if (*p != '\0') 6932 *p++ = '\0'; 6933 } 6934 if (tTd(38, 3)) 6935 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags); 6936 6937 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0) 6938 { 6939 /* Errorhandling */ 6940 char errbuf[ERRBUF_SIZE]; 6941 6942 (void) regerror(regerr, map_p->regex_pattern_buf, 6943 errbuf, sizeof errbuf); 6944 syserr("pattern-compile-error: %s", errbuf); 6945 sm_free(map_p->regex_pattern_buf); /* XXX */ 6946 sm_free(map_p); /* XXX */ 6947 return false; 6948 } 6949 6950 if (map->map_app != NULL) 6951 map->map_app = newstr(map->map_app); 6952 if (map_p->regex_delim != NULL) 6953 map_p->regex_delim = newstr(map_p->regex_delim); 6954 else 6955 map_p->regex_delim = defdstr; 6956 6957 if (!bitset(REG_NOSUB, pflags)) 6958 { 6959 /* substring matching */ 6960 int substrings; 6961 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1)); 6962 6963 substrings = map_p->regex_pattern_buf->re_nsub + 1; 6964 6965 if (tTd(38, 3)) 6966 sm_dprintf("regex_map_init: nr of substrings %d\n", 6967 substrings); 6968 6969 if (substrings >= MAX_MATCH) 6970 { 6971 syserr("too many substrings, %d max", MAX_MATCH); 6972 sm_free(map_p->regex_pattern_buf); /* XXX */ 6973 sm_free(map_p); /* XXX */ 6974 return false; 6975 } 6976 if (sub_param != NULL && sub_param[0] != '\0') 6977 { 6978 /* optional parameter -sfields */ 6979 if (parse_fields(sub_param, fields, 6980 MAX_MATCH + 1, substrings) == -1) 6981 return false; 6982 } 6983 else 6984 { 6985 int i; 6986 6987 /* set default fields */ 6988 for (i = 0; i < substrings; i++) 6989 fields[i] = i; 6990 fields[i] = END_OF_FIELDS; 6991 } 6992 map_p->regex_subfields = fields; 6993 if (tTd(38, 3)) 6994 { 6995 int *ip; 6996 6997 sm_dprintf("regex_map_init: subfields"); 6998 for (ip = fields; *ip != END_OF_FIELDS; ip++) 6999 sm_dprintf(" %d", *ip); 7000 sm_dprintf("\n"); 7001 } 7002 } 7003 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */ 7004 return true; 7005 } 7006 7007 static char * 7008 regex_map_rewrite(map, s, slen, av) 7009 MAP *map; 7010 const char *s; 7011 size_t slen; 7012 char **av; 7013 { 7014 if (bitset(MF_MATCHONLY, map->map_mflags)) 7015 return map_rewrite(map, av[0], strlen(av[0]), NULL); 7016 else 7017 return map_rewrite(map, s, slen, av); 7018 } 7019 7020 char * 7021 regex_map_lookup(map, name, av, statp) 7022 MAP *map; 7023 char *name; 7024 char **av; 7025 int *statp; 7026 { 7027 int reg_res; 7028 struct regex_map *map_p; 7029 regmatch_t pmatch[MAX_MATCH]; 7030 7031 if (tTd(38, 20)) 7032 { 7033 char **cpp; 7034 7035 sm_dprintf("regex_map_lookup: key '%s'\n", name); 7036 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7037 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp); 7038 } 7039 7040 map_p = (struct regex_map *)(map->map_db1); 7041 reg_res = regexec(map_p->regex_pattern_buf, 7042 name, MAX_MATCH, pmatch, 0); 7043 7044 if (bitset(MF_REGEX_NOT, map->map_mflags)) 7045 { 7046 /* option -n */ 7047 if (reg_res == REG_NOMATCH) 7048 return regex_map_rewrite(map, "", (size_t) 0, av); 7049 else 7050 return NULL; 7051 } 7052 if (reg_res == REG_NOMATCH) 7053 return NULL; 7054 7055 if (map_p->regex_subfields != NULL) 7056 { 7057 /* option -s */ 7058 static char retbuf[MAXNAME]; 7059 int fields[MAX_MATCH + 1]; 7060 bool first = true; 7061 int anglecnt = 0, cmntcnt = 0, spacecnt = 0; 7062 bool quotemode = false, bslashmode = false; 7063 register char *dp, *sp; 7064 char *endp, *ldp; 7065 int *ip; 7066 7067 dp = retbuf; 7068 ldp = retbuf + sizeof(retbuf) - 1; 7069 7070 if (av[1] != NULL) 7071 { 7072 if (parse_fields(av[1], fields, MAX_MATCH + 1, 7073 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1) 7074 { 7075 *statp = EX_CONFIG; 7076 return NULL; 7077 } 7078 ip = fields; 7079 } 7080 else 7081 ip = map_p->regex_subfields; 7082 7083 for ( ; *ip != END_OF_FIELDS; ip++) 7084 { 7085 if (!first) 7086 { 7087 for (sp = map_p->regex_delim; *sp; sp++) 7088 { 7089 if (dp < ldp) 7090 *dp++ = *sp; 7091 } 7092 } 7093 else 7094 first = false; 7095 7096 if (*ip >= MAX_MATCH || 7097 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0) 7098 continue; 7099 7100 sp = name + pmatch[*ip].rm_so; 7101 endp = name + pmatch[*ip].rm_eo; 7102 for (; endp > sp; sp++) 7103 { 7104 if (dp < ldp) 7105 { 7106 if (bslashmode) 7107 { 7108 *dp++ = *sp; 7109 bslashmode = false; 7110 } 7111 else if (quotemode && *sp != '"' && 7112 *sp != '\\') 7113 { 7114 *dp++ = *sp; 7115 } 7116 else switch (*dp++ = *sp) 7117 { 7118 case '\\': 7119 bslashmode = true; 7120 break; 7121 7122 case '(': 7123 cmntcnt++; 7124 break; 7125 7126 case ')': 7127 cmntcnt--; 7128 break; 7129 7130 case '<': 7131 anglecnt++; 7132 break; 7133 7134 case '>': 7135 anglecnt--; 7136 break; 7137 7138 case ' ': 7139 spacecnt++; 7140 break; 7141 7142 case '"': 7143 quotemode = !quotemode; 7144 break; 7145 } 7146 } 7147 } 7148 } 7149 if (anglecnt != 0 || cmntcnt != 0 || quotemode || 7150 bslashmode || spacecnt != 0) 7151 { 7152 sm_syslog(LOG_WARNING, NOQID, 7153 "Warning: regex may cause prescan() failure map=%s lookup=%s", 7154 map->map_mname, name); 7155 return NULL; 7156 } 7157 7158 *dp = '\0'; 7159 7160 return regex_map_rewrite(map, retbuf, strlen(retbuf), av); 7161 } 7162 return regex_map_rewrite(map, "", (size_t)0, av); 7163 } 7164 #endif /* MAP_REGEX */ 7165 /* 7166 ** NSD modules 7167 */ 7168 #if MAP_NSD 7169 7170 # include <ndbm.h> 7171 # define _DATUM_DEFINED 7172 # include <ns_api.h> 7173 7174 typedef struct ns_map_list 7175 { 7176 ns_map_t *map; /* XXX ns_ ? */ 7177 char *mapname; 7178 struct ns_map_list *next; 7179 } ns_map_list_t; 7180 7181 static ns_map_t * 7182 ns_map_t_find(mapname) 7183 char *mapname; 7184 { 7185 static ns_map_list_t *ns_maps = NULL; 7186 ns_map_list_t *ns_map; 7187 7188 /* walk the list of maps looking for the correctly named map */ 7189 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next) 7190 { 7191 if (strcmp(ns_map->mapname, mapname) == 0) 7192 break; 7193 } 7194 7195 /* if we are looking at a NULL ns_map_list_t, then create a new one */ 7196 if (ns_map == NULL) 7197 { 7198 ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map); 7199 ns_map->mapname = newstr(mapname); 7200 ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map); 7201 ns_map->next = ns_maps; 7202 ns_maps = ns_map; 7203 } 7204 return ns_map->map; 7205 } 7206 7207 char * 7208 nsd_map_lookup(map, name, av, statp) 7209 MAP *map; 7210 char *name; 7211 char **av; 7212 int *statp; 7213 { 7214 int buflen, r; 7215 char *p; 7216 ns_map_t *ns_map; 7217 char keybuf[MAXNAME + 1]; 7218 char buf[MAXLINE]; 7219 7220 if (tTd(38, 20)) 7221 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name); 7222 7223 buflen = strlen(name); 7224 if (buflen > sizeof keybuf - 1) 7225 buflen = sizeof keybuf - 1; /* XXX simply cut off? */ 7226 memmove(keybuf, name, buflen); 7227 keybuf[buflen] = '\0'; 7228 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 7229 makelower(keybuf); 7230 7231 ns_map = ns_map_t_find(map->map_file); 7232 if (ns_map == NULL) 7233 { 7234 if (tTd(38, 20)) 7235 sm_dprintf("nsd_map_t_find failed\n"); 7236 *statp = EX_UNAVAILABLE; 7237 return NULL; 7238 } 7239 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL, 7240 buf, sizeof buf); 7241 if (r == NS_UNAVAIL || r == NS_TRYAGAIN) 7242 { 7243 *statp = EX_TEMPFAIL; 7244 return NULL; 7245 } 7246 if (r == NS_BADREQ 7247 # ifdef NS_NOPERM 7248 || r == NS_NOPERM 7249 # endif /* NS_NOPERM */ 7250 ) 7251 { 7252 *statp = EX_CONFIG; 7253 return NULL; 7254 } 7255 if (r != NS_SUCCESS) 7256 { 7257 *statp = EX_NOTFOUND; 7258 return NULL; 7259 } 7260 7261 *statp = EX_OK; 7262 7263 /* Null out trailing \n */ 7264 if ((p = strchr(buf, '\n')) != NULL) 7265 *p = '\0'; 7266 7267 return map_rewrite(map, buf, strlen(buf), av); 7268 } 7269 #endif /* MAP_NSD */ 7270 7271 char * 7272 arith_map_lookup(map, name, av, statp) 7273 MAP *map; 7274 char *name; 7275 char **av; 7276 int *statp; 7277 { 7278 long r; 7279 long v[2]; 7280 bool res = false; 7281 bool boolres; 7282 static char result[16]; 7283 char **cpp; 7284 7285 if (tTd(38, 2)) 7286 { 7287 sm_dprintf("arith_map_lookup: key '%s'\n", name); 7288 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7289 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp); 7290 } 7291 r = 0; 7292 boolres = false; 7293 cpp = av; 7294 *statp = EX_OK; 7295 7296 /* 7297 ** read arguments for arith map 7298 ** - no check is made whether they are really numbers 7299 ** - just ignores args after the second 7300 */ 7301 7302 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++) 7303 v[r++] = strtol(*cpp, NULL, 0); 7304 7305 /* operator and (at least) two operands given? */ 7306 if (name != NULL && r == 2) 7307 { 7308 switch (*name) 7309 { 7310 case '|': 7311 r = v[0] | v[1]; 7312 break; 7313 7314 case '&': 7315 r = v[0] & v[1]; 7316 break; 7317 7318 case '%': 7319 if (v[1] == 0) 7320 return NULL; 7321 r = v[0] % v[1]; 7322 break; 7323 case '+': 7324 r = v[0] + v[1]; 7325 break; 7326 7327 case '-': 7328 r = v[0] - v[1]; 7329 break; 7330 7331 case '*': 7332 r = v[0] * v[1]; 7333 break; 7334 7335 case '/': 7336 if (v[1] == 0) 7337 return NULL; 7338 r = v[0] / v[1]; 7339 break; 7340 7341 case 'l': 7342 res = v[0] < v[1]; 7343 boolres = true; 7344 break; 7345 7346 case '=': 7347 res = v[0] == v[1]; 7348 boolres = true; 7349 break; 7350 7351 default: 7352 /* XXX */ 7353 *statp = EX_CONFIG; 7354 if (LogLevel > 10) 7355 sm_syslog(LOG_WARNING, NOQID, 7356 "arith_map: unknown operator %c", 7357 isprint(*name) ? *name : '?'); 7358 return NULL; 7359 } 7360 if (boolres) 7361 (void) sm_snprintf(result, sizeof result, 7362 res ? "TRUE" : "FALSE"); 7363 else 7364 (void) sm_snprintf(result, sizeof result, "%ld", r); 7365 return result; 7366 } 7367 *statp = EX_CONFIG; 7368 return NULL; 7369 } 7370