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