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