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