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