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