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