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