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.670 2005/06/23 23:11:22 ca Exp $") 24 SM_IDSTR(i2, "%W% (Sun) %G%") 25 26 #if LDAPMAP 27 # include <sm/ldap.h> 28 #endif /* LDAPMAP */ 29 30 #if NDBM 31 # include <ndbm.h> 32 # ifdef R_FIRST 33 ERROR README: You are running the Berkeley DB version of ndbm.h. See 34 ERROR README: the README file about tweaking Berkeley DB so it can 35 ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile 36 ERROR README: and use -DNEWDB instead. 37 # endif /* R_FIRST */ 38 #endif /* NDBM */ 39 #if NEWDB 40 # include "sm/bdb.h" 41 #endif /* NEWDB */ 42 #if NIS 43 struct dom_binding; /* forward reference needed on IRIX */ 44 # include <rpcsvc/ypclnt.h> 45 # if NDBM 46 # define NDBM_YP_COMPAT /* create YP-compatible NDBM files */ 47 # endif /* NDBM */ 48 #endif /* NIS */ 49 50 #if NEWDB 51 # if DB_VERSION_MAJOR < 2 52 static bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *)); 53 # endif /* DB_VERSION_MAJOR < 2 */ 54 # if DB_VERSION_MAJOR == 2 55 static bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *)); 56 # endif /* DB_VERSION_MAJOR == 2 */ 57 # if DB_VERSION_MAJOR > 2 58 static bool db_map_open __P((MAP *, int, char *, DBTYPE, void **)); 59 # endif /* DB_VERSION_MAJOR > 2 */ 60 #endif /* NEWDB */ 61 static bool extract_canonname __P((char *, char *, char *, char[], int)); 62 static void map_close __P((STAB *, int)); 63 static void map_init __P((STAB *, int)); 64 #ifdef LDAPMAP 65 static STAB * ldapmap_findconn __P((SM_LDAP_STRUCT *)); 66 #endif /* LDAPMAP */ 67 #if NISPLUS 68 static bool nisplus_getcanonname __P((char *, int, int *)); 69 #endif /* NISPLUS */ 70 #if NIS 71 static bool nis_getcanonname __P((char *, int, int *)); 72 #endif /* NIS */ 73 #if NETINFO 74 static bool ni_getcanonname __P((char *, int, int *)); 75 #endif /* NETINFO */ 76 static bool text_getcanonname __P((char *, int, int *)); 77 #if SOCKETMAP 78 static STAB *socket_map_findconn __P((const char*)); 79 80 /* XXX arbitrary limit for sanity */ 81 # define SOCKETMAP_MAXL 1000000 82 #endif /* SOCKETMAP */ 83 84 /* default error message for trying to open a map in write mode */ 85 #ifdef ENOSYS 86 # define SM_EMAPCANTWRITE ENOSYS 87 #else /* ENOSYS */ 88 # ifdef EFTYPE 89 # define SM_EMAPCANTWRITE EFTYPE 90 # else /* EFTYPE */ 91 # define SM_EMAPCANTWRITE ENXIO 92 # endif /* EFTYPE */ 93 #endif /* ENOSYS */ 94 95 /* 96 ** MAP.C -- implementations for various map classes. 97 ** 98 ** Each map class implements a series of functions: 99 ** 100 ** bool map_parse(MAP *map, char *args) 101 ** Parse the arguments from the config file. Return true 102 ** if they were ok, false otherwise. Fill in map with the 103 ** values. 104 ** 105 ** char *map_lookup(MAP *map, char *key, char **args, int *pstat) 106 ** Look up the key in the given map. If found, do any 107 ** rewriting the map wants (including "args" if desired) 108 ** and return the value. Set *pstat to the appropriate status 109 ** on error and return NULL. Args will be NULL if called 110 ** from the alias routines, although this should probably 111 ** not be relied upon. It is suggested you call map_rewrite 112 ** to return the results -- it takes care of null termination 113 ** and uses a dynamically expanded buffer as needed. 114 ** 115 ** void map_store(MAP *map, char *key, char *value) 116 ** Store the key:value pair in the map. 117 ** 118 ** bool map_open(MAP *map, int mode) 119 ** Open the map for the indicated mode. Mode should 120 ** be either O_RDONLY or O_RDWR. Return true if it 121 ** was opened successfully, false otherwise. If the open 122 ** failed and the MF_OPTIONAL flag is not set, it should 123 ** also print an error. If the MF_ALIAS bit is set 124 ** and this map class understands the @:@ convention, it 125 ** should call aliaswait() before returning. 126 ** 127 ** void map_close(MAP *map) 128 ** Close the map. 129 ** 130 ** This file also includes the implementation for getcanonname. 131 ** It is currently implemented in a pretty ad-hoc manner; it ought 132 ** to be more properly integrated into the map structure. 133 */ 134 135 #if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL 136 # define LOCK_ON_OPEN 1 /* we can open/create a locked file */ 137 #else /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */ 138 # define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */ 139 #endif /* O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL */ 140 141 /* 142 ** MAP_PARSEARGS -- parse config line arguments for database lookup 143 ** 144 ** This is a generic version of the map_parse method. 145 ** 146 ** Parameters: 147 ** map -- the map being initialized. 148 ** ap -- a pointer to the args on the config line. 149 ** 150 ** Returns: 151 ** true -- if everything parsed OK. 152 ** false -- otherwise. 153 ** 154 ** Side Effects: 155 ** null terminates the filename; stores it in map 156 */ 157 158 bool 159 map_parseargs(map, ap) 160 MAP *map; 161 char *ap; 162 { 163 register char *p = ap; 164 165 /* 166 ** There is no check whether there is really an argument, 167 ** but that's not important enough to warrant extra code. 168 */ 169 170 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 171 map->map_spacesub = SpaceSub; /* default value */ 172 for (;;) 173 { 174 while (isascii(*p) && isspace(*p)) 175 p++; 176 if (*p != '-') 177 break; 178 switch (*++p) 179 { 180 case 'N': 181 map->map_mflags |= MF_INCLNULL; 182 map->map_mflags &= ~MF_TRY0NULL; 183 break; 184 185 case 'O': 186 map->map_mflags &= ~MF_TRY1NULL; 187 break; 188 189 case 'o': 190 map->map_mflags |= MF_OPTIONAL; 191 break; 192 193 case 'f': 194 map->map_mflags |= MF_NOFOLDCASE; 195 break; 196 197 case 'm': 198 map->map_mflags |= MF_MATCHONLY; 199 break; 200 201 case 'A': 202 map->map_mflags |= MF_APPEND; 203 break; 204 205 case 'q': 206 map->map_mflags |= MF_KEEPQUOTES; 207 break; 208 209 case 'a': 210 map->map_app = ++p; 211 break; 212 213 case 'T': 214 map->map_tapp = ++p; 215 break; 216 217 case 'k': 218 while (isascii(*++p) && isspace(*p)) 219 continue; 220 map->map_keycolnm = p; 221 break; 222 223 case 'v': 224 while (isascii(*++p) && isspace(*p)) 225 continue; 226 map->map_valcolnm = p; 227 break; 228 229 case 'z': 230 if (*++p != '\\') 231 map->map_coldelim = *p; 232 else 233 { 234 switch (*++p) 235 { 236 case 'n': 237 map->map_coldelim = '\n'; 238 break; 239 240 case 't': 241 map->map_coldelim = '\t'; 242 break; 243 244 default: 245 map->map_coldelim = '\\'; 246 } 247 } 248 break; 249 250 case 't': 251 map->map_mflags |= MF_NODEFER; 252 break; 253 254 255 case 'S': 256 map->map_spacesub = *++p; 257 break; 258 259 case 'D': 260 map->map_mflags |= MF_DEFER; 261 break; 262 263 default: 264 syserr("Illegal option %c map %s", *p, map->map_mname); 265 break; 266 } 267 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 268 p++; 269 if (*p != '\0') 270 *p++ = '\0'; 271 } 272 if (map->map_app != NULL) 273 map->map_app = newstr(map->map_app); 274 if (map->map_tapp != NULL) 275 map->map_tapp = newstr(map->map_tapp); 276 if (map->map_keycolnm != NULL) 277 map->map_keycolnm = newstr(map->map_keycolnm); 278 if (map->map_valcolnm != NULL) 279 map->map_valcolnm = newstr(map->map_valcolnm); 280 281 if (*p != '\0') 282 { 283 map->map_file = p; 284 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 285 p++; 286 if (*p != '\0') 287 *p++ = '\0'; 288 map->map_file = newstr(map->map_file); 289 } 290 291 while (*p != '\0' && isascii(*p) && isspace(*p)) 292 p++; 293 if (*p != '\0') 294 map->map_rebuild = newstr(p); 295 296 if (map->map_file == NULL && 297 !bitset(MCF_OPTFILE, map->map_class->map_cflags)) 298 { 299 syserr("No file name for %s map %s", 300 map->map_class->map_cname, map->map_mname); 301 return false; 302 } 303 return true; 304 } 305 /* 306 ** MAP_REWRITE -- rewrite a database key, interpolating %n indications. 307 ** 308 ** It also adds the map_app string. It can be used as a utility 309 ** in the map_lookup method. 310 ** 311 ** Parameters: 312 ** map -- the map that causes this. 313 ** s -- the string to rewrite, NOT necessarily null terminated. 314 ** slen -- the length of s. 315 ** av -- arguments to interpolate into buf. 316 ** 317 ** Returns: 318 ** Pointer to rewritten result. This is static data that 319 ** should be copied if it is to be saved! 320 */ 321 322 char * 323 map_rewrite(map, s, slen, av) 324 register MAP *map; 325 register const char *s; 326 size_t slen; 327 char **av; 328 { 329 register char *bp; 330 register char c; 331 char **avp; 332 register char *ap; 333 size_t l; 334 size_t len; 335 static size_t buflen = 0; 336 static char *buf = NULL; 337 338 if (tTd(39, 1)) 339 { 340 sm_dprintf("map_rewrite(%.*s), av =", (int) slen, s); 341 if (av == NULL) 342 sm_dprintf(" (nullv)"); 343 else 344 { 345 for (avp = av; *avp != NULL; avp++) 346 sm_dprintf("\n\t%s", *avp); 347 } 348 sm_dprintf("\n"); 349 } 350 351 /* count expected size of output (can safely overestimate) */ 352 l = len = slen; 353 if (av != NULL) 354 { 355 const char *sp = s; 356 357 while (l-- > 0 && (c = *sp++) != '\0') 358 { 359 if (c != '%') 360 continue; 361 if (l-- <= 0) 362 break; 363 c = *sp++; 364 if (!(isascii(c) && isdigit(c))) 365 continue; 366 for (avp = av; --c >= '0' && *avp != NULL; avp++) 367 continue; 368 if (*avp == NULL) 369 continue; 370 len += strlen(*avp); 371 } 372 } 373 if (map->map_app != NULL) 374 len += strlen(map->map_app); 375 if (buflen < ++len) 376 { 377 /* need to malloc additional space */ 378 buflen = len; 379 if (buf != NULL) 380 sm_free(buf); 381 buf = sm_pmalloc_x(buflen); 382 } 383 384 bp = buf; 385 if (av == NULL) 386 { 387 memmove(bp, s, slen); 388 bp += slen; 389 390 /* assert(len > slen); */ 391 len -= slen; 392 } 393 else 394 { 395 while (slen-- > 0 && (c = *s++) != '\0') 396 { 397 if (c != '%') 398 { 399 pushc: 400 if (len-- <= 1) 401 break; 402 *bp++ = c; 403 continue; 404 } 405 if (slen-- <= 0 || (c = *s++) == '\0') 406 c = '%'; 407 if (c == '%') 408 goto pushc; 409 if (!(isascii(c) && isdigit(c))) 410 { 411 if (len-- <= 1) 412 break; 413 *bp++ = '%'; 414 goto pushc; 415 } 416 for (avp = av; --c >= '0' && *avp != NULL; avp++) 417 continue; 418 if (*avp == NULL) 419 continue; 420 421 /* transliterate argument into output string */ 422 for (ap = *avp; (c = *ap++) != '\0' && len > 0; --len) 423 *bp++ = c; 424 } 425 } 426 if (map->map_app != NULL && len > 0) 427 (void) sm_strlcpy(bp, map->map_app, len); 428 else 429 *bp = '\0'; 430 if (tTd(39, 1)) 431 sm_dprintf("map_rewrite => %s\n", buf); 432 return buf; 433 } 434 /* 435 ** INITMAPS -- rebuild alias maps 436 ** 437 ** Parameters: 438 ** none. 439 ** 440 ** Returns: 441 ** none. 442 */ 443 444 void 445 initmaps() 446 { 447 #if XDEBUG 448 checkfd012("entering initmaps"); 449 #endif /* XDEBUG */ 450 stabapply(map_init, 0); 451 #if XDEBUG 452 checkfd012("exiting initmaps"); 453 #endif /* XDEBUG */ 454 } 455 /* 456 ** MAP_INIT -- rebuild a map 457 ** 458 ** Parameters: 459 ** s -- STAB entry: if map: try to rebuild 460 ** unused -- unused variable 461 ** 462 ** Returns: 463 ** none. 464 ** 465 ** Side Effects: 466 ** will close already open rebuildable map. 467 */ 468 469 /* ARGSUSED1 */ 470 static void 471 map_init(s, unused) 472 register STAB *s; 473 int unused; 474 { 475 register MAP *map; 476 477 /* has to be a map */ 478 if (s->s_symtype != ST_MAP) 479 return; 480 481 map = &s->s_map; 482 if (!bitset(MF_VALID, map->map_mflags)) 483 return; 484 485 if (tTd(38, 2)) 486 sm_dprintf("map_init(%s:%s, %s)\n", 487 map->map_class->map_cname == NULL ? "NULL" : 488 map->map_class->map_cname, 489 map->map_mname == NULL ? "NULL" : map->map_mname, 490 map->map_file == NULL ? "NULL" : map->map_file); 491 492 if (!bitset(MF_ALIAS, map->map_mflags) || 493 !bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) 494 { 495 if (tTd(38, 3)) 496 sm_dprintf("\tnot rebuildable\n"); 497 return; 498 } 499 500 /* if already open, close it (for nested open) */ 501 if (bitset(MF_OPEN, map->map_mflags)) 502 { 503 map->map_mflags |= MF_CLOSING; 504 map->map_class->map_close(map); 505 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 506 } 507 508 (void) rebuildaliases(map, false); 509 return; 510 } 511 /* 512 ** OPENMAP -- open a map 513 ** 514 ** Parameters: 515 ** map -- map to open (it must not be open). 516 ** 517 ** Returns: 518 ** whether open succeeded. 519 */ 520 521 bool 522 openmap(map) 523 MAP *map; 524 { 525 bool restore = false; 526 bool savehold = HoldErrs; 527 bool savequick = QuickAbort; 528 int saveerrors = Errors; 529 530 if (!bitset(MF_VALID, map->map_mflags)) 531 return false; 532 533 /* better safe than sorry... */ 534 if (bitset(MF_OPEN, map->map_mflags)) 535 return true; 536 537 /* Don't send a map open error out via SMTP */ 538 if ((OnlyOneError || QuickAbort) && 539 (OpMode == MD_SMTP || OpMode == MD_DAEMON)) 540 { 541 restore = true; 542 HoldErrs = true; 543 QuickAbort = false; 544 } 545 546 errno = 0; 547 if (map->map_class->map_open(map, O_RDONLY)) 548 { 549 if (tTd(38, 4)) 550 sm_dprintf("openmap()\t%s:%s %s: valid\n", 551 map->map_class->map_cname == NULL ? "NULL" : 552 map->map_class->map_cname, 553 map->map_mname == NULL ? "NULL" : 554 map->map_mname, 555 map->map_file == NULL ? "NULL" : 556 map->map_file); 557 map->map_mflags |= MF_OPEN; 558 map->map_pid = CurrentPid; 559 } 560 else 561 { 562 if (tTd(38, 4)) 563 sm_dprintf("openmap()\t%s:%s %s: invalid%s%s\n", 564 map->map_class->map_cname == NULL ? "NULL" : 565 map->map_class->map_cname, 566 map->map_mname == NULL ? "NULL" : 567 map->map_mname, 568 map->map_file == NULL ? "NULL" : 569 map->map_file, 570 errno == 0 ? "" : ": ", 571 errno == 0 ? "" : sm_errstring(errno)); 572 if (!bitset(MF_OPTIONAL, map->map_mflags)) 573 { 574 extern MAPCLASS BogusMapClass; 575 576 map->map_orgclass = map->map_class; 577 map->map_class = &BogusMapClass; 578 map->map_mflags |= MF_OPEN|MF_OPENBOGUS; 579 map->map_pid = CurrentPid; 580 } 581 else 582 { 583 /* don't try again */ 584 map->map_mflags &= ~MF_VALID; 585 } 586 } 587 588 if (restore) 589 { 590 Errors = saveerrors; 591 HoldErrs = savehold; 592 QuickAbort = savequick; 593 } 594 595 return bitset(MF_OPEN, map->map_mflags); 596 } 597 /* 598 ** CLOSEMAPS -- close all open maps opened by the current pid. 599 ** 600 ** Parameters: 601 ** bogus -- only close bogus maps. 602 ** 603 ** Returns: 604 ** none. 605 */ 606 607 void 608 closemaps(bogus) 609 bool bogus; 610 { 611 stabapply(map_close, bogus); 612 } 613 /* 614 ** MAP_CLOSE -- close a map opened by the current pid. 615 ** 616 ** Parameters: 617 ** s -- STAB entry: if map: try to close 618 ** bogus -- only close bogus maps or MCF_NOTPERSIST maps. 619 ** 620 ** Returns: 621 ** none. 622 */ 623 624 /* ARGSUSED1 */ 625 static void 626 map_close(s, bogus) 627 register STAB *s; 628 int bogus; /* int because of stabapply(), used as bool */ 629 { 630 MAP *map; 631 extern MAPCLASS BogusMapClass; 632 633 if (s->s_symtype != ST_MAP) 634 return; 635 636 map = &s->s_map; 637 638 /* 639 ** close the map iff: 640 ** it is valid and open and opened by this process 641 ** and (!bogus or it's a bogus map or it is not persistent) 642 ** negate this: return iff 643 ** it is not valid or it is not open or not opened by this process 644 ** or (bogus and it's not a bogus map and it's not not-persistent) 645 */ 646 647 if (!bitset(MF_VALID, map->map_mflags) || 648 !bitset(MF_OPEN, map->map_mflags) || 649 bitset(MF_CLOSING, map->map_mflags) || 650 map->map_pid != CurrentPid || 651 (bogus && map->map_class != &BogusMapClass && 652 !bitset(MCF_NOTPERSIST, map->map_class->map_cflags))) 653 return; 654 655 if (map->map_class == &BogusMapClass && map->map_orgclass != NULL && 656 map->map_orgclass != &BogusMapClass) 657 map->map_class = map->map_orgclass; 658 if (tTd(38, 5)) 659 sm_dprintf("closemaps: closing %s (%s)\n", 660 map->map_mname == NULL ? "NULL" : map->map_mname, 661 map->map_file == NULL ? "NULL" : map->map_file); 662 663 if (!bitset(MF_OPENBOGUS, map->map_mflags)) 664 { 665 map->map_mflags |= MF_CLOSING; 666 map->map_class->map_close(map); 667 } 668 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_OPENBOGUS|MF_CLOSING); 669 } 670 /* 671 ** GETCANONNAME -- look up name using service switch 672 ** 673 ** Parameters: 674 ** host -- the host name to look up. 675 ** hbsize -- the size of the host buffer. 676 ** trymx -- if set, try MX records. 677 ** pttl -- pointer to return TTL (can be NULL). 678 ** 679 ** Returns: 680 ** true -- if the host was found. 681 ** false -- otherwise. 682 */ 683 684 bool 685 getcanonname(host, hbsize, trymx, pttl) 686 char *host; 687 int hbsize; 688 bool trymx; 689 int *pttl; 690 { 691 int nmaps; 692 int mapno; 693 bool found = false; 694 bool got_tempfail = false; 695 auto int status; 696 char *maptype[MAXMAPSTACK]; 697 short mapreturn[MAXMAPACTIONS]; 698 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 699 bool should_try_nis_domain = false; 700 static char *nis_domain = NULL; 701 extern char *sun_init_domain(); 702 #endif 703 704 nmaps = switch_map_find("hosts", maptype, mapreturn); 705 if (pttl != 0) 706 *pttl = SM_DEFAULT_TTL; 707 for (mapno = 0; mapno < nmaps; mapno++) 708 { 709 int i; 710 711 if (tTd(38, 20)) 712 sm_dprintf("getcanonname(%s), trying %s\n", 713 host, maptype[mapno]); 714 if (strcmp("files", maptype[mapno]) == 0) 715 { 716 found = text_getcanonname(host, hbsize, &status); 717 } 718 #if NIS 719 else if (strcmp("nis", maptype[mapno]) == 0) 720 { 721 found = nis_getcanonname(host, hbsize, &status); 722 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 723 if (nis_domain == NULL) 724 nis_domain = sun_init_domain(); 725 # endif 726 } 727 #endif /* NIS */ 728 #if NISPLUS 729 else if (strcmp("nisplus", maptype[mapno]) == 0) 730 { 731 found = nisplus_getcanonname(host, hbsize, &status); 732 # if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 733 if (nis_domain == NULL) 734 nis_domain = sun_init_domain(); 735 # endif 736 } 737 #endif /* NISPLUS */ 738 #if NAMED_BIND 739 else if (strcmp("dns", maptype[mapno]) == 0) 740 { 741 found = dns_getcanonname(host, hbsize, trymx, &status, pttl); 742 } 743 #endif /* NAMED_BIND */ 744 #if NETINFO 745 else if (strcmp("netinfo", maptype[mapno]) == 0) 746 { 747 found = ni_getcanonname(host, hbsize, &status); 748 } 749 #endif /* NETINFO */ 750 else 751 { 752 found = false; 753 status = EX_UNAVAILABLE; 754 } 755 756 /* 757 ** Heuristic: if $m is not set, we are running during system 758 ** startup. In this case, when a name is apparently found 759 ** but has no dot, treat is as not found. This avoids 760 ** problems if /etc/hosts has no FQDN but is listed first 761 ** in the service switch. 762 */ 763 764 if (found && 765 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL)) 766 break; 767 768 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 769 if (found) 770 should_try_nis_domain = true; 771 /* but don't break, as we need to try all methods first */ 772 #endif 773 774 /* see if we should continue */ 775 if (status == EX_TEMPFAIL) 776 { 777 i = MA_TRYAGAIN; 778 got_tempfail = true; 779 } 780 else if (status == EX_NOTFOUND) 781 i = MA_NOTFOUND; 782 else 783 i = MA_UNAVAIL; 784 if (bitset(1 << mapno, mapreturn[i])) 785 break; 786 } 787 788 if (found) 789 { 790 char *d; 791 792 if (tTd(38, 20)) 793 sm_dprintf("getcanonname(%s), found\n", host); 794 795 /* 796 ** If returned name is still single token, compensate 797 ** by tagging on $m. This is because some sites set 798 ** up their DNS or NIS databases wrong. 799 */ 800 801 if ((d = strchr(host, '.')) == NULL || d[1] == '\0') 802 { 803 d = macvalue('m', CurEnv); 804 if (d != NULL && 805 hbsize > (int) (strlen(host) + strlen(d) + 1)) 806 { 807 if (host[strlen(host) - 1] != '.') 808 (void) sm_strlcat2(host, ".", d, 809 hbsize); 810 else 811 (void) sm_strlcat(host, d, hbsize); 812 } 813 else 814 { 815 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 816 if (VendorCode == VENDOR_SUN && 817 should_try_nis_domain) 818 { 819 goto try_nis_domain; 820 } 821 #endif 822 return false; 823 } 824 } 825 return true; 826 } 827 828 #if defined(SUN_EXTENSIONS) && defined(SUN_INIT_DOMAIN) 829 if (VendorCode == VENDOR_SUN && should_try_nis_domain) 830 { 831 try_nis_domain: 832 if (nis_domain != NULL && 833 strlen(nis_domain) + strlen(host) + 1 < hbsize) 834 { 835 (void) sm_strlcat2(host, ".", nis_domain, hbsize); 836 return true; 837 } 838 } 839 #endif 840 841 if (tTd(38, 20)) 842 sm_dprintf("getcanonname(%s), failed, status=%d\n", host, 843 status); 844 845 if (got_tempfail) 846 SM_SET_H_ERRNO(TRY_AGAIN); 847 else 848 SM_SET_H_ERRNO(HOST_NOT_FOUND); 849 850 return false; 851 } 852 /* 853 ** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry 854 ** 855 ** Parameters: 856 ** name -- the name against which to match. 857 ** dot -- where to reinsert '.' to get FQDN 858 ** line -- the /etc/hosts line. 859 ** cbuf -- the location to store the result. 860 ** cbuflen -- the size of cbuf. 861 ** 862 ** Returns: 863 ** true -- if the line matched the desired name. 864 ** false -- otherwise. 865 */ 866 867 static bool 868 extract_canonname(name, dot, line, cbuf, cbuflen) 869 char *name; 870 char *dot; 871 char *line; 872 char cbuf[]; 873 int cbuflen; 874 { 875 int i; 876 char *p; 877 bool found = false; 878 879 cbuf[0] = '\0'; 880 if (line[0] == '#') 881 return false; 882 883 for (i = 1; ; i++) 884 { 885 char nbuf[MAXNAME + 1]; 886 887 p = get_column(line, i, '\0', nbuf, sizeof nbuf); 888 if (p == NULL) 889 break; 890 if (*p == '\0') 891 continue; 892 if (cbuf[0] == '\0' || 893 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL)) 894 { 895 (void) sm_strlcpy(cbuf, p, cbuflen); 896 } 897 if (sm_strcasecmp(name, p) == 0) 898 found = true; 899 else if (dot != NULL) 900 { 901 /* try looking for the FQDN as well */ 902 *dot = '.'; 903 if (sm_strcasecmp(name, p) == 0) 904 found = true; 905 *dot = '\0'; 906 } 907 } 908 if (found && strchr(cbuf, '.') == NULL) 909 { 910 /* try to add a domain on the end of the name */ 911 char *domain = macvalue('m', CurEnv); 912 913 if (domain != NULL && 914 strlen(domain) + (i = strlen(cbuf)) + 1 < (size_t) cbuflen) 915 { 916 p = &cbuf[i]; 917 *p++ = '.'; 918 (void) sm_strlcpy(p, domain, cbuflen - i - 1); 919 } 920 } 921 return found; 922 } 923 924 /* 925 ** DNS modules 926 */ 927 928 #if NAMED_BIND 929 # if DNSMAP 930 931 # include "sm_resolve.h" 932 # if NETINET || NETINET6 933 # include <arpa/inet.h> 934 # endif /* NETINET || NETINET6 */ 935 936 /* 937 ** DNS_MAP_OPEN -- stub to check proper value for dns map type 938 */ 939 940 bool 941 dns_map_open(map, mode) 942 MAP *map; 943 int mode; 944 { 945 if (tTd(38,2)) 946 sm_dprintf("dns_map_open(%s, %d)\n", map->map_mname, mode); 947 948 mode &= O_ACCMODE; 949 if (mode != O_RDONLY) 950 { 951 /* issue a pseudo-error message */ 952 errno = SM_EMAPCANTWRITE; 953 return false; 954 } 955 return true; 956 } 957 958 /* 959 ** DNS_MAP_PARSEARGS -- parse dns map definition args. 960 ** 961 ** Parameters: 962 ** map -- pointer to MAP 963 ** args -- pointer to the args on the config line. 964 ** 965 ** Returns: 966 ** true -- if everything parsed OK. 967 ** false -- otherwise. 968 */ 969 970 # if _FFR_DNSMAP_MULTILIMIT 971 # if !_FFR_DNSMAP_MULTI 972 ERROR README: You must define _FFR_DNSMAP_MULTI to use _FFR_DNSMAP_MULTILIMIT 973 # endif /* ! _FFR_DNSMAP_MULTI */ 974 # endif /* _FFR_DNSMAP_MULTILIMIT */ 975 976 # if _FFR_DNSMAP_MULTI 977 # if _FFR_DNSMAP_MULTILIMIT 978 # define map_sizelimit map_lockfd /* overload field */ 979 # endif /* _FFR_DNSMAP_MULTILIMIT */ 980 # endif /* _FFR_DNSMAP_MULTI */ 981 982 struct dns_map 983 { 984 int dns_m_type; 985 }; 986 987 bool 988 dns_map_parseargs(map,args) 989 MAP *map; 990 char *args; 991 { 992 register char *p = args; 993 struct dns_map *map_p; 994 995 map_p = (struct dns_map *) xalloc(sizeof *map_p); 996 map_p->dns_m_type = -1; 997 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 998 999 for (;;) 1000 { 1001 while (isascii(*p) && isspace(*p)) 1002 p++; 1003 if (*p != '-') 1004 break; 1005 switch (*++p) 1006 { 1007 case 'N': 1008 map->map_mflags |= MF_INCLNULL; 1009 map->map_mflags &= ~MF_TRY0NULL; 1010 break; 1011 1012 case 'O': 1013 map->map_mflags &= ~MF_TRY1NULL; 1014 break; 1015 1016 case 'o': 1017 map->map_mflags |= MF_OPTIONAL; 1018 break; 1019 1020 case 'f': 1021 map->map_mflags |= MF_NOFOLDCASE; 1022 break; 1023 1024 case 'm': 1025 map->map_mflags |= MF_MATCHONLY; 1026 break; 1027 1028 case 'A': 1029 map->map_mflags |= MF_APPEND; 1030 break; 1031 1032 case 'q': 1033 map->map_mflags |= MF_KEEPQUOTES; 1034 break; 1035 1036 case 't': 1037 map->map_mflags |= MF_NODEFER; 1038 break; 1039 1040 case 'a': 1041 map->map_app = ++p; 1042 break; 1043 1044 case 'T': 1045 map->map_tapp = ++p; 1046 break; 1047 1048 case 'd': 1049 { 1050 char *h; 1051 1052 ++p; 1053 h = strchr(p, ' '); 1054 if (h != NULL) 1055 *h = '\0'; 1056 map->map_timeout = convtime(p, 's'); 1057 if (h != NULL) 1058 *h = ' '; 1059 } 1060 break; 1061 1062 case 'r': 1063 while (isascii(*++p) && isspace(*p)) 1064 continue; 1065 map->map_retry = atoi(p); 1066 break; 1067 1068 # if _FFR_DNSMAP_MULTI 1069 case 'z': 1070 if (*++p != '\\') 1071 map->map_coldelim = *p; 1072 else 1073 { 1074 switch (*++p) 1075 { 1076 case 'n': 1077 map->map_coldelim = '\n'; 1078 break; 1079 1080 case 't': 1081 map->map_coldelim = '\t'; 1082 break; 1083 1084 default: 1085 map->map_coldelim = '\\'; 1086 } 1087 } 1088 break; 1089 1090 # if _FFR_DNSMAP_MULTILIMIT 1091 case 'Z': 1092 while (isascii(*++p) && isspace(*p)) 1093 continue; 1094 map->map_sizelimit = atoi(p); 1095 break; 1096 # endif /* _FFR_DNSMAP_MULTILIMIT */ 1097 # endif /* _FFR_DNSMAP_MULTI */ 1098 1099 /* Start of dns_map specific args */ 1100 case 'R': /* search field */ 1101 { 1102 char *h; 1103 1104 while (isascii(*++p) && isspace(*p)) 1105 continue; 1106 h = strchr(p, ' '); 1107 if (h != NULL) 1108 *h = '\0'; 1109 map_p->dns_m_type = dns_string_to_type(p); 1110 if (h != NULL) 1111 *h = ' '; 1112 if (map_p->dns_m_type < 0) 1113 syserr("dns map %s: wrong type %s", 1114 map->map_mname, p); 1115 } 1116 break; 1117 1118 # if _FFR_DNSMAP_BASE 1119 case 'B': /* base domain */ 1120 { 1121 char *h; 1122 1123 while (isascii(*++p) && isspace(*p)) 1124 continue; 1125 h = strchr(p, ' '); 1126 if (h != NULL) 1127 *h = '\0'; 1128 1129 /* 1130 ** slight abuse of map->map_file; it isn't 1131 ** used otherwise in this map type. 1132 */ 1133 1134 map->map_file = newstr(p); 1135 if (h != NULL) 1136 *h = ' '; 1137 } 1138 break; 1139 # endif /* _FFR_DNSMAP_BASE */ 1140 1141 } 1142 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1143 p++; 1144 if (*p != '\0') 1145 *p++ = '\0'; 1146 } 1147 if (map_p->dns_m_type < 0) 1148 syserr("dns map %s: missing -R type", map->map_mname); 1149 if (map->map_app != NULL) 1150 map->map_app = newstr(map->map_app); 1151 if (map->map_tapp != NULL) 1152 map->map_tapp = newstr(map->map_tapp); 1153 1154 /* 1155 ** Assumption: assert(sizeof int <= sizeof(ARBPTR_T)); 1156 ** Even if this assumption is wrong, we use only one byte, 1157 ** so it doesn't really matter. 1158 */ 1159 1160 map->map_db1 = (ARBPTR_T) map_p; 1161 return true; 1162 } 1163 1164 /* 1165 ** DNS_MAP_LOOKUP -- perform dns map lookup. 1166 ** 1167 ** Parameters: 1168 ** map -- pointer to MAP 1169 ** name -- name to lookup 1170 ** av -- arguments to interpolate into buf. 1171 ** statp -- pointer to status (EX_) 1172 ** 1173 ** Returns: 1174 ** result of lookup if succeeded. 1175 ** NULL -- otherwise. 1176 */ 1177 1178 char * 1179 dns_map_lookup(map, name, av, statp) 1180 MAP *map; 1181 char *name; 1182 char **av; 1183 int *statp; 1184 { 1185 # if _FFR_DNSMAP_MULTI 1186 # if _FFR_DNSMAP_MULTILIMIT 1187 int resnum = 0; 1188 # endif /* _FFR_DNSMAP_MULTILIMIT */ 1189 # endif /* _FFR_DNSMAP_MULTI */ 1190 char *vp = NULL, *result = NULL; 1191 size_t vsize; 1192 struct dns_map *map_p; 1193 RESOURCE_RECORD_T *rr = NULL; 1194 DNS_REPLY_T *r = NULL; 1195 # if NETINET6 1196 static char buf6[INET6_ADDRSTRLEN]; 1197 # endif /* NETINET6 */ 1198 1199 if (tTd(38, 20)) 1200 sm_dprintf("dns_map_lookup(%s, %s)\n", 1201 map->map_mname, name); 1202 1203 map_p = (struct dns_map *)(map->map_db1); 1204 # if _FFR_DNSMAP_BASE 1205 if (map->map_file != NULL && *map->map_file != '\0') 1206 { 1207 size_t len; 1208 char *appdomain; 1209 1210 len = strlen(map->map_file) + strlen(name) + 2; 1211 appdomain = (char *) sm_malloc(len); 1212 if (appdomain == NULL) 1213 { 1214 *statp = EX_UNAVAILABLE; 1215 return NULL; 1216 } 1217 (void) sm_strlcpyn(appdomain, len, 3, name, ".", map->map_file); 1218 r = dns_lookup_int(appdomain, C_IN, map_p->dns_m_type, 1219 map->map_timeout, map->map_retry); 1220 sm_free(appdomain); 1221 } 1222 else 1223 # endif /* _FFR_DNSMAP_BASE */ 1224 { 1225 r = dns_lookup_int(name, C_IN, map_p->dns_m_type, 1226 map->map_timeout, map->map_retry); 1227 } 1228 1229 if (r == NULL) 1230 { 1231 result = NULL; 1232 if (h_errno == TRY_AGAIN || transienterror(errno)) 1233 *statp = EX_TEMPFAIL; 1234 else 1235 *statp = EX_NOTFOUND; 1236 goto cleanup; 1237 } 1238 *statp = EX_OK; 1239 for (rr = r->dns_r_head; rr != NULL; rr = rr->rr_next) 1240 { 1241 char *type = NULL; 1242 char *value = NULL; 1243 1244 switch (rr->rr_type) 1245 { 1246 case T_NS: 1247 type = "T_NS"; 1248 value = rr->rr_u.rr_txt; 1249 break; 1250 case T_CNAME: 1251 type = "T_CNAME"; 1252 value = rr->rr_u.rr_txt; 1253 break; 1254 case T_AFSDB: 1255 type = "T_AFSDB"; 1256 value = rr->rr_u.rr_mx->mx_r_domain; 1257 break; 1258 case T_SRV: 1259 type = "T_SRV"; 1260 value = rr->rr_u.rr_srv->srv_r_target; 1261 break; 1262 case T_PTR: 1263 type = "T_PTR"; 1264 value = rr->rr_u.rr_txt; 1265 break; 1266 case T_TXT: 1267 type = "T_TXT"; 1268 value = rr->rr_u.rr_txt; 1269 break; 1270 case T_MX: 1271 type = "T_MX"; 1272 value = rr->rr_u.rr_mx->mx_r_domain; 1273 break; 1274 # if NETINET 1275 case T_A: 1276 type = "T_A"; 1277 value = inet_ntoa(*(rr->rr_u.rr_a)); 1278 break; 1279 # endif /* NETINET */ 1280 # if NETINET6 1281 case T_AAAA: 1282 type = "T_AAAA"; 1283 value = anynet_ntop(rr->rr_u.rr_aaaa, buf6, 1284 sizeof buf6); 1285 break; 1286 # endif /* NETINET6 */ 1287 } 1288 1289 (void) strreplnonprt(value, 'X'); 1290 if (map_p->dns_m_type != rr->rr_type) 1291 { 1292 if (tTd(38, 40)) 1293 sm_dprintf("\tskipping type %s (%d) value %s\n", 1294 type != NULL ? type : "<UNKNOWN>", 1295 rr->rr_type, 1296 value != NULL ? value : "<NO VALUE>"); 1297 continue; 1298 } 1299 1300 # if NETINET6 1301 if (rr->rr_type == T_AAAA && value == NULL) 1302 { 1303 result = NULL; 1304 *statp = EX_DATAERR; 1305 if (tTd(38, 40)) 1306 sm_dprintf("\tbad T_AAAA conversion\n"); 1307 goto cleanup; 1308 } 1309 # endif /* NETINET6 */ 1310 if (tTd(38, 40)) 1311 sm_dprintf("\tfound type %s (%d) value %s\n", 1312 type != NULL ? type : "<UNKNOWN>", 1313 rr->rr_type, 1314 value != NULL ? value : "<NO VALUE>"); 1315 # if _FFR_DNSMAP_MULTI 1316 if (value != NULL && 1317 (map->map_coldelim == '\0' || 1318 # if _FFR_DNSMAP_MULTILIMIT 1319 map->map_sizelimit == 1 || 1320 # endif /* _FFR_DNSMAP_MULTILIMIT */ 1321 bitset(MF_MATCHONLY, map->map_mflags))) 1322 { 1323 /* Only care about the first match */ 1324 vp = newstr(value); 1325 break; 1326 } 1327 else if (vp == NULL) 1328 { 1329 /* First result */ 1330 vp = newstr(value); 1331 } 1332 else 1333 { 1334 /* concatenate the results */ 1335 int sz; 1336 char *new; 1337 1338 sz = strlen(vp) + strlen(value) + 2; 1339 new = xalloc(sz); 1340 (void) sm_snprintf(new, sz, "%s%c%s", 1341 vp, map->map_coldelim, value); 1342 sm_free(vp); 1343 vp = new; 1344 # if _FFR_DNSMAP_MULTILIMIT 1345 if (map->map_sizelimit > 0 && 1346 ++resnum >= map->map_sizelimit) 1347 break; 1348 # endif /* _FFR_DNSMAP_MULTILIMIT */ 1349 } 1350 # else /* _FFR_DNSMAP_MULTI */ 1351 vp = value; 1352 break; 1353 # endif /* _FFR_DNSMAP_MULTI */ 1354 } 1355 if (vp == NULL) 1356 { 1357 result = NULL; 1358 *statp = EX_NOTFOUND; 1359 if (tTd(38, 40)) 1360 sm_dprintf("\tno match found\n"); 1361 goto cleanup; 1362 } 1363 1364 # if _FFR_DNSMAP_MULTI 1365 /* Cleanly truncate for rulesets */ 1366 truncate_at_delim(vp, PSBUFSIZE / 2, map->map_coldelim); 1367 # endif /* _FFR_DNSMAP_MULTI */ 1368 1369 vsize = strlen(vp); 1370 1371 if (LogLevel > 9) 1372 sm_syslog(LOG_INFO, CurEnv->e_id, "dns %.100s => %s", 1373 name, vp); 1374 if (bitset(MF_MATCHONLY, map->map_mflags)) 1375 result = map_rewrite(map, name, strlen(name), NULL); 1376 else 1377 result = map_rewrite(map, vp, vsize, av); 1378 1379 cleanup: 1380 # if _FFR_DNSMAP_MULTI 1381 if (vp != NULL) 1382 sm_free(vp); 1383 # endif /* _FFR_DNSMAP_MULTI */ 1384 if (r != NULL) 1385 dns_free_data(r); 1386 return result; 1387 } 1388 # endif /* DNSMAP */ 1389 #endif /* NAMED_BIND */ 1390 1391 /* 1392 ** NDBM modules 1393 */ 1394 1395 #if NDBM 1396 1397 /* 1398 ** NDBM_MAP_OPEN -- DBM-style map open 1399 */ 1400 1401 bool 1402 ndbm_map_open(map, mode) 1403 MAP *map; 1404 int mode; 1405 { 1406 register DBM *dbm; 1407 int save_errno; 1408 int dfd; 1409 int pfd; 1410 long sff; 1411 int ret; 1412 int smode = S_IREAD; 1413 char dirfile[MAXPATHLEN]; 1414 char pagfile[MAXPATHLEN]; 1415 struct stat st; 1416 struct stat std, stp; 1417 1418 if (tTd(38, 2)) 1419 sm_dprintf("ndbm_map_open(%s, %s, %d)\n", 1420 map->map_mname, map->map_file, mode); 1421 map->map_lockfd = -1; 1422 mode &= O_ACCMODE; 1423 1424 /* do initial file and directory checks */ 1425 if (sm_strlcpyn(dirfile, sizeof dirfile, 2, 1426 map->map_file, ".dir") >= sizeof dirfile || 1427 sm_strlcpyn(pagfile, sizeof pagfile, 2, 1428 map->map_file, ".pag") >= sizeof pagfile) 1429 { 1430 errno = 0; 1431 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1432 syserr("dbm map \"%s\": map file %s name too long", 1433 map->map_mname, map->map_file); 1434 return false; 1435 } 1436 sff = SFF_ROOTOK|SFF_REGONLY; 1437 if (mode == O_RDWR) 1438 { 1439 sff |= SFF_CREAT; 1440 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 1441 sff |= SFF_NOSLINK; 1442 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 1443 sff |= SFF_NOHLINK; 1444 smode = S_IWRITE; 1445 } 1446 else 1447 { 1448 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 1449 sff |= SFF_NOWLINK; 1450 } 1451 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 1452 sff |= SFF_SAFEDIRPATH; 1453 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName, 1454 sff, smode, &std); 1455 if (ret == 0) 1456 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName, 1457 sff, smode, &stp); 1458 1459 if (ret != 0) 1460 { 1461 char *prob = "unsafe"; 1462 1463 /* cannot open this map */ 1464 if (ret == ENOENT) 1465 prob = "missing"; 1466 if (tTd(38, 2)) 1467 sm_dprintf("\t%s map file: %d\n", prob, ret); 1468 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1469 syserr("dbm map \"%s\": %s map file %s", 1470 map->map_mname, prob, map->map_file); 1471 return false; 1472 } 1473 if (std.st_mode == ST_MODE_NOFILE) 1474 mode |= O_CREAT|O_EXCL; 1475 1476 # if LOCK_ON_OPEN 1477 if (mode == O_RDONLY) 1478 mode |= O_SHLOCK; 1479 else 1480 mode |= O_TRUNC|O_EXLOCK; 1481 # else /* LOCK_ON_OPEN */ 1482 if ((mode & O_ACCMODE) == O_RDWR) 1483 { 1484 # if NOFTRUNCATE 1485 /* 1486 ** Warning: race condition. Try to lock the file as 1487 ** quickly as possible after opening it. 1488 ** This may also have security problems on some systems, 1489 ** but there isn't anything we can do about it. 1490 */ 1491 1492 mode |= O_TRUNC; 1493 # else /* NOFTRUNCATE */ 1494 /* 1495 ** This ugly code opens the map without truncating it, 1496 ** locks the file, then truncates it. Necessary to 1497 ** avoid race conditions. 1498 */ 1499 1500 int dirfd; 1501 int pagfd; 1502 long sff = SFF_CREAT|SFF_OPENASROOT; 1503 1504 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 1505 sff |= SFF_NOSLINK; 1506 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 1507 sff |= SFF_NOHLINK; 1508 1509 dirfd = safeopen(dirfile, mode, DBMMODE, sff); 1510 pagfd = safeopen(pagfile, mode, DBMMODE, sff); 1511 1512 if (dirfd < 0 || pagfd < 0) 1513 { 1514 save_errno = errno; 1515 if (dirfd >= 0) 1516 (void) close(dirfd); 1517 if (pagfd >= 0) 1518 (void) close(pagfd); 1519 errno = save_errno; 1520 syserr("ndbm_map_open: cannot create database %s", 1521 map->map_file); 1522 return false; 1523 } 1524 if (ftruncate(dirfd, (off_t) 0) < 0 || 1525 ftruncate(pagfd, (off_t) 0) < 0) 1526 { 1527 save_errno = errno; 1528 (void) close(dirfd); 1529 (void) close(pagfd); 1530 errno = save_errno; 1531 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}", 1532 map->map_file); 1533 return false; 1534 } 1535 1536 /* if new file, get "before" bits for later filechanged check */ 1537 if (std.st_mode == ST_MODE_NOFILE && 1538 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0)) 1539 { 1540 save_errno = errno; 1541 (void) close(dirfd); 1542 (void) close(pagfd); 1543 errno = save_errno; 1544 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file", 1545 map->map_file); 1546 return false; 1547 } 1548 1549 /* have to save the lock for the duration (bletch) */ 1550 map->map_lockfd = dirfd; 1551 (void) close(pagfd); 1552 1553 /* twiddle bits for dbm_open */ 1554 mode &= ~(O_CREAT|O_EXCL); 1555 # endif /* NOFTRUNCATE */ 1556 } 1557 # endif /* LOCK_ON_OPEN */ 1558 1559 /* open the database */ 1560 dbm = dbm_open(map->map_file, mode, DBMMODE); 1561 if (dbm == NULL) 1562 { 1563 save_errno = errno; 1564 if (bitset(MF_ALIAS, map->map_mflags) && 1565 aliaswait(map, ".pag", false)) 1566 return true; 1567 # if !LOCK_ON_OPEN && !NOFTRUNCATE 1568 if (map->map_lockfd >= 0) 1569 (void) close(map->map_lockfd); 1570 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */ 1571 errno = save_errno; 1572 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1573 syserr("Cannot open DBM database %s", map->map_file); 1574 return false; 1575 } 1576 dfd = dbm_dirfno(dbm); 1577 pfd = dbm_pagfno(dbm); 1578 if (dfd == pfd) 1579 { 1580 /* heuristic: if files are linked, this is actually gdbm */ 1581 dbm_close(dbm); 1582 # if !LOCK_ON_OPEN && !NOFTRUNCATE 1583 if (map->map_lockfd >= 0) 1584 (void) close(map->map_lockfd); 1585 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */ 1586 errno = 0; 1587 syserr("dbm map \"%s\": cannot support GDBM", 1588 map->map_mname); 1589 return false; 1590 } 1591 1592 if (filechanged(dirfile, dfd, &std) || 1593 filechanged(pagfile, pfd, &stp)) 1594 { 1595 save_errno = errno; 1596 dbm_close(dbm); 1597 # if !LOCK_ON_OPEN && !NOFTRUNCATE 1598 if (map->map_lockfd >= 0) 1599 (void) close(map->map_lockfd); 1600 # endif /* !LOCK_ON_OPEN && !NOFTRUNCATE */ 1601 errno = save_errno; 1602 syserr("ndbm_map_open(%s): file changed after open", 1603 map->map_file); 1604 return false; 1605 } 1606 1607 map->map_db1 = (ARBPTR_T) dbm; 1608 1609 /* 1610 ** Need to set map_mtime before the call to aliaswait() 1611 ** as aliaswait() will call map_lookup() which requires 1612 ** map_mtime to be set 1613 */ 1614 1615 if (fstat(pfd, &st) >= 0) 1616 map->map_mtime = st.st_mtime; 1617 1618 if (mode == O_RDONLY) 1619 { 1620 # if LOCK_ON_OPEN 1621 if (dfd >= 0) 1622 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); 1623 if (pfd >= 0) 1624 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN); 1625 # endif /* LOCK_ON_OPEN */ 1626 if (bitset(MF_ALIAS, map->map_mflags) && 1627 !aliaswait(map, ".pag", true)) 1628 return false; 1629 } 1630 else 1631 { 1632 map->map_mflags |= MF_LOCKED; 1633 if (geteuid() == 0 && TrustedUid != 0) 1634 { 1635 # if HASFCHOWN 1636 if (fchown(dfd, TrustedUid, -1) < 0 || 1637 fchown(pfd, TrustedUid, -1) < 0) 1638 { 1639 int err = errno; 1640 1641 sm_syslog(LOG_ALERT, NOQID, 1642 "ownership change on %s failed: %s", 1643 map->map_file, sm_errstring(err)); 1644 message("050 ownership change on %s failed: %s", 1645 map->map_file, sm_errstring(err)); 1646 } 1647 # else /* HASFCHOWN */ 1648 sm_syslog(LOG_ALERT, NOQID, 1649 "no fchown(): cannot change ownership on %s", 1650 map->map_file); 1651 message("050 no fchown(): cannot change ownership on %s", 1652 map->map_file); 1653 # endif /* HASFCHOWN */ 1654 } 1655 } 1656 return true; 1657 } 1658 1659 1660 /* 1661 ** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map 1662 */ 1663 1664 char * 1665 ndbm_map_lookup(map, name, av, statp) 1666 MAP *map; 1667 char *name; 1668 char **av; 1669 int *statp; 1670 { 1671 datum key, val; 1672 int dfd, pfd; 1673 char keybuf[MAXNAME + 1]; 1674 struct stat stbuf; 1675 1676 if (tTd(38, 20)) 1677 sm_dprintf("ndbm_map_lookup(%s, %s)\n", 1678 map->map_mname, name); 1679 1680 key.dptr = name; 1681 key.dsize = strlen(name); 1682 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 1683 { 1684 if (key.dsize > sizeof keybuf - 1) 1685 key.dsize = sizeof keybuf - 1; 1686 memmove(keybuf, key.dptr, key.dsize); 1687 keybuf[key.dsize] = '\0'; 1688 makelower(keybuf); 1689 key.dptr = keybuf; 1690 } 1691 lockdbm: 1692 dfd = dbm_dirfno((DBM *) map->map_db1); 1693 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1694 (void) lockfile(dfd, map->map_file, ".dir", LOCK_SH); 1695 pfd = dbm_pagfno((DBM *) map->map_db1); 1696 if (pfd < 0 || fstat(pfd, &stbuf) < 0 || 1697 stbuf.st_mtime > map->map_mtime) 1698 { 1699 /* Reopen the database to sync the cache */ 1700 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR 1701 : O_RDONLY; 1702 1703 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1704 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); 1705 map->map_mflags |= MF_CLOSING; 1706 map->map_class->map_close(map); 1707 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 1708 if (map->map_class->map_open(map, omode)) 1709 { 1710 map->map_mflags |= MF_OPEN; 1711 map->map_pid = CurrentPid; 1712 if ((omode && O_ACCMODE) == O_RDWR) 1713 map->map_mflags |= MF_WRITABLE; 1714 goto lockdbm; 1715 } 1716 else 1717 { 1718 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1719 { 1720 extern MAPCLASS BogusMapClass; 1721 1722 *statp = EX_TEMPFAIL; 1723 map->map_orgclass = map->map_class; 1724 map->map_class = &BogusMapClass; 1725 map->map_mflags |= MF_OPEN; 1726 map->map_pid = CurrentPid; 1727 syserr("Cannot reopen NDBM database %s", 1728 map->map_file); 1729 } 1730 return NULL; 1731 } 1732 } 1733 val.dptr = NULL; 1734 if (bitset(MF_TRY0NULL, map->map_mflags)) 1735 { 1736 val = dbm_fetch((DBM *) map->map_db1, key); 1737 if (val.dptr != NULL) 1738 map->map_mflags &= ~MF_TRY1NULL; 1739 } 1740 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags)) 1741 { 1742 key.dsize++; 1743 val = dbm_fetch((DBM *) map->map_db1, key); 1744 if (val.dptr != NULL) 1745 map->map_mflags &= ~MF_TRY0NULL; 1746 } 1747 if (dfd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1748 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); 1749 if (val.dptr == NULL) 1750 return NULL; 1751 if (bitset(MF_MATCHONLY, map->map_mflags)) 1752 return map_rewrite(map, name, strlen(name), NULL); 1753 else 1754 return map_rewrite(map, val.dptr, val.dsize, av); 1755 } 1756 1757 1758 /* 1759 ** NDBM_MAP_STORE -- store a datum in the database 1760 */ 1761 1762 void 1763 ndbm_map_store(map, lhs, rhs) 1764 register MAP *map; 1765 char *lhs; 1766 char *rhs; 1767 { 1768 datum key; 1769 datum data; 1770 int status; 1771 char keybuf[MAXNAME + 1]; 1772 1773 if (tTd(38, 12)) 1774 sm_dprintf("ndbm_map_store(%s, %s, %s)\n", 1775 map->map_mname, lhs, rhs); 1776 1777 key.dsize = strlen(lhs); 1778 key.dptr = lhs; 1779 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 1780 { 1781 if (key.dsize > sizeof keybuf - 1) 1782 key.dsize = sizeof keybuf - 1; 1783 memmove(keybuf, key.dptr, key.dsize); 1784 keybuf[key.dsize] = '\0'; 1785 makelower(keybuf); 1786 key.dptr = keybuf; 1787 } 1788 1789 data.dsize = strlen(rhs); 1790 data.dptr = rhs; 1791 1792 if (bitset(MF_INCLNULL, map->map_mflags)) 1793 { 1794 key.dsize++; 1795 data.dsize++; 1796 } 1797 1798 status = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT); 1799 if (status > 0) 1800 { 1801 if (!bitset(MF_APPEND, map->map_mflags)) 1802 message("050 Warning: duplicate alias name %s", lhs); 1803 else 1804 { 1805 static char *buf = NULL; 1806 static int bufsiz = 0; 1807 auto int xstat; 1808 datum old; 1809 1810 old.dptr = ndbm_map_lookup(map, key.dptr, 1811 (char **) NULL, &xstat); 1812 if (old.dptr != NULL && *(char *) old.dptr != '\0') 1813 { 1814 old.dsize = strlen(old.dptr); 1815 if (data.dsize + old.dsize + 2 > bufsiz) 1816 { 1817 if (buf != NULL) 1818 (void) sm_free(buf); 1819 bufsiz = data.dsize + old.dsize + 2; 1820 buf = sm_pmalloc_x(bufsiz); 1821 } 1822 (void) sm_strlcpyn(buf, bufsiz, 3, 1823 data.dptr, ",", old.dptr); 1824 data.dsize = data.dsize + old.dsize + 1; 1825 data.dptr = buf; 1826 if (tTd(38, 9)) 1827 sm_dprintf("ndbm_map_store append=%s\n", 1828 data.dptr); 1829 } 1830 } 1831 status = dbm_store((DBM *) map->map_db1, 1832 key, data, DBM_REPLACE); 1833 } 1834 if (status != 0) 1835 syserr("readaliases: dbm put (%s): %d", lhs, status); 1836 } 1837 1838 1839 /* 1840 ** NDBM_MAP_CLOSE -- close the database 1841 */ 1842 1843 void 1844 ndbm_map_close(map) 1845 register MAP *map; 1846 { 1847 if (tTd(38, 9)) 1848 sm_dprintf("ndbm_map_close(%s, %s, %lx)\n", 1849 map->map_mname, map->map_file, map->map_mflags); 1850 1851 if (bitset(MF_WRITABLE, map->map_mflags)) 1852 { 1853 # ifdef NDBM_YP_COMPAT 1854 bool inclnull; 1855 char buf[MAXHOSTNAMELEN]; 1856 1857 inclnull = bitset(MF_INCLNULL, map->map_mflags); 1858 map->map_mflags &= ~MF_INCLNULL; 1859 1860 if (strstr(map->map_file, "/yp/") != NULL) 1861 { 1862 long save_mflags = map->map_mflags; 1863 1864 map->map_mflags |= MF_NOFOLDCASE; 1865 1866 (void) sm_snprintf(buf, sizeof buf, "%010ld", curtime()); 1867 ndbm_map_store(map, "YP_LAST_MODIFIED", buf); 1868 1869 (void) gethostname(buf, sizeof buf); 1870 ndbm_map_store(map, "YP_MASTER_NAME", buf); 1871 1872 map->map_mflags = save_mflags; 1873 } 1874 1875 if (inclnull) 1876 map->map_mflags |= MF_INCLNULL; 1877 # endif /* NDBM_YP_COMPAT */ 1878 1879 /* write out the distinguished alias */ 1880 ndbm_map_store(map, "@", "@"); 1881 } 1882 dbm_close((DBM *) map->map_db1); 1883 1884 /* release lock (if needed) */ 1885 # if !LOCK_ON_OPEN 1886 if (map->map_lockfd >= 0) 1887 (void) close(map->map_lockfd); 1888 # endif /* !LOCK_ON_OPEN */ 1889 } 1890 1891 #endif /* NDBM */ 1892 /* 1893 ** NEWDB (Hash and BTree) Modules 1894 */ 1895 1896 #if NEWDB 1897 1898 /* 1899 ** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives. 1900 ** 1901 ** These do rather bizarre locking. If you can lock on open, 1902 ** do that to avoid the condition of opening a database that 1903 ** is being rebuilt. If you don't, we'll try to fake it, but 1904 ** there will be a race condition. If opening for read-only, 1905 ** we immediately release the lock to avoid freezing things up. 1906 ** We really ought to hold the lock, but guarantee that we won't 1907 ** be pokey about it. That's hard to do. 1908 */ 1909 1910 /* these should be K line arguments */ 1911 # if DB_VERSION_MAJOR < 2 1912 # define db_cachesize cachesize 1913 # define h_nelem nelem 1914 # ifndef DB_CACHE_SIZE 1915 # define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */ 1916 # endif /* ! DB_CACHE_SIZE */ 1917 # ifndef DB_HASH_NELEM 1918 # define DB_HASH_NELEM 4096 /* (starting) size of hash table */ 1919 # endif /* ! DB_HASH_NELEM */ 1920 # endif /* DB_VERSION_MAJOR < 2 */ 1921 1922 bool 1923 bt_map_open(map, mode) 1924 MAP *map; 1925 int mode; 1926 { 1927 # if DB_VERSION_MAJOR < 2 1928 BTREEINFO btinfo; 1929 # endif /* DB_VERSION_MAJOR < 2 */ 1930 # if DB_VERSION_MAJOR == 2 1931 DB_INFO btinfo; 1932 # endif /* DB_VERSION_MAJOR == 2 */ 1933 # if DB_VERSION_MAJOR > 2 1934 void *btinfo = NULL; 1935 # endif /* DB_VERSION_MAJOR > 2 */ 1936 1937 if (tTd(38, 2)) 1938 sm_dprintf("bt_map_open(%s, %s, %d)\n", 1939 map->map_mname, map->map_file, mode); 1940 1941 # if DB_VERSION_MAJOR < 3 1942 memset(&btinfo, '\0', sizeof btinfo); 1943 # ifdef DB_CACHE_SIZE 1944 btinfo.db_cachesize = DB_CACHE_SIZE; 1945 # endif /* DB_CACHE_SIZE */ 1946 # endif /* DB_VERSION_MAJOR < 3 */ 1947 1948 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo); 1949 } 1950 1951 bool 1952 hash_map_open(map, mode) 1953 MAP *map; 1954 int mode; 1955 { 1956 # if DB_VERSION_MAJOR < 2 1957 HASHINFO hinfo; 1958 # endif /* DB_VERSION_MAJOR < 2 */ 1959 # if DB_VERSION_MAJOR == 2 1960 DB_INFO hinfo; 1961 # endif /* DB_VERSION_MAJOR == 2 */ 1962 # if DB_VERSION_MAJOR > 2 1963 void *hinfo = NULL; 1964 # endif /* DB_VERSION_MAJOR > 2 */ 1965 1966 if (tTd(38, 2)) 1967 sm_dprintf("hash_map_open(%s, %s, %d)\n", 1968 map->map_mname, map->map_file, mode); 1969 1970 # if DB_VERSION_MAJOR < 3 1971 memset(&hinfo, '\0', sizeof hinfo); 1972 # ifdef DB_HASH_NELEM 1973 hinfo.h_nelem = DB_HASH_NELEM; 1974 # endif /* DB_HASH_NELEM */ 1975 # ifdef DB_CACHE_SIZE 1976 hinfo.db_cachesize = DB_CACHE_SIZE; 1977 # endif /* DB_CACHE_SIZE */ 1978 # endif /* DB_VERSION_MAJOR < 3 */ 1979 1980 return db_map_open(map, mode, "hash", DB_HASH, &hinfo); 1981 } 1982 1983 static bool 1984 db_map_open(map, mode, mapclassname, dbtype, openinfo) 1985 MAP *map; 1986 int mode; 1987 char *mapclassname; 1988 DBTYPE dbtype; 1989 # if DB_VERSION_MAJOR < 2 1990 const void *openinfo; 1991 # endif /* DB_VERSION_MAJOR < 2 */ 1992 # if DB_VERSION_MAJOR == 2 1993 DB_INFO *openinfo; 1994 # endif /* DB_VERSION_MAJOR == 2 */ 1995 # if DB_VERSION_MAJOR > 2 1996 void **openinfo; 1997 # endif /* DB_VERSION_MAJOR > 2 */ 1998 { 1999 DB *db = NULL; 2000 int i; 2001 int omode; 2002 int smode = S_IREAD; 2003 int fd; 2004 long sff; 2005 int save_errno; 2006 struct stat st; 2007 char buf[MAXPATHLEN]; 2008 2009 /* do initial file and directory checks */ 2010 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf) 2011 { 2012 errno = 0; 2013 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2014 syserr("map \"%s\": map file %s name too long", 2015 map->map_mname, map->map_file); 2016 return false; 2017 } 2018 i = strlen(buf); 2019 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0) 2020 { 2021 if (sm_strlcat(buf, ".db", sizeof buf) >= sizeof buf) 2022 { 2023 errno = 0; 2024 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2025 syserr("map \"%s\": map file %s name too long", 2026 map->map_mname, map->map_file); 2027 return false; 2028 } 2029 } 2030 2031 mode &= O_ACCMODE; 2032 omode = mode; 2033 2034 sff = SFF_ROOTOK|SFF_REGONLY; 2035 if (mode == O_RDWR) 2036 { 2037 sff |= SFF_CREAT; 2038 if (!bitnset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 2039 sff |= SFF_NOSLINK; 2040 if (!bitnset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 2041 sff |= SFF_NOHLINK; 2042 smode = S_IWRITE; 2043 } 2044 else 2045 { 2046 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 2047 sff |= SFF_NOWLINK; 2048 } 2049 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 2050 sff |= SFF_SAFEDIRPATH; 2051 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st); 2052 2053 if (i != 0) 2054 { 2055 char *prob = "unsafe"; 2056 2057 /* cannot open this map */ 2058 if (i == ENOENT) 2059 prob = "missing"; 2060 if (tTd(38, 2)) 2061 sm_dprintf("\t%s map file: %s\n", prob, sm_errstring(i)); 2062 errno = i; 2063 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2064 syserr("%s map \"%s\": %s map file %s", 2065 mapclassname, map->map_mname, prob, buf); 2066 return false; 2067 } 2068 if (st.st_mode == ST_MODE_NOFILE) 2069 omode |= O_CREAT|O_EXCL; 2070 2071 map->map_lockfd = -1; 2072 2073 # if LOCK_ON_OPEN 2074 if (mode == O_RDWR) 2075 omode |= O_TRUNC|O_EXLOCK; 2076 else 2077 omode |= O_SHLOCK; 2078 # else /* LOCK_ON_OPEN */ 2079 /* 2080 ** Pre-lock the file to avoid race conditions. In particular, 2081 ** since dbopen returns NULL if the file is zero length, we 2082 ** must have a locked instance around the dbopen. 2083 */ 2084 2085 fd = open(buf, omode, DBMMODE); 2086 if (fd < 0) 2087 { 2088 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2089 syserr("db_map_open: cannot pre-open database %s", buf); 2090 return false; 2091 } 2092 2093 /* make sure no baddies slipped in just before the open... */ 2094 if (filechanged(buf, fd, &st)) 2095 { 2096 save_errno = errno; 2097 (void) close(fd); 2098 errno = save_errno; 2099 syserr("db_map_open(%s): file changed after pre-open", buf); 2100 return false; 2101 } 2102 2103 /* if new file, get the "before" bits for later filechanged check */ 2104 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0) 2105 { 2106 save_errno = errno; 2107 (void) close(fd); 2108 errno = save_errno; 2109 syserr("db_map_open(%s): cannot fstat pre-opened file", 2110 buf); 2111 return false; 2112 } 2113 2114 /* actually lock the pre-opened file */ 2115 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX)) 2116 syserr("db_map_open: cannot lock %s", buf); 2117 2118 /* set up mode bits for dbopen */ 2119 if (mode == O_RDWR) 2120 omode |= O_TRUNC; 2121 omode &= ~(O_EXCL|O_CREAT); 2122 # endif /* LOCK_ON_OPEN */ 2123 2124 # if DB_VERSION_MAJOR < 2 2125 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo); 2126 # else /* DB_VERSION_MAJOR < 2 */ 2127 { 2128 int flags = 0; 2129 # if DB_VERSION_MAJOR > 2 2130 int ret; 2131 # endif /* DB_VERSION_MAJOR > 2 */ 2132 2133 if (mode == O_RDONLY) 2134 flags |= DB_RDONLY; 2135 if (bitset(O_CREAT, omode)) 2136 flags |= DB_CREATE; 2137 if (bitset(O_TRUNC, omode)) 2138 flags |= DB_TRUNCATE; 2139 SM_DB_FLAG_ADD(flags); 2140 2141 # if DB_VERSION_MAJOR > 2 2142 ret = db_create(&db, NULL, 0); 2143 # ifdef DB_CACHE_SIZE 2144 if (ret == 0 && db != NULL) 2145 { 2146 ret = db->set_cachesize(db, 0, DB_CACHE_SIZE, 0); 2147 if (ret != 0) 2148 { 2149 (void) db->close(db, 0); 2150 db = NULL; 2151 } 2152 } 2153 # endif /* DB_CACHE_SIZE */ 2154 # ifdef DB_HASH_NELEM 2155 if (dbtype == DB_HASH && ret == 0 && db != NULL) 2156 { 2157 ret = db->set_h_nelem(db, DB_HASH_NELEM); 2158 if (ret != 0) 2159 { 2160 (void) db->close(db, 0); 2161 db = NULL; 2162 } 2163 } 2164 # endif /* DB_HASH_NELEM */ 2165 if (ret == 0 && db != NULL) 2166 { 2167 ret = db->open(db, 2168 DBTXN /* transaction for DB 4.1 */ 2169 buf, NULL, dbtype, flags, DBMMODE); 2170 if (ret != 0) 2171 { 2172 #ifdef DB_OLD_VERSION 2173 if (ret == DB_OLD_VERSION) 2174 ret = EINVAL; 2175 #endif /* DB_OLD_VERSION */ 2176 (void) db->close(db, 0); 2177 db = NULL; 2178 } 2179 } 2180 errno = ret; 2181 # else /* DB_VERSION_MAJOR > 2 */ 2182 errno = db_open(buf, dbtype, flags, DBMMODE, 2183 NULL, openinfo, &db); 2184 # endif /* DB_VERSION_MAJOR > 2 */ 2185 } 2186 # endif /* DB_VERSION_MAJOR < 2 */ 2187 save_errno = errno; 2188 2189 # if !LOCK_ON_OPEN 2190 if (mode == O_RDWR) 2191 map->map_lockfd = fd; 2192 else 2193 (void) close(fd); 2194 # endif /* !LOCK_ON_OPEN */ 2195 2196 if (db == NULL) 2197 { 2198 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && 2199 aliaswait(map, ".db", false)) 2200 return true; 2201 # if !LOCK_ON_OPEN 2202 if (map->map_lockfd >= 0) 2203 (void) close(map->map_lockfd); 2204 # endif /* !LOCK_ON_OPEN */ 2205 errno = save_errno; 2206 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2207 syserr("Cannot open %s database %s", 2208 mapclassname, buf); 2209 return false; 2210 } 2211 2212 # if DB_VERSION_MAJOR < 2 2213 fd = db->fd(db); 2214 # else /* DB_VERSION_MAJOR < 2 */ 2215 fd = -1; 2216 errno = db->fd(db, &fd); 2217 # endif /* DB_VERSION_MAJOR < 2 */ 2218 if (filechanged(buf, fd, &st)) 2219 { 2220 save_errno = errno; 2221 # if DB_VERSION_MAJOR < 2 2222 (void) db->close(db); 2223 # else /* DB_VERSION_MAJOR < 2 */ 2224 errno = db->close(db, 0); 2225 # endif /* DB_VERSION_MAJOR < 2 */ 2226 # if !LOCK_ON_OPEN 2227 if (map->map_lockfd >= 0) 2228 (void) close(map->map_lockfd); 2229 # endif /* !LOCK_ON_OPEN */ 2230 errno = save_errno; 2231 syserr("db_map_open(%s): file changed after open", buf); 2232 return false; 2233 } 2234 2235 if (mode == O_RDWR) 2236 map->map_mflags |= MF_LOCKED; 2237 # if LOCK_ON_OPEN 2238 if (fd >= 0 && mode == O_RDONLY) 2239 { 2240 (void) lockfile(fd, buf, NULL, LOCK_UN); 2241 } 2242 # endif /* LOCK_ON_OPEN */ 2243 2244 /* try to make sure that at least the database header is on disk */ 2245 if (mode == O_RDWR) 2246 { 2247 (void) db->sync(db, 0); 2248 if (geteuid() == 0 && TrustedUid != 0) 2249 { 2250 # if HASFCHOWN 2251 if (fchown(fd, TrustedUid, -1) < 0) 2252 { 2253 int err = errno; 2254 2255 sm_syslog(LOG_ALERT, NOQID, 2256 "ownership change on %s failed: %s", 2257 buf, sm_errstring(err)); 2258 message("050 ownership change on %s failed: %s", 2259 buf, sm_errstring(err)); 2260 } 2261 # else /* HASFCHOWN */ 2262 sm_syslog(LOG_ALERT, NOQID, 2263 "no fchown(): cannot change ownership on %s", 2264 map->map_file); 2265 message("050 no fchown(): cannot change ownership on %s", 2266 map->map_file); 2267 # endif /* HASFCHOWN */ 2268 } 2269 } 2270 2271 map->map_db2 = (ARBPTR_T) db; 2272 2273 /* 2274 ** Need to set map_mtime before the call to aliaswait() 2275 ** as aliaswait() will call map_lookup() which requires 2276 ** map_mtime to be set 2277 */ 2278 2279 if (fd >= 0 && fstat(fd, &st) >= 0) 2280 map->map_mtime = st.st_mtime; 2281 2282 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && 2283 !aliaswait(map, ".db", true)) 2284 return false; 2285 return true; 2286 } 2287 2288 2289 /* 2290 ** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map 2291 */ 2292 2293 char * 2294 db_map_lookup(map, name, av, statp) 2295 MAP *map; 2296 char *name; 2297 char **av; 2298 int *statp; 2299 { 2300 DBT key, val; 2301 register DB *db = (DB *) map->map_db2; 2302 int i; 2303 int st; 2304 int save_errno; 2305 int fd; 2306 struct stat stbuf; 2307 char keybuf[MAXNAME + 1]; 2308 char buf[MAXPATHLEN]; 2309 2310 memset(&key, '\0', sizeof key); 2311 memset(&val, '\0', sizeof val); 2312 2313 if (tTd(38, 20)) 2314 sm_dprintf("db_map_lookup(%s, %s)\n", 2315 map->map_mname, name); 2316 2317 if (sm_strlcpy(buf, map->map_file, sizeof buf) >= sizeof buf) 2318 { 2319 errno = 0; 2320 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2321 syserr("map \"%s\": map file %s name too long", 2322 map->map_mname, map->map_file); 2323 return NULL; 2324 } 2325 i = strlen(buf); 2326 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0) 2327 buf[i - 3] = '\0'; 2328 2329 key.size = strlen(name); 2330 if (key.size > sizeof keybuf - 1) 2331 key.size = sizeof keybuf - 1; 2332 key.data = keybuf; 2333 memmove(keybuf, name, key.size); 2334 keybuf[key.size] = '\0'; 2335 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2336 makelower(keybuf); 2337 lockdb: 2338 # if DB_VERSION_MAJOR < 2 2339 fd = db->fd(db); 2340 # else /* DB_VERSION_MAJOR < 2 */ 2341 fd = -1; 2342 errno = db->fd(db, &fd); 2343 # endif /* DB_VERSION_MAJOR < 2 */ 2344 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 2345 (void) lockfile(fd, buf, ".db", LOCK_SH); 2346 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime) 2347 { 2348 /* Reopen the database to sync the cache */ 2349 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR 2350 : O_RDONLY; 2351 2352 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 2353 (void) lockfile(fd, buf, ".db", LOCK_UN); 2354 map->map_mflags |= MF_CLOSING; 2355 map->map_class->map_close(map); 2356 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 2357 if (map->map_class->map_open(map, omode)) 2358 { 2359 map->map_mflags |= MF_OPEN; 2360 map->map_pid = CurrentPid; 2361 if ((omode && O_ACCMODE) == O_RDWR) 2362 map->map_mflags |= MF_WRITABLE; 2363 db = (DB *) map->map_db2; 2364 goto lockdb; 2365 } 2366 else 2367 { 2368 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2369 { 2370 extern MAPCLASS BogusMapClass; 2371 2372 *statp = EX_TEMPFAIL; 2373 map->map_orgclass = map->map_class; 2374 map->map_class = &BogusMapClass; 2375 map->map_mflags |= MF_OPEN; 2376 map->map_pid = CurrentPid; 2377 syserr("Cannot reopen DB database %s", 2378 map->map_file); 2379 } 2380 return NULL; 2381 } 2382 } 2383 2384 st = 1; 2385 if (bitset(MF_TRY0NULL, map->map_mflags)) 2386 { 2387 # if DB_VERSION_MAJOR < 2 2388 st = db->get(db, &key, &val, 0); 2389 # else /* DB_VERSION_MAJOR < 2 */ 2390 errno = db->get(db, NULL, &key, &val, 0); 2391 switch (errno) 2392 { 2393 case DB_NOTFOUND: 2394 case DB_KEYEMPTY: 2395 st = 1; 2396 break; 2397 2398 case 0: 2399 st = 0; 2400 break; 2401 2402 default: 2403 st = -1; 2404 break; 2405 } 2406 # endif /* DB_VERSION_MAJOR < 2 */ 2407 if (st == 0) 2408 map->map_mflags &= ~MF_TRY1NULL; 2409 } 2410 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags)) 2411 { 2412 key.size++; 2413 # if DB_VERSION_MAJOR < 2 2414 st = db->get(db, &key, &val, 0); 2415 # else /* DB_VERSION_MAJOR < 2 */ 2416 errno = db->get(db, NULL, &key, &val, 0); 2417 switch (errno) 2418 { 2419 case DB_NOTFOUND: 2420 case DB_KEYEMPTY: 2421 st = 1; 2422 break; 2423 2424 case 0: 2425 st = 0; 2426 break; 2427 2428 default: 2429 st = -1; 2430 break; 2431 } 2432 # endif /* DB_VERSION_MAJOR < 2 */ 2433 if (st == 0) 2434 map->map_mflags &= ~MF_TRY0NULL; 2435 } 2436 save_errno = errno; 2437 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 2438 (void) lockfile(fd, buf, ".db", LOCK_UN); 2439 if (st != 0) 2440 { 2441 errno = save_errno; 2442 if (st < 0) 2443 syserr("db_map_lookup: get (%s)", name); 2444 return NULL; 2445 } 2446 if (bitset(MF_MATCHONLY, map->map_mflags)) 2447 return map_rewrite(map, name, strlen(name), NULL); 2448 else 2449 return map_rewrite(map, val.data, val.size, av); 2450 } 2451 2452 2453 /* 2454 ** DB_MAP_STORE -- store a datum in the NEWDB database 2455 */ 2456 2457 void 2458 db_map_store(map, lhs, rhs) 2459 register MAP *map; 2460 char *lhs; 2461 char *rhs; 2462 { 2463 int status; 2464 DBT key; 2465 DBT data; 2466 register DB *db = map->map_db2; 2467 char keybuf[MAXNAME + 1]; 2468 2469 memset(&key, '\0', sizeof key); 2470 memset(&data, '\0', sizeof data); 2471 2472 if (tTd(38, 12)) 2473 sm_dprintf("db_map_store(%s, %s, %s)\n", 2474 map->map_mname, lhs, rhs); 2475 2476 key.size = strlen(lhs); 2477 key.data = lhs; 2478 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2479 { 2480 if (key.size > sizeof keybuf - 1) 2481 key.size = sizeof keybuf - 1; 2482 memmove(keybuf, key.data, key.size); 2483 keybuf[key.size] = '\0'; 2484 makelower(keybuf); 2485 key.data = keybuf; 2486 } 2487 2488 data.size = strlen(rhs); 2489 data.data = rhs; 2490 2491 if (bitset(MF_INCLNULL, map->map_mflags)) 2492 { 2493 key.size++; 2494 data.size++; 2495 } 2496 2497 # if DB_VERSION_MAJOR < 2 2498 status = db->put(db, &key, &data, R_NOOVERWRITE); 2499 # else /* DB_VERSION_MAJOR < 2 */ 2500 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE); 2501 switch (errno) 2502 { 2503 case DB_KEYEXIST: 2504 status = 1; 2505 break; 2506 2507 case 0: 2508 status = 0; 2509 break; 2510 2511 default: 2512 status = -1; 2513 break; 2514 } 2515 # endif /* DB_VERSION_MAJOR < 2 */ 2516 if (status > 0) 2517 { 2518 if (!bitset(MF_APPEND, map->map_mflags)) 2519 message("050 Warning: duplicate alias name %s", lhs); 2520 else 2521 { 2522 static char *buf = NULL; 2523 static int bufsiz = 0; 2524 DBT old; 2525 2526 memset(&old, '\0', sizeof old); 2527 2528 old.data = db_map_lookup(map, key.data, 2529 (char **) NULL, &status); 2530 if (old.data != NULL) 2531 { 2532 old.size = strlen(old.data); 2533 if (data.size + old.size + 2 > (size_t) bufsiz) 2534 { 2535 if (buf != NULL) 2536 sm_free(buf); 2537 bufsiz = data.size + old.size + 2; 2538 buf = sm_pmalloc_x(bufsiz); 2539 } 2540 (void) sm_strlcpyn(buf, bufsiz, 3, 2541 (char *) data.data, ",", 2542 (char *) old.data); 2543 data.size = data.size + old.size + 1; 2544 data.data = buf; 2545 if (tTd(38, 9)) 2546 sm_dprintf("db_map_store append=%s\n", 2547 (char *) data.data); 2548 } 2549 } 2550 # if DB_VERSION_MAJOR < 2 2551 status = db->put(db, &key, &data, 0); 2552 # else /* DB_VERSION_MAJOR < 2 */ 2553 status = errno = db->put(db, NULL, &key, &data, 0); 2554 # endif /* DB_VERSION_MAJOR < 2 */ 2555 } 2556 if (status != 0) 2557 syserr("readaliases: db put (%s)", lhs); 2558 } 2559 2560 2561 /* 2562 ** DB_MAP_CLOSE -- add distinguished entries and close the database 2563 */ 2564 2565 void 2566 db_map_close(map) 2567 MAP *map; 2568 { 2569 register DB *db = map->map_db2; 2570 2571 if (tTd(38, 9)) 2572 sm_dprintf("db_map_close(%s, %s, %lx)\n", 2573 map->map_mname, map->map_file, map->map_mflags); 2574 2575 if (bitset(MF_WRITABLE, map->map_mflags)) 2576 { 2577 /* write out the distinguished alias */ 2578 db_map_store(map, "@", "@"); 2579 } 2580 2581 (void) db->sync(db, 0); 2582 2583 # if !LOCK_ON_OPEN 2584 if (map->map_lockfd >= 0) 2585 (void) close(map->map_lockfd); 2586 # endif /* !LOCK_ON_OPEN */ 2587 2588 # if DB_VERSION_MAJOR < 2 2589 if (db->close(db) != 0) 2590 # else /* DB_VERSION_MAJOR < 2 */ 2591 /* 2592 ** Berkeley DB can use internal shared memory 2593 ** locking for its memory pool. Closing a map 2594 ** opened by another process will interfere 2595 ** with the shared memory and locks of the parent 2596 ** process leaving things in a bad state. 2597 */ 2598 2599 /* 2600 ** If this map was not opened by the current 2601 ** process, do not close the map but recover 2602 ** the file descriptor. 2603 */ 2604 2605 if (map->map_pid != CurrentPid) 2606 { 2607 int fd = -1; 2608 2609 errno = db->fd(db, &fd); 2610 if (fd >= 0) 2611 (void) close(fd); 2612 return; 2613 } 2614 2615 if ((errno = db->close(db, 0)) != 0) 2616 # endif /* DB_VERSION_MAJOR < 2 */ 2617 syserr("db_map_close(%s, %s, %lx): db close failure", 2618 map->map_mname, map->map_file, map->map_mflags); 2619 } 2620 #endif /* NEWDB */ 2621 /* 2622 ** NIS Modules 2623 */ 2624 2625 #if NIS 2626 2627 # ifndef YPERR_BUSY 2628 # define YPERR_BUSY 16 2629 # endif /* ! YPERR_BUSY */ 2630 2631 /* 2632 ** NIS_MAP_OPEN -- open DBM map 2633 */ 2634 2635 bool 2636 nis_map_open(map, mode) 2637 MAP *map; 2638 int mode; 2639 { 2640 int yperr; 2641 register char *p; 2642 auto char *vp; 2643 auto int vsize; 2644 2645 if (tTd(38, 2)) 2646 sm_dprintf("nis_map_open(%s, %s, %d)\n", 2647 map->map_mname, map->map_file, mode); 2648 2649 mode &= O_ACCMODE; 2650 if (mode != O_RDONLY) 2651 { 2652 /* issue a pseudo-error message */ 2653 errno = SM_EMAPCANTWRITE; 2654 return false; 2655 } 2656 2657 p = strchr(map->map_file, '@'); 2658 if (p != NULL) 2659 { 2660 *p++ = '\0'; 2661 if (*p != '\0') 2662 map->map_domain = p; 2663 } 2664 2665 if (*map->map_file == '\0') 2666 map->map_file = "mail.aliases"; 2667 2668 if (map->map_domain == NULL) 2669 { 2670 yperr = yp_get_default_domain(&map->map_domain); 2671 if (yperr != 0) 2672 { 2673 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2674 syserr("451 4.3.5 NIS map %s specified, but NIS not running", 2675 map->map_file); 2676 return false; 2677 } 2678 } 2679 2680 /* check to see if this map actually exists */ 2681 vp = NULL; 2682 yperr = yp_match(map->map_domain, map->map_file, "@", 1, 2683 &vp, &vsize); 2684 if (tTd(38, 10)) 2685 sm_dprintf("nis_map_open: yp_match(@, %s, %s) => %s\n", 2686 map->map_domain, map->map_file, yperr_string(yperr)); 2687 if (vp != NULL) 2688 sm_free(vp); 2689 2690 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY) 2691 { 2692 /* 2693 ** We ought to be calling aliaswait() here if this is an 2694 ** alias file, but powerful HP-UX NIS servers apparently 2695 ** don't insert the @:@ token into the alias map when it 2696 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX. 2697 */ 2698 2699 # if 0 2700 if (!bitset(MF_ALIAS, map->map_mflags) || 2701 aliaswait(map, NULL, true)) 2702 # endif /* 0 */ 2703 return true; 2704 } 2705 2706 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2707 { 2708 syserr("451 4.3.5 Cannot bind to map %s in domain %s: %s", 2709 map->map_file, map->map_domain, yperr_string(yperr)); 2710 } 2711 2712 return false; 2713 } 2714 2715 2716 /* 2717 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map 2718 */ 2719 2720 /* ARGSUSED3 */ 2721 char * 2722 nis_map_lookup(map, name, av, statp) 2723 MAP *map; 2724 char *name; 2725 char **av; 2726 int *statp; 2727 { 2728 char *vp; 2729 auto int vsize; 2730 int buflen; 2731 int yperr; 2732 char keybuf[MAXNAME + 1]; 2733 char *SM_NONVOLATILE result = NULL; 2734 2735 if (tTd(38, 20)) 2736 sm_dprintf("nis_map_lookup(%s, %s)\n", 2737 map->map_mname, name); 2738 2739 buflen = strlen(name); 2740 if (buflen > sizeof keybuf - 1) 2741 buflen = sizeof keybuf - 1; 2742 memmove(keybuf, name, buflen); 2743 keybuf[buflen] = '\0'; 2744 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2745 makelower(keybuf); 2746 yperr = YPERR_KEY; 2747 vp = NULL; 2748 if (bitset(MF_TRY0NULL, map->map_mflags)) 2749 { 2750 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, 2751 &vp, &vsize); 2752 if (yperr == 0) 2753 map->map_mflags &= ~MF_TRY1NULL; 2754 } 2755 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags)) 2756 { 2757 SM_FREE_CLR(vp); 2758 buflen++; 2759 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, 2760 &vp, &vsize); 2761 if (yperr == 0) 2762 map->map_mflags &= ~MF_TRY0NULL; 2763 } 2764 if (yperr != 0) 2765 { 2766 if (yperr != YPERR_KEY && yperr != YPERR_BUSY) 2767 map->map_mflags &= ~(MF_VALID|MF_OPEN); 2768 if (vp != NULL) 2769 sm_free(vp); 2770 return NULL; 2771 } 2772 SM_TRY 2773 if (bitset(MF_MATCHONLY, map->map_mflags)) 2774 result = map_rewrite(map, name, strlen(name), NULL); 2775 else 2776 result = map_rewrite(map, vp, vsize, av); 2777 SM_FINALLY 2778 if (vp != NULL) 2779 sm_free(vp); 2780 SM_END_TRY 2781 return result; 2782 } 2783 2784 2785 /* 2786 ** NIS_GETCANONNAME -- look up canonical name in NIS 2787 */ 2788 2789 static bool 2790 nis_getcanonname(name, hbsize, statp) 2791 char *name; 2792 int hbsize; 2793 int *statp; 2794 { 2795 char *vp; 2796 auto int vsize; 2797 int keylen; 2798 int yperr; 2799 static bool try0null = true; 2800 static bool try1null = true; 2801 static char *yp_domain = NULL; 2802 char host_record[MAXLINE]; 2803 char cbuf[MAXNAME]; 2804 char nbuf[MAXNAME + 1]; 2805 2806 if (tTd(38, 20)) 2807 sm_dprintf("nis_getcanonname(%s)\n", name); 2808 2809 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) 2810 { 2811 *statp = EX_UNAVAILABLE; 2812 return false; 2813 } 2814 (void) shorten_hostname(nbuf); 2815 keylen = strlen(nbuf); 2816 2817 if (yp_domain == NULL) 2818 (void) yp_get_default_domain(&yp_domain); 2819 makelower(nbuf); 2820 yperr = YPERR_KEY; 2821 vp = NULL; 2822 if (try0null) 2823 { 2824 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, 2825 &vp, &vsize); 2826 if (yperr == 0) 2827 try1null = false; 2828 } 2829 if (yperr == YPERR_KEY && try1null) 2830 { 2831 SM_FREE_CLR(vp); 2832 keylen++; 2833 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, 2834 &vp, &vsize); 2835 if (yperr == 0) 2836 try0null = false; 2837 } 2838 if (yperr != 0) 2839 { 2840 if (yperr == YPERR_KEY) 2841 *statp = EX_NOHOST; 2842 else if (yperr == YPERR_BUSY) 2843 *statp = EX_TEMPFAIL; 2844 else 2845 *statp = EX_UNAVAILABLE; 2846 if (vp != NULL) 2847 sm_free(vp); 2848 return false; 2849 } 2850 (void) sm_strlcpy(host_record, vp, sizeof host_record); 2851 sm_free(vp); 2852 if (tTd(38, 44)) 2853 sm_dprintf("got record `%s'\n", host_record); 2854 vp = strpbrk(host_record, "#\n"); 2855 if (vp != NULL) 2856 *vp = '\0'; 2857 if (!extract_canonname(nbuf, NULL, host_record, cbuf, sizeof cbuf)) 2858 { 2859 /* this should not happen, but.... */ 2860 *statp = EX_NOHOST; 2861 return false; 2862 } 2863 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize) 2864 { 2865 *statp = EX_UNAVAILABLE; 2866 return false; 2867 } 2868 *statp = EX_OK; 2869 return true; 2870 } 2871 2872 #endif /* NIS */ 2873 /* 2874 ** NISPLUS Modules 2875 ** 2876 ** This code donated by Sun Microsystems. 2877 */ 2878 2879 #if NISPLUS 2880 2881 # undef NIS /* symbol conflict in nis.h */ 2882 # undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */ 2883 # include <rpcsvc/nis.h> 2884 # include <rpcsvc/nislib.h> 2885 2886 # define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val 2887 # define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name 2888 # define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len) 2889 # define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.') 2890 2891 /* 2892 ** NISPLUS_MAP_OPEN -- open nisplus table 2893 */ 2894 2895 bool 2896 nisplus_map_open(map, mode) 2897 MAP *map; 2898 int mode; 2899 { 2900 nis_result *res = NULL; 2901 int retry_cnt, max_col, i; 2902 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 2903 2904 if (tTd(38, 2)) 2905 sm_dprintf("nisplus_map_open(%s, %s, %d)\n", 2906 map->map_mname, map->map_file, mode); 2907 2908 mode &= O_ACCMODE; 2909 if (mode != O_RDONLY) 2910 { 2911 errno = EPERM; 2912 return false; 2913 } 2914 2915 if (*map->map_file == '\0') 2916 map->map_file = "mail_aliases.org_dir"; 2917 2918 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL) 2919 { 2920 /* set default NISPLUS Domain to $m */ 2921 map->map_domain = newstr(nisplus_default_domain()); 2922 if (tTd(38, 2)) 2923 sm_dprintf("nisplus_map_open(%s): using domain %s\n", 2924 map->map_file, map->map_domain); 2925 } 2926 if (!PARTIAL_NAME(map->map_file)) 2927 { 2928 map->map_domain = newstr(""); 2929 (void) sm_strlcpy(qbuf, map->map_file, sizeof qbuf); 2930 } 2931 else 2932 { 2933 /* check to see if this map actually exists */ 2934 (void) sm_strlcpyn(qbuf, sizeof qbuf, 3, 2935 map->map_file, ".", map->map_domain); 2936 } 2937 2938 retry_cnt = 0; 2939 while (res == NULL || res->status != NIS_SUCCESS) 2940 { 2941 res = nis_lookup(qbuf, FOLLOW_LINKS); 2942 switch (res->status) 2943 { 2944 case NIS_SUCCESS: 2945 break; 2946 2947 case NIS_TRYAGAIN: 2948 case NIS_RPCERROR: 2949 case NIS_NAMEUNREACHABLE: 2950 if (retry_cnt++ > 4) 2951 { 2952 errno = EAGAIN; 2953 return false; 2954 } 2955 /* try not to overwhelm hosed server */ 2956 sleep(2); 2957 break; 2958 2959 default: /* all other nisplus errors */ 2960 # if 0 2961 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2962 syserr("451 4.3.5 Cannot find table %s.%s: %s", 2963 map->map_file, map->map_domain, 2964 nis_sperrno(res->status)); 2965 # endif /* 0 */ 2966 errno = EAGAIN; 2967 return false; 2968 } 2969 } 2970 2971 if (NIS_RES_NUMOBJ(res) != 1 || 2972 (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ)) 2973 { 2974 if (tTd(38, 10)) 2975 sm_dprintf("nisplus_map_open: %s is not a table\n", qbuf); 2976 # if 0 2977 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2978 syserr("451 4.3.5 %s.%s: %s is not a table", 2979 map->map_file, map->map_domain, 2980 nis_sperrno(res->status)); 2981 # endif /* 0 */ 2982 errno = EBADF; 2983 return false; 2984 } 2985 /* default key column is column 0 */ 2986 if (map->map_keycolnm == NULL) 2987 map->map_keycolnm = newstr(COL_NAME(res,0)); 2988 2989 max_col = COL_MAX(res); 2990 2991 /* verify the key column exist */ 2992 for (i = 0; i < max_col; i++) 2993 { 2994 if (strcmp(map->map_keycolnm, COL_NAME(res,i)) == 0) 2995 break; 2996 } 2997 if (i == max_col) 2998 { 2999 if (tTd(38, 2)) 3000 sm_dprintf("nisplus_map_open(%s): can not find key column %s\n", 3001 map->map_file, map->map_keycolnm); 3002 errno = ENOENT; 3003 return false; 3004 } 3005 3006 /* default value column is the last column */ 3007 if (map->map_valcolnm == NULL) 3008 { 3009 map->map_valcolno = max_col - 1; 3010 return true; 3011 } 3012 3013 for (i = 0; i< max_col; i++) 3014 { 3015 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0) 3016 { 3017 map->map_valcolno = i; 3018 return true; 3019 } 3020 } 3021 3022 if (tTd(38, 2)) 3023 sm_dprintf("nisplus_map_open(%s): can not find column %s\n", 3024 map->map_file, map->map_keycolnm); 3025 errno = ENOENT; 3026 return false; 3027 } 3028 3029 3030 /* 3031 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table 3032 */ 3033 3034 char * 3035 nisplus_map_lookup(map, name, av, statp) 3036 MAP *map; 3037 char *name; 3038 char **av; 3039 int *statp; 3040 { 3041 char *p; 3042 auto int vsize; 3043 char *skp; 3044 int skleft; 3045 char search_key[MAXNAME + 4]; 3046 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 3047 nis_result *result; 3048 3049 if (tTd(38, 20)) 3050 sm_dprintf("nisplus_map_lookup(%s, %s)\n", 3051 map->map_mname, name); 3052 3053 if (!bitset(MF_OPEN, map->map_mflags)) 3054 { 3055 if (nisplus_map_open(map, O_RDONLY)) 3056 { 3057 map->map_mflags |= MF_OPEN; 3058 map->map_pid = CurrentPid; 3059 } 3060 else 3061 { 3062 *statp = EX_UNAVAILABLE; 3063 return NULL; 3064 } 3065 } 3066 3067 /* 3068 ** Copy the name to the key buffer, escaping double quote characters 3069 ** by doubling them and quoting "]" and "," to avoid having the 3070 ** NIS+ parser choke on them. 3071 */ 3072 3073 skleft = sizeof search_key - 4; 3074 skp = search_key; 3075 for (p = name; *p != '\0' && skleft > 0; p++) 3076 { 3077 switch (*p) 3078 { 3079 case ']': 3080 case ',': 3081 /* quote the character */ 3082 *skp++ = '"'; 3083 *skp++ = *p; 3084 *skp++ = '"'; 3085 skleft -= 3; 3086 break; 3087 3088 case '"': 3089 /* double the quote */ 3090 *skp++ = '"'; 3091 skleft--; 3092 /* FALLTHROUGH */ 3093 3094 default: 3095 *skp++ = *p; 3096 skleft--; 3097 break; 3098 } 3099 } 3100 *skp = '\0'; 3101 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3102 makelower(search_key); 3103 3104 /* construct the query */ 3105 if (PARTIAL_NAME(map->map_file)) 3106 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s", 3107 map->map_keycolnm, search_key, map->map_file, 3108 map->map_domain); 3109 else 3110 (void) sm_snprintf(qbuf, sizeof qbuf, "[%s=%s],%s", 3111 map->map_keycolnm, search_key, map->map_file); 3112 3113 if (tTd(38, 20)) 3114 sm_dprintf("qbuf=%s\n", qbuf); 3115 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL); 3116 if (result->status == NIS_SUCCESS) 3117 { 3118 int count; 3119 char *str; 3120 3121 if ((count = NIS_RES_NUMOBJ(result)) != 1) 3122 { 3123 if (LogLevel > 10) 3124 sm_syslog(LOG_WARNING, CurEnv->e_id, 3125 "%s: lookup error, expected 1 entry, got %d", 3126 map->map_file, count); 3127 3128 /* ignore second entry */ 3129 if (tTd(38, 20)) 3130 sm_dprintf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n", 3131 name, count); 3132 } 3133 3134 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno)); 3135 /* set the length of the result */ 3136 if (p == NULL) 3137 p = ""; 3138 vsize = strlen(p); 3139 if (tTd(38, 20)) 3140 sm_dprintf("nisplus_map_lookup(%s), found %s\n", 3141 name, p); 3142 if (bitset(MF_MATCHONLY, map->map_mflags)) 3143 str = map_rewrite(map, name, strlen(name), NULL); 3144 else 3145 str = map_rewrite(map, p, vsize, av); 3146 nis_freeresult(result); 3147 *statp = EX_OK; 3148 return str; 3149 } 3150 else 3151 { 3152 if (result->status == NIS_NOTFOUND) 3153 *statp = EX_NOTFOUND; 3154 else if (result->status == NIS_TRYAGAIN) 3155 *statp = EX_TEMPFAIL; 3156 else 3157 { 3158 *statp = EX_UNAVAILABLE; 3159 map->map_mflags &= ~(MF_VALID|MF_OPEN); 3160 } 3161 } 3162 if (tTd(38, 20)) 3163 sm_dprintf("nisplus_map_lookup(%s), failed\n", name); 3164 nis_freeresult(result); 3165 return NULL; 3166 } 3167 3168 3169 3170 /* 3171 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+ 3172 */ 3173 3174 static bool 3175 nisplus_getcanonname(name, hbsize, statp) 3176 char *name; 3177 int hbsize; 3178 int *statp; 3179 { 3180 char *vp; 3181 auto int vsize; 3182 nis_result *result; 3183 char *p; 3184 char nbuf[MAXNAME + 1]; 3185 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 3186 3187 if (sm_strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) 3188 { 3189 *statp = EX_UNAVAILABLE; 3190 return false; 3191 } 3192 (void) shorten_hostname(nbuf); 3193 3194 p = strchr(nbuf, '.'); 3195 if (p == NULL) 3196 { 3197 /* single token */ 3198 (void) sm_snprintf(qbuf, sizeof qbuf, 3199 "[name=%s],hosts.org_dir", nbuf); 3200 } 3201 else if (p[1] != '\0') 3202 { 3203 /* multi token -- take only first token in nbuf */ 3204 *p = '\0'; 3205 (void) sm_snprintf(qbuf, sizeof qbuf, 3206 "[name=%s],hosts.org_dir.%s", nbuf, &p[1]); 3207 } 3208 else 3209 { 3210 *statp = EX_NOHOST; 3211 return false; 3212 } 3213 3214 if (tTd(38, 20)) 3215 sm_dprintf("\nnisplus_getcanonname(%s), qbuf=%s\n", 3216 name, qbuf); 3217 3218 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH, 3219 NULL, NULL); 3220 3221 if (result->status == NIS_SUCCESS) 3222 { 3223 int count; 3224 char *domain; 3225 3226 if ((count = NIS_RES_NUMOBJ(result)) != 1) 3227 { 3228 if (LogLevel > 10) 3229 sm_syslog(LOG_WARNING, CurEnv->e_id, 3230 "nisplus_getcanonname: lookup error, expected 1 entry, got %d", 3231 count); 3232 3233 /* ignore second entry */ 3234 if (tTd(38, 20)) 3235 sm_dprintf("nisplus_getcanonname(%s), got %d entries, all but first ignored\n", 3236 name, count); 3237 } 3238 3239 if (tTd(38, 20)) 3240 sm_dprintf("nisplus_getcanonname(%s), found in directory \"%s\"\n", 3241 name, (NIS_RES_OBJECT(result))->zo_domain); 3242 3243 3244 vp = ((NIS_RES_OBJECT(result))->EN_col(0)); 3245 vsize = strlen(vp); 3246 if (tTd(38, 20)) 3247 sm_dprintf("nisplus_getcanonname(%s), found %s\n", 3248 name, vp); 3249 if (strchr(vp, '.') != NULL) 3250 { 3251 domain = ""; 3252 } 3253 else 3254 { 3255 domain = macvalue('m', CurEnv); 3256 if (domain == NULL) 3257 domain = ""; 3258 } 3259 if (hbsize > vsize + (int) strlen(domain) + 1) 3260 { 3261 if (domain[0] == '\0') 3262 (void) sm_strlcpy(name, vp, hbsize); 3263 else 3264 (void) sm_snprintf(name, hbsize, 3265 "%s.%s", vp, domain); 3266 *statp = EX_OK; 3267 } 3268 else 3269 *statp = EX_NOHOST; 3270 nis_freeresult(result); 3271 return true; 3272 } 3273 else 3274 { 3275 if (result->status == NIS_NOTFOUND) 3276 *statp = EX_NOHOST; 3277 else if (result->status == NIS_TRYAGAIN) 3278 *statp = EX_TEMPFAIL; 3279 else 3280 *statp = EX_UNAVAILABLE; 3281 } 3282 if (tTd(38, 20)) 3283 sm_dprintf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n", 3284 name, result->status, *statp); 3285 nis_freeresult(result); 3286 return false; 3287 } 3288 3289 char * 3290 nisplus_default_domain() 3291 { 3292 static char default_domain[MAXNAME + 1] = ""; 3293 char *p; 3294 3295 if (default_domain[0] != '\0') 3296 return default_domain; 3297 3298 p = nis_local_directory(); 3299 (void) sm_strlcpy(default_domain, p, sizeof default_domain); 3300 return default_domain; 3301 } 3302 3303 #endif /* NISPLUS */ 3304 /* 3305 ** LDAP Modules 3306 */ 3307 3308 /* 3309 ** LDAPMAP_DEQUOTE - helper routine for ldapmap_parseargs 3310 */ 3311 3312 #if defined(LDAPMAP) || defined(PH_MAP) 3313 3314 # if PH_MAP 3315 # define ph_map_dequote ldapmap_dequote 3316 # endif /* PH_MAP */ 3317 3318 static char *ldapmap_dequote __P((char *)); 3319 3320 static char * 3321 ldapmap_dequote(str) 3322 char *str; 3323 { 3324 char *p; 3325 char *start; 3326 3327 if (str == NULL) 3328 return NULL; 3329 3330 p = str; 3331 if (*p == '"') 3332 { 3333 /* Should probably swallow initial whitespace here */ 3334 start = ++p; 3335 } 3336 else 3337 return str; 3338 while (*p != '"' && *p != '\0') 3339 p++; 3340 if (*p != '\0') 3341 *p = '\0'; 3342 return start; 3343 } 3344 #endif /* defined(LDAPMAP) || defined(PH_MAP) */ 3345 3346 #if LDAPMAP 3347 3348 static SM_LDAP_STRUCT *LDAPDefaults = NULL; 3349 3350 /* 3351 ** LDAPMAP_OPEN -- open LDAP map 3352 ** 3353 ** Connect to the LDAP server. Re-use existing connections since a 3354 ** single server connection to a host (with the same host, port, 3355 ** bind DN, and secret) can answer queries for multiple maps. 3356 */ 3357 3358 bool 3359 ldapmap_open(map, mode) 3360 MAP *map; 3361 int mode; 3362 { 3363 SM_LDAP_STRUCT *lmap; 3364 STAB *s; 3365 char *id; 3366 3367 if (tTd(38, 2)) 3368 sm_dprintf("ldapmap_open(%s, %d): ", map->map_mname, mode); 3369 3370 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \ 3371 defined(HASLDAPGETALIASBYNAME) 3372 if (VendorCode == VENDOR_SUN && 3373 strcmp(map->map_mname, "aliases.ldap") == 0) 3374 { 3375 return true; 3376 } 3377 #endif 3378 3379 mode &= O_ACCMODE; 3380 3381 /* sendmail doesn't have the ability to write to LDAP (yet) */ 3382 if (mode != O_RDONLY) 3383 { 3384 /* issue a pseudo-error message */ 3385 errno = SM_EMAPCANTWRITE; 3386 return false; 3387 } 3388 3389 lmap = (SM_LDAP_STRUCT *) map->map_db1; 3390 3391 s = ldapmap_findconn(lmap); 3392 if (s->s_lmap != NULL) 3393 { 3394 /* Already have a connection open to this LDAP server */ 3395 lmap->ldap_ld = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_ld; 3396 lmap->ldap_pid = ((SM_LDAP_STRUCT *)s->s_lmap->map_db1)->ldap_pid; 3397 3398 /* Add this map as head of linked list */ 3399 lmap->ldap_next = s->s_lmap; 3400 s->s_lmap = map; 3401 3402 if (tTd(38, 2)) 3403 sm_dprintf("using cached connection\n"); 3404 return true; 3405 } 3406 3407 if (tTd(38, 2)) 3408 sm_dprintf("opening new connection\n"); 3409 3410 if (lmap->ldap_host != NULL) 3411 id = lmap->ldap_host; 3412 else if (lmap->ldap_uri != NULL) 3413 id = lmap->ldap_uri; 3414 else 3415 id = "localhost"; 3416 3417 /* No connection yet, connect */ 3418 if (!sm_ldap_start(map->map_mname, lmap)) 3419 { 3420 if (errno == ETIMEDOUT) 3421 { 3422 if (LogLevel > 1) 3423 sm_syslog(LOG_NOTICE, CurEnv->e_id, 3424 "timeout conning to LDAP server %.100s", 3425 id); 3426 } 3427 3428 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3429 { 3430 if (bitset(MF_NODEFER, map->map_mflags)) 3431 { 3432 syserr("%s failed to %s in map %s", 3433 # if USE_LDAP_INIT 3434 "ldap_init/ldap_bind", 3435 # else /* USE_LDAP_INIT */ 3436 "ldap_open", 3437 # endif /* USE_LDAP_INIT */ 3438 id, map->map_mname); 3439 } 3440 else 3441 { 3442 syserr("451 4.3.5 %s failed to %s in map %s", 3443 # if USE_LDAP_INIT 3444 "ldap_init/ldap_bind", 3445 # else /* USE_LDAP_INIT */ 3446 "ldap_open", 3447 # endif /* USE_LDAP_INIT */ 3448 id, map->map_mname); 3449 } 3450 } 3451 return false; 3452 } 3453 3454 /* Save connection for reuse */ 3455 s->s_lmap = map; 3456 return true; 3457 } 3458 3459 /* 3460 ** LDAPMAP_CLOSE -- close ldap map 3461 */ 3462 3463 void 3464 ldapmap_close(map) 3465 MAP *map; 3466 { 3467 SM_LDAP_STRUCT *lmap; 3468 STAB *s; 3469 3470 if (tTd(38, 2)) 3471 sm_dprintf("ldapmap_close(%s)\n", map->map_mname); 3472 3473 lmap = (SM_LDAP_STRUCT *) map->map_db1; 3474 3475 /* Check if already closed */ 3476 if (lmap->ldap_ld == NULL) 3477 return; 3478 3479 /* Close the LDAP connection */ 3480 sm_ldap_close(lmap); 3481 3482 /* Mark all the maps that share the connection as closed */ 3483 s = ldapmap_findconn(lmap); 3484 3485 while (s->s_lmap != NULL) 3486 { 3487 MAP *smap = s->s_lmap; 3488 3489 if (tTd(38, 2) && smap != map) 3490 sm_dprintf("ldapmap_close(%s): closed %s (shared LDAP connection)\n", 3491 map->map_mname, smap->map_mname); 3492 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 3493 lmap = (SM_LDAP_STRUCT *) smap->map_db1; 3494 lmap->ldap_ld = NULL; 3495 s->s_lmap = lmap->ldap_next; 3496 lmap->ldap_next = NULL; 3497 } 3498 } 3499 3500 # ifdef SUNET_ID 3501 /* 3502 ** SUNET_ID_HASH -- Convert a string to its Sunet_id canonical form 3503 ** This only makes sense at Stanford University. 3504 */ 3505 3506 static char * 3507 sunet_id_hash(str) 3508 char *str; 3509 { 3510 char *p, *p_last; 3511 3512 p = str; 3513 p_last = p; 3514 while (*p != '\0') 3515 { 3516 if (islower(*p) || isdigit(*p)) 3517 { 3518 *p_last = *p; 3519 p_last++; 3520 } 3521 else if (isupper(*p)) 3522 { 3523 *p_last = tolower(*p); 3524 p_last++; 3525 } 3526 ++p; 3527 } 3528 if (*p_last != '\0') 3529 *p_last = '\0'; 3530 return str; 3531 } 3532 # endif /* SUNET_ID */ 3533 3534 /* 3535 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map 3536 */ 3537 3538 char * 3539 ldapmap_lookup(map, name, av, statp) 3540 MAP *map; 3541 char *name; 3542 char **av; 3543 int *statp; 3544 { 3545 int flags; 3546 int plen = 0; 3547 int psize = 0; 3548 int msgid; 3549 int save_errno; 3550 char *vp, *p; 3551 char *result = NULL; 3552 SM_RPOOL_T *rpool; 3553 SM_LDAP_STRUCT *lmap = NULL; 3554 char keybuf[MAXNAME + 1]; 3555 3556 if (tTd(38, 20)) 3557 sm_dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name); 3558 3559 #if defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && \ 3560 defined(HASLDAPGETALIASBYNAME) 3561 if (VendorCode == VENDOR_SUN && 3562 strcmp(map->map_mname, "aliases.ldap") == 0) 3563 { 3564 char answer[MAXNAME + 1]; 3565 int rc; 3566 3567 rc = __getldapaliasbyname(name, answer, sizeof(answer)); 3568 if (rc != 0) 3569 { 3570 if (tTd(38, 20)) 3571 sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n", 3572 name, errno); 3573 *statp = EX_NOTFOUND; 3574 return NULL; 3575 } 3576 *statp = EX_OK; 3577 if (tTd(38, 20)) 3578 sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name, 3579 answer); 3580 if (bitset(MF_MATCHONLY, map->map_mflags)) 3581 result = map_rewrite(map, name, strlen(name), NULL); 3582 else 3583 result = map_rewrite(map, answer, strlen(answer), av); 3584 return result; 3585 } 3586 #endif 3587 3588 /* Get ldap struct pointer from map */ 3589 lmap = (SM_LDAP_STRUCT *) map->map_db1; 3590 sm_ldap_setopts(lmap->ldap_ld, lmap); 3591 3592 (void) sm_strlcpy(keybuf, name, sizeof keybuf); 3593 3594 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3595 { 3596 # ifdef SUNET_ID 3597 sunet_id_hash(keybuf); 3598 # else /* SUNET_ID */ 3599 makelower(keybuf); 3600 # endif /* SUNET_ID */ 3601 } 3602 3603 msgid = sm_ldap_search(lmap, keybuf); 3604 if (msgid == -1) 3605 { 3606 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE; 3607 save_errno = errno; 3608 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3609 { 3610 if (bitset(MF_NODEFER, map->map_mflags)) 3611 syserr("Error in ldap_search using %s in map %s", 3612 keybuf, map->map_mname); 3613 else 3614 syserr("451 4.3.5 Error in ldap_search using %s in map %s", 3615 keybuf, map->map_mname); 3616 } 3617 *statp = EX_TEMPFAIL; 3618 switch (save_errno - E_LDAPBASE) 3619 { 3620 # ifdef LDAP_SERVER_DOWN 3621 case LDAP_SERVER_DOWN: 3622 # endif /* LDAP_SERVER_DOWN */ 3623 case LDAP_TIMEOUT: 3624 case LDAP_UNAVAILABLE: 3625 /* server disappeared, try reopen on next search */ 3626 ldapmap_close(map); 3627 break; 3628 } 3629 errno = save_errno; 3630 return NULL; 3631 } 3632 3633 *statp = EX_NOTFOUND; 3634 vp = NULL; 3635 3636 flags = 0; 3637 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 3638 flags |= SM_LDAP_SINGLEMATCH; 3639 if (bitset(MF_MATCHONLY, map->map_mflags)) 3640 flags |= SM_LDAP_MATCHONLY; 3641 # 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 } 6083 return map_rewrite(map, rwval, strlen(rwval), av); 6084 } 6085 } 6086 /* 6087 ** Program map type. 6088 ** 6089 ** This provides access to arbitrary programs. It should be used 6090 ** only very sparingly, since there is no way to bound the cost 6091 ** of invoking an arbitrary program. 6092 */ 6093 6094 char * 6095 prog_map_lookup(map, name, av, statp) 6096 MAP *map; 6097 char *name; 6098 char **av; 6099 int *statp; 6100 { 6101 int i; 6102 int save_errno; 6103 int fd; 6104 int status; 6105 auto pid_t pid; 6106 register char *p; 6107 char *rval; 6108 char *argv[MAXPV + 1]; 6109 char buf[MAXLINE]; 6110 6111 if (tTd(38, 20)) 6112 sm_dprintf("prog_map_lookup(%s, %s) %s\n", 6113 map->map_mname, name, map->map_file); 6114 6115 i = 0; 6116 argv[i++] = map->map_file; 6117 if (map->map_rebuild != NULL) 6118 { 6119 (void) sm_strlcpy(buf, map->map_rebuild, sizeof buf); 6120 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) 6121 { 6122 if (i >= MAXPV - 1) 6123 break; 6124 argv[i++] = p; 6125 } 6126 } 6127 argv[i++] = name; 6128 argv[i] = NULL; 6129 if (tTd(38, 21)) 6130 { 6131 sm_dprintf("prog_open:"); 6132 for (i = 0; argv[i] != NULL; i++) 6133 sm_dprintf(" %s", argv[i]); 6134 sm_dprintf("\n"); 6135 } 6136 (void) sm_blocksignal(SIGCHLD); 6137 pid = prog_open(argv, &fd, CurEnv); 6138 if (pid < 0) 6139 { 6140 if (!bitset(MF_OPTIONAL, map->map_mflags)) 6141 syserr("prog_map_lookup(%s) failed (%s) -- closing", 6142 map->map_mname, sm_errstring(errno)); 6143 else if (tTd(38, 9)) 6144 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing", 6145 map->map_mname, sm_errstring(errno)); 6146 map->map_mflags &= ~(MF_VALID|MF_OPEN); 6147 *statp = EX_OSFILE; 6148 return NULL; 6149 } 6150 i = read(fd, buf, sizeof buf - 1); 6151 if (i < 0) 6152 { 6153 syserr("prog_map_lookup(%s): read error %s", 6154 map->map_mname, sm_errstring(errno)); 6155 rval = NULL; 6156 } 6157 else if (i == 0) 6158 { 6159 if (tTd(38, 20)) 6160 sm_dprintf("prog_map_lookup(%s): empty answer\n", 6161 map->map_mname); 6162 rval = NULL; 6163 } 6164 else 6165 { 6166 buf[i] = '\0'; 6167 p = strchr(buf, '\n'); 6168 if (p != NULL) 6169 *p = '\0'; 6170 6171 /* collect the return value */ 6172 if (bitset(MF_MATCHONLY, map->map_mflags)) 6173 rval = map_rewrite(map, name, strlen(name), NULL); 6174 else 6175 rval = map_rewrite(map, buf, strlen(buf), av); 6176 6177 /* now flush any additional output */ 6178 while ((i = read(fd, buf, sizeof buf)) > 0) 6179 continue; 6180 } 6181 6182 /* wait for the process to terminate */ 6183 (void) close(fd); 6184 status = waitfor(pid); 6185 save_errno = errno; 6186 (void) sm_releasesignal(SIGCHLD); 6187 errno = save_errno; 6188 6189 if (status == -1) 6190 { 6191 syserr("prog_map_lookup(%s): wait error %s", 6192 map->map_mname, sm_errstring(errno)); 6193 *statp = EX_SOFTWARE; 6194 rval = NULL; 6195 } 6196 else if (WIFEXITED(status)) 6197 { 6198 if ((*statp = WEXITSTATUS(status)) != EX_OK) 6199 rval = NULL; 6200 } 6201 else 6202 { 6203 syserr("prog_map_lookup(%s): child died on signal %d", 6204 map->map_mname, status); 6205 *statp = EX_UNAVAILABLE; 6206 rval = NULL; 6207 } 6208 return rval; 6209 } 6210 /* 6211 ** Sequenced map type. 6212 ** 6213 ** Tries each map in order until something matches, much like 6214 ** implicit. Stores go to the first map in the list that can 6215 ** support storing. 6216 ** 6217 ** This is slightly unusual in that there are two interfaces. 6218 ** The "sequence" interface lets you stack maps arbitrarily. 6219 ** The "switch" interface builds a sequence map by looking 6220 ** at a system-dependent configuration file such as 6221 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. 6222 ** 6223 ** We don't need an explicit open, since all maps are 6224 ** opened on demand. 6225 */ 6226 6227 /* 6228 ** SEQ_MAP_PARSE -- Sequenced map parsing 6229 */ 6230 6231 bool 6232 seq_map_parse(map, ap) 6233 MAP *map; 6234 char *ap; 6235 { 6236 int maxmap; 6237 6238 if (tTd(38, 2)) 6239 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap); 6240 maxmap = 0; 6241 while (*ap != '\0') 6242 { 6243 register char *p; 6244 STAB *s; 6245 6246 /* find beginning of map name */ 6247 while (isascii(*ap) && isspace(*ap)) 6248 ap++; 6249 for (p = ap; 6250 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.'; 6251 p++) 6252 continue; 6253 if (*p != '\0') 6254 *p++ = '\0'; 6255 while (*p != '\0' && (!isascii(*p) || !isalnum(*p))) 6256 p++; 6257 if (*ap == '\0') 6258 { 6259 ap = p; 6260 continue; 6261 } 6262 s = stab(ap, ST_MAP, ST_FIND); 6263 if (s == NULL) 6264 { 6265 syserr("Sequence map %s: unknown member map %s", 6266 map->map_mname, ap); 6267 } 6268 else if (maxmap >= MAXMAPSTACK) 6269 { 6270 syserr("Sequence map %s: too many member maps (%d max)", 6271 map->map_mname, MAXMAPSTACK); 6272 maxmap++; 6273 } 6274 else if (maxmap < MAXMAPSTACK) 6275 { 6276 map->map_stack[maxmap++] = &s->s_map; 6277 } 6278 ap = p; 6279 } 6280 return true; 6281 } 6282 6283 /* 6284 ** SWITCH_MAP_OPEN -- open a switched map 6285 ** 6286 ** This looks at the system-dependent configuration and builds 6287 ** a sequence map that does the same thing. 6288 ** 6289 ** Every system must define a switch_map_find routine in conf.c 6290 ** that will return the list of service types associated with a 6291 ** given service class. 6292 */ 6293 6294 bool 6295 switch_map_open(map, mode) 6296 MAP *map; 6297 int mode; 6298 { 6299 int mapno; 6300 int nmaps; 6301 char *maptype[MAXMAPSTACK]; 6302 6303 if (tTd(38, 2)) 6304 sm_dprintf("switch_map_open(%s, %s, %d)\n", 6305 map->map_mname, map->map_file, mode); 6306 6307 mode &= O_ACCMODE; 6308 nmaps = switch_map_find(map->map_file, maptype, map->map_return); 6309 if (tTd(38, 19)) 6310 { 6311 sm_dprintf("\tswitch_map_find => %d\n", nmaps); 6312 for (mapno = 0; mapno < nmaps; mapno++) 6313 sm_dprintf("\t\t%s\n", maptype[mapno]); 6314 } 6315 if (nmaps <= 0 || nmaps > MAXMAPSTACK) 6316 return false; 6317 6318 for (mapno = 0; mapno < nmaps; mapno++) 6319 { 6320 register STAB *s; 6321 char nbuf[MAXNAME + 1]; 6322 6323 if (maptype[mapno] == NULL) 6324 continue; 6325 (void) sm_strlcpyn(nbuf, sizeof nbuf, 3, 6326 map->map_mname, ".", maptype[mapno]); 6327 s = stab(nbuf, ST_MAP, ST_FIND); 6328 if (s == NULL) 6329 { 6330 syserr("Switch map %s: unknown member map %s", 6331 map->map_mname, nbuf); 6332 } 6333 else 6334 { 6335 map->map_stack[mapno] = &s->s_map; 6336 if (tTd(38, 4)) 6337 sm_dprintf("\tmap_stack[%d] = %s:%s\n", 6338 mapno, 6339 s->s_map.map_class->map_cname, 6340 nbuf); 6341 } 6342 } 6343 return true; 6344 } 6345 6346 #if 0 6347 /* 6348 ** SEQ_MAP_CLOSE -- close all underlying maps 6349 */ 6350 6351 void 6352 seq_map_close(map) 6353 MAP *map; 6354 { 6355 int mapno; 6356 6357 if (tTd(38, 9)) 6358 sm_dprintf("seq_map_close(%s)\n", map->map_mname); 6359 6360 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6361 { 6362 MAP *mm = map->map_stack[mapno]; 6363 6364 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags)) 6365 continue; 6366 mm->map_mflags |= MF_CLOSING; 6367 mm->map_class->map_close(mm); 6368 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 6369 } 6370 } 6371 #endif /* 0 */ 6372 6373 /* 6374 ** SEQ_MAP_LOOKUP -- sequenced map lookup 6375 */ 6376 6377 char * 6378 seq_map_lookup(map, key, args, pstat) 6379 MAP *map; 6380 char *key; 6381 char **args; 6382 int *pstat; 6383 { 6384 int mapno; 6385 int mapbit = 0x01; 6386 bool tempfail = false; 6387 6388 if (tTd(38, 20)) 6389 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key); 6390 6391 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) 6392 { 6393 MAP *mm = map->map_stack[mapno]; 6394 char *rv; 6395 6396 if (mm == NULL) 6397 continue; 6398 if (!bitset(MF_OPEN, mm->map_mflags) && 6399 !openmap(mm)) 6400 { 6401 if (bitset(mapbit, map->map_return[MA_UNAVAIL])) 6402 { 6403 *pstat = EX_UNAVAILABLE; 6404 return NULL; 6405 } 6406 continue; 6407 } 6408 *pstat = EX_OK; 6409 rv = mm->map_class->map_lookup(mm, key, args, pstat); 6410 if (rv != NULL) 6411 return rv; 6412 if (*pstat == EX_TEMPFAIL) 6413 { 6414 if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) 6415 return NULL; 6416 tempfail = true; 6417 } 6418 else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) 6419 break; 6420 } 6421 if (tempfail) 6422 *pstat = EX_TEMPFAIL; 6423 else if (*pstat == EX_OK) 6424 *pstat = EX_NOTFOUND; 6425 return NULL; 6426 } 6427 6428 /* 6429 ** SEQ_MAP_STORE -- sequenced map store 6430 */ 6431 6432 void 6433 seq_map_store(map, key, val) 6434 MAP *map; 6435 char *key; 6436 char *val; 6437 { 6438 int mapno; 6439 6440 if (tTd(38, 12)) 6441 sm_dprintf("seq_map_store(%s, %s, %s)\n", 6442 map->map_mname, key, val); 6443 6444 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6445 { 6446 MAP *mm = map->map_stack[mapno]; 6447 6448 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags)) 6449 continue; 6450 6451 mm->map_class->map_store(mm, key, val); 6452 return; 6453 } 6454 syserr("seq_map_store(%s, %s, %s): no writable map", 6455 map->map_mname, key, val); 6456 } 6457 /* 6458 ** NULL stubs 6459 */ 6460 6461 /* ARGSUSED */ 6462 bool 6463 null_map_open(map, mode) 6464 MAP *map; 6465 int mode; 6466 { 6467 return true; 6468 } 6469 6470 /* ARGSUSED */ 6471 void 6472 null_map_close(map) 6473 MAP *map; 6474 { 6475 return; 6476 } 6477 6478 char * 6479 null_map_lookup(map, key, args, pstat) 6480 MAP *map; 6481 char *key; 6482 char **args; 6483 int *pstat; 6484 { 6485 *pstat = EX_NOTFOUND; 6486 return NULL; 6487 } 6488 6489 /* ARGSUSED */ 6490 void 6491 null_map_store(map, key, val) 6492 MAP *map; 6493 char *key; 6494 char *val; 6495 { 6496 return; 6497 } 6498 6499 /* 6500 ** BOGUS stubs 6501 */ 6502 6503 char * 6504 bogus_map_lookup(map, key, args, pstat) 6505 MAP *map; 6506 char *key; 6507 char **args; 6508 int *pstat; 6509 { 6510 *pstat = EX_TEMPFAIL; 6511 return NULL; 6512 } 6513 6514 MAPCLASS BogusMapClass = 6515 { 6516 "bogus-map", NULL, 0, 6517 NULL, bogus_map_lookup, null_map_store, 6518 null_map_open, null_map_close, 6519 }; 6520 /* 6521 ** MACRO modules 6522 */ 6523 6524 char * 6525 macro_map_lookup(map, name, av, statp) 6526 MAP *map; 6527 char *name; 6528 char **av; 6529 int *statp; 6530 { 6531 int mid; 6532 6533 if (tTd(38, 20)) 6534 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname, 6535 name == NULL ? "NULL" : name); 6536 6537 if (name == NULL || 6538 *name == '\0' || 6539 (mid = macid(name)) == 0) 6540 { 6541 *statp = EX_CONFIG; 6542 return NULL; 6543 } 6544 6545 if (av[1] == NULL) 6546 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL); 6547 else 6548 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]); 6549 6550 *statp = EX_OK; 6551 return ""; 6552 } 6553 /* 6554 ** REGEX modules 6555 */ 6556 6557 #if MAP_REGEX 6558 6559 # include <regex.h> 6560 6561 # define DEFAULT_DELIM CONDELSE 6562 # define END_OF_FIELDS -1 6563 # define ERRBUF_SIZE 80 6564 # define MAX_MATCH 32 6565 6566 # define xnalloc(s) memset(xalloc(s), '\0', s); 6567 6568 struct regex_map 6569 { 6570 regex_t *regex_pattern_buf; /* xalloc it */ 6571 int *regex_subfields; /* move to type MAP */ 6572 char *regex_delim; /* move to type MAP */ 6573 }; 6574 6575 static int parse_fields __P((char *, int *, int, int)); 6576 static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **)); 6577 6578 static int 6579 parse_fields(s, ibuf, blen, nr_substrings) 6580 char *s; 6581 int *ibuf; /* array */ 6582 int blen; /* number of elements in ibuf */ 6583 int nr_substrings; /* number of substrings in the pattern */ 6584 { 6585 register char *cp; 6586 int i = 0; 6587 bool lastone = false; 6588 6589 blen--; /* for terminating END_OF_FIELDS */ 6590 cp = s; 6591 do 6592 { 6593 for (;; cp++) 6594 { 6595 if (*cp == ',') 6596 { 6597 *cp = '\0'; 6598 break; 6599 } 6600 if (*cp == '\0') 6601 { 6602 lastone = true; 6603 break; 6604 } 6605 } 6606 if (i < blen) 6607 { 6608 int val = atoi(s); 6609 6610 if (val < 0 || val >= nr_substrings) 6611 { 6612 syserr("field (%d) out of range, only %d substrings in pattern", 6613 val, nr_substrings); 6614 return -1; 6615 } 6616 ibuf[i++] = val; 6617 } 6618 else 6619 { 6620 syserr("too many fields, %d max", blen); 6621 return -1; 6622 } 6623 s = ++cp; 6624 } while (!lastone); 6625 ibuf[i] = END_OF_FIELDS; 6626 return i; 6627 } 6628 6629 bool 6630 regex_map_init(map, ap) 6631 MAP *map; 6632 char *ap; 6633 { 6634 int regerr; 6635 struct regex_map *map_p; 6636 register char *p; 6637 char *sub_param = NULL; 6638 int pflags; 6639 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' }; 6640 6641 if (tTd(38, 2)) 6642 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n", 6643 map->map_mname, ap); 6644 6645 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; 6646 p = ap; 6647 map_p = (struct regex_map *) xnalloc(sizeof *map_p); 6648 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t)); 6649 6650 for (;;) 6651 { 6652 while (isascii(*p) && isspace(*p)) 6653 p++; 6654 if (*p != '-') 6655 break; 6656 switch (*++p) 6657 { 6658 case 'n': /* not */ 6659 map->map_mflags |= MF_REGEX_NOT; 6660 break; 6661 6662 case 'f': /* case sensitive */ 6663 map->map_mflags |= MF_NOFOLDCASE; 6664 pflags &= ~REG_ICASE; 6665 break; 6666 6667 case 'b': /* basic regular expressions */ 6668 pflags &= ~REG_EXTENDED; 6669 break; 6670 6671 case 's': /* substring match () syntax */ 6672 sub_param = ++p; 6673 pflags &= ~REG_NOSUB; 6674 break; 6675 6676 case 'd': /* delimiter */ 6677 map_p->regex_delim = ++p; 6678 break; 6679 6680 case 'a': /* map append */ 6681 map->map_app = ++p; 6682 break; 6683 6684 case 'm': /* matchonly */ 6685 map->map_mflags |= MF_MATCHONLY; 6686 break; 6687 6688 case 'q': 6689 map->map_mflags |= MF_KEEPQUOTES; 6690 break; 6691 6692 case 'S': 6693 map->map_spacesub = *++p; 6694 break; 6695 6696 case 'D': 6697 map->map_mflags |= MF_DEFER; 6698 break; 6699 6700 } 6701 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 6702 p++; 6703 if (*p != '\0') 6704 *p++ = '\0'; 6705 } 6706 if (tTd(38, 3)) 6707 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags); 6708 6709 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0) 6710 { 6711 /* Errorhandling */ 6712 char errbuf[ERRBUF_SIZE]; 6713 6714 (void) regerror(regerr, map_p->regex_pattern_buf, 6715 errbuf, sizeof errbuf); 6716 syserr("pattern-compile-error: %s", errbuf); 6717 sm_free(map_p->regex_pattern_buf); /* XXX */ 6718 sm_free(map_p); /* XXX */ 6719 return false; 6720 } 6721 6722 if (map->map_app != NULL) 6723 map->map_app = newstr(map->map_app); 6724 if (map_p->regex_delim != NULL) 6725 map_p->regex_delim = newstr(map_p->regex_delim); 6726 else 6727 map_p->regex_delim = defdstr; 6728 6729 if (!bitset(REG_NOSUB, pflags)) 6730 { 6731 /* substring matching */ 6732 int substrings; 6733 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1)); 6734 6735 substrings = map_p->regex_pattern_buf->re_nsub + 1; 6736 6737 if (tTd(38, 3)) 6738 sm_dprintf("regex_map_init: nr of substrings %d\n", 6739 substrings); 6740 6741 if (substrings >= MAX_MATCH) 6742 { 6743 syserr("too many substrings, %d max", MAX_MATCH); 6744 sm_free(map_p->regex_pattern_buf); /* XXX */ 6745 sm_free(map_p); /* XXX */ 6746 return false; 6747 } 6748 if (sub_param != NULL && sub_param[0] != '\0') 6749 { 6750 /* optional parameter -sfields */ 6751 if (parse_fields(sub_param, fields, 6752 MAX_MATCH + 1, substrings) == -1) 6753 return false; 6754 } 6755 else 6756 { 6757 int i; 6758 6759 /* set default fields */ 6760 for (i = 0; i < substrings; i++) 6761 fields[i] = i; 6762 fields[i] = END_OF_FIELDS; 6763 } 6764 map_p->regex_subfields = fields; 6765 if (tTd(38, 3)) 6766 { 6767 int *ip; 6768 6769 sm_dprintf("regex_map_init: subfields"); 6770 for (ip = fields; *ip != END_OF_FIELDS; ip++) 6771 sm_dprintf(" %d", *ip); 6772 sm_dprintf("\n"); 6773 } 6774 } 6775 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */ 6776 return true; 6777 } 6778 6779 static char * 6780 regex_map_rewrite(map, s, slen, av) 6781 MAP *map; 6782 const char *s; 6783 size_t slen; 6784 char **av; 6785 { 6786 if (bitset(MF_MATCHONLY, map->map_mflags)) 6787 return map_rewrite(map, av[0], strlen(av[0]), NULL); 6788 else 6789 return map_rewrite(map, s, slen, av); 6790 } 6791 6792 char * 6793 regex_map_lookup(map, name, av, statp) 6794 MAP *map; 6795 char *name; 6796 char **av; 6797 int *statp; 6798 { 6799 int reg_res; 6800 struct regex_map *map_p; 6801 regmatch_t pmatch[MAX_MATCH]; 6802 6803 if (tTd(38, 20)) 6804 { 6805 char **cpp; 6806 6807 sm_dprintf("regex_map_lookup: key '%s'\n", name); 6808 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 6809 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp); 6810 } 6811 6812 map_p = (struct regex_map *)(map->map_db1); 6813 reg_res = regexec(map_p->regex_pattern_buf, 6814 name, MAX_MATCH, pmatch, 0); 6815 6816 if (bitset(MF_REGEX_NOT, map->map_mflags)) 6817 { 6818 /* option -n */ 6819 if (reg_res == REG_NOMATCH) 6820 return regex_map_rewrite(map, "", (size_t) 0, av); 6821 else 6822 return NULL; 6823 } 6824 if (reg_res == REG_NOMATCH) 6825 return NULL; 6826 6827 if (map_p->regex_subfields != NULL) 6828 { 6829 /* option -s */ 6830 static char retbuf[MAXNAME]; 6831 int fields[MAX_MATCH + 1]; 6832 bool first = true; 6833 int anglecnt = 0, cmntcnt = 0, spacecnt = 0; 6834 bool quotemode = false, bslashmode = false; 6835 register char *dp, *sp; 6836 char *endp, *ldp; 6837 int *ip; 6838 6839 dp = retbuf; 6840 ldp = retbuf + sizeof(retbuf) - 1; 6841 6842 if (av[1] != NULL) 6843 { 6844 if (parse_fields(av[1], fields, MAX_MATCH + 1, 6845 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1) 6846 { 6847 *statp = EX_CONFIG; 6848 return NULL; 6849 } 6850 ip = fields; 6851 } 6852 else 6853 ip = map_p->regex_subfields; 6854 6855 for ( ; *ip != END_OF_FIELDS; ip++) 6856 { 6857 if (!first) 6858 { 6859 for (sp = map_p->regex_delim; *sp; sp++) 6860 { 6861 if (dp < ldp) 6862 *dp++ = *sp; 6863 } 6864 } 6865 else 6866 first = false; 6867 6868 if (*ip >= MAX_MATCH || 6869 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0) 6870 continue; 6871 6872 sp = name + pmatch[*ip].rm_so; 6873 endp = name + pmatch[*ip].rm_eo; 6874 for (; endp > sp; sp++) 6875 { 6876 if (dp < ldp) 6877 { 6878 if (bslashmode) 6879 { 6880 *dp++ = *sp; 6881 bslashmode = false; 6882 } 6883 else if (quotemode && *sp != '"' && 6884 *sp != '\\') 6885 { 6886 *dp++ = *sp; 6887 } 6888 else switch (*dp++ = *sp) 6889 { 6890 case '\\': 6891 bslashmode = true; 6892 break; 6893 6894 case '(': 6895 cmntcnt++; 6896 break; 6897 6898 case ')': 6899 cmntcnt--; 6900 break; 6901 6902 case '<': 6903 anglecnt++; 6904 break; 6905 6906 case '>': 6907 anglecnt--; 6908 break; 6909 6910 case ' ': 6911 spacecnt++; 6912 break; 6913 6914 case '"': 6915 quotemode = !quotemode; 6916 break; 6917 } 6918 } 6919 } 6920 } 6921 if (anglecnt != 0 || cmntcnt != 0 || quotemode || 6922 bslashmode || spacecnt != 0) 6923 { 6924 sm_syslog(LOG_WARNING, NOQID, 6925 "Warning: regex may cause prescan() failure map=%s lookup=%s", 6926 map->map_mname, name); 6927 return NULL; 6928 } 6929 6930 *dp = '\0'; 6931 6932 return regex_map_rewrite(map, retbuf, strlen(retbuf), av); 6933 } 6934 return regex_map_rewrite(map, "", (size_t)0, av); 6935 } 6936 #endif /* MAP_REGEX */ 6937 /* 6938 ** NSD modules 6939 */ 6940 #if MAP_NSD 6941 6942 # include <ndbm.h> 6943 # define _DATUM_DEFINED 6944 # include <ns_api.h> 6945 6946 typedef struct ns_map_list 6947 { 6948 ns_map_t *map; /* XXX ns_ ? */ 6949 char *mapname; 6950 struct ns_map_list *next; 6951 } ns_map_list_t; 6952 6953 static ns_map_t * 6954 ns_map_t_find(mapname) 6955 char *mapname; 6956 { 6957 static ns_map_list_t *ns_maps = NULL; 6958 ns_map_list_t *ns_map; 6959 6960 /* walk the list of maps looking for the correctly named map */ 6961 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next) 6962 { 6963 if (strcmp(ns_map->mapname, mapname) == 0) 6964 break; 6965 } 6966 6967 /* if we are looking at a NULL ns_map_list_t, then create a new one */ 6968 if (ns_map == NULL) 6969 { 6970 ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map); 6971 ns_map->mapname = newstr(mapname); 6972 ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map); 6973 memset(ns_map->map, '\0', sizeof *ns_map->map); 6974 ns_map->next = ns_maps; 6975 ns_maps = ns_map; 6976 } 6977 return ns_map->map; 6978 } 6979 6980 char * 6981 nsd_map_lookup(map, name, av, statp) 6982 MAP *map; 6983 char *name; 6984 char **av; 6985 int *statp; 6986 { 6987 int buflen, r; 6988 char *p; 6989 ns_map_t *ns_map; 6990 char keybuf[MAXNAME + 1]; 6991 char buf[MAXLINE]; 6992 6993 if (tTd(38, 20)) 6994 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name); 6995 6996 buflen = strlen(name); 6997 if (buflen > sizeof keybuf - 1) 6998 buflen = sizeof keybuf - 1; /* XXX simply cut off? */ 6999 memmove(keybuf, name, buflen); 7000 keybuf[buflen] = '\0'; 7001 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 7002 makelower(keybuf); 7003 7004 ns_map = ns_map_t_find(map->map_file); 7005 if (ns_map == NULL) 7006 { 7007 if (tTd(38, 20)) 7008 sm_dprintf("nsd_map_t_find failed\n"); 7009 *statp = EX_UNAVAILABLE; 7010 return NULL; 7011 } 7012 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL, 7013 buf, sizeof buf); 7014 if (r == NS_UNAVAIL || r == NS_TRYAGAIN) 7015 { 7016 *statp = EX_TEMPFAIL; 7017 return NULL; 7018 } 7019 if (r == NS_BADREQ 7020 # ifdef NS_NOPERM 7021 || r == NS_NOPERM 7022 # endif /* NS_NOPERM */ 7023 ) 7024 { 7025 *statp = EX_CONFIG; 7026 return NULL; 7027 } 7028 if (r != NS_SUCCESS) 7029 { 7030 *statp = EX_NOTFOUND; 7031 return NULL; 7032 } 7033 7034 *statp = EX_OK; 7035 7036 /* Null out trailing \n */ 7037 if ((p = strchr(buf, '\n')) != NULL) 7038 *p = '\0'; 7039 7040 return map_rewrite(map, buf, strlen(buf), av); 7041 } 7042 #endif /* MAP_NSD */ 7043 7044 char * 7045 arith_map_lookup(map, name, av, statp) 7046 MAP *map; 7047 char *name; 7048 char **av; 7049 int *statp; 7050 { 7051 long r; 7052 long v[2]; 7053 bool res = false; 7054 bool boolres; 7055 static char result[16]; 7056 char **cpp; 7057 7058 if (tTd(38, 2)) 7059 { 7060 sm_dprintf("arith_map_lookup: key '%s'\n", name); 7061 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7062 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp); 7063 } 7064 r = 0; 7065 boolres = false; 7066 cpp = av; 7067 *statp = EX_OK; 7068 7069 /* 7070 ** read arguments for arith map 7071 ** - no check is made whether they are really numbers 7072 ** - just ignores args after the second 7073 */ 7074 7075 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++) 7076 v[r++] = strtol(*cpp, NULL, 0); 7077 7078 /* operator and (at least) two operands given? */ 7079 if (name != NULL && r == 2) 7080 { 7081 switch (*name) 7082 { 7083 case '|': 7084 r = v[0] | v[1]; 7085 break; 7086 7087 case '&': 7088 r = v[0] & v[1]; 7089 break; 7090 7091 case '%': 7092 if (v[1] == 0) 7093 return NULL; 7094 r = v[0] % v[1]; 7095 break; 7096 case '+': 7097 r = v[0] + v[1]; 7098 break; 7099 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 if (v[1] == 0) 7110 return NULL; 7111 r = v[0] / v[1]; 7112 break; 7113 7114 case 'l': 7115 res = v[0] < v[1]; 7116 boolres = true; 7117 break; 7118 7119 case '=': 7120 res = v[0] == v[1]; 7121 boolres = true; 7122 break; 7123 7124 default: 7125 /* XXX */ 7126 *statp = EX_CONFIG; 7127 if (LogLevel > 10) 7128 sm_syslog(LOG_WARNING, NOQID, 7129 "arith_map: unknown operator %c", 7130 isprint(*name) ? *name : '?'); 7131 return NULL; 7132 } 7133 if (boolres) 7134 (void) sm_snprintf(result, sizeof result, 7135 res ? "TRUE" : "FALSE"); 7136 else 7137 (void) sm_snprintf(result, sizeof result, "%ld", r); 7138 return result; 7139 } 7140 *statp = EX_CONFIG; 7141 return NULL; 7142 } 7143 7144 #if SOCKETMAP 7145 7146 # if NETINET || NETINET6 7147 # include <arpa/inet.h> 7148 # endif /* NETINET || NETINET6 */ 7149 7150 # define socket_map_next map_stack[0] 7151 7152 /* 7153 ** SOCKET_MAP_OPEN -- open socket table 7154 */ 7155 7156 bool 7157 socket_map_open(map, mode) 7158 MAP *map; 7159 int mode; 7160 { 7161 STAB *s; 7162 int sock = 0; 7163 SOCKADDR_LEN_T addrlen = 0; 7164 int addrno = 0; 7165 int save_errno; 7166 char *p; 7167 char *colon; 7168 char *at; 7169 struct hostent *hp = NULL; 7170 SOCKADDR addr; 7171 7172 if (tTd(38, 2)) 7173 sm_dprintf("socket_map_open(%s, %s, %d)\n", 7174 map->map_mname, map->map_file, mode); 7175 7176 mode &= O_ACCMODE; 7177 7178 /* sendmail doesn't have the ability to write to SOCKET (yet) */ 7179 if (mode != O_RDONLY) 7180 { 7181 /* issue a pseudo-error message */ 7182 errno = SM_EMAPCANTWRITE; 7183 return false; 7184 } 7185 7186 if (*map->map_file == '\0') 7187 { 7188 syserr("socket map \"%s\": empty or missing socket information", 7189 map->map_mname); 7190 return false; 7191 } 7192 7193 s = socket_map_findconn(map->map_file); 7194 if (s->s_socketmap != NULL) 7195 { 7196 /* Copy open connection */ 7197 map->map_db1 = s->s_socketmap->map_db1; 7198 7199 /* Add this map as head of linked list */ 7200 map->socket_map_next = s->s_socketmap; 7201 s->s_socketmap = map; 7202 7203 if (tTd(38, 2)) 7204 sm_dprintf("using cached connection\n"); 7205 return true; 7206 } 7207 7208 if (tTd(38, 2)) 7209 sm_dprintf("opening new connection\n"); 7210 7211 /* following code is ripped from milter.c */ 7212 /* XXX It should be put in a library... */ 7213 7214 /* protocol:filename or protocol:port@host */ 7215 memset(&addr, '\0', sizeof addr); 7216 p = map->map_file; 7217 colon = strchr(p, ':'); 7218 if (colon != NULL) 7219 { 7220 *colon = '\0'; 7221 7222 if (*p == '\0') 7223 { 7224 # if NETUNIX 7225 /* default to AF_UNIX */ 7226 addr.sa.sa_family = AF_UNIX; 7227 # else /* NETUNIX */ 7228 # if NETINET 7229 /* default to AF_INET */ 7230 addr.sa.sa_family = AF_INET; 7231 # else /* NETINET */ 7232 # if NETINET6 7233 /* default to AF_INET6 */ 7234 addr.sa.sa_family = AF_INET6; 7235 # else /* NETINET6 */ 7236 /* no protocols available */ 7237 syserr("socket map \"%s\": no valid socket protocols available", 7238 map->map_mname); 7239 return false; 7240 # endif /* NETINET6 */ 7241 # endif /* NETINET */ 7242 # endif /* NETUNIX */ 7243 } 7244 # if NETUNIX 7245 else if (sm_strcasecmp(p, "unix") == 0 || 7246 sm_strcasecmp(p, "local") == 0) 7247 addr.sa.sa_family = AF_UNIX; 7248 # endif /* NETUNIX */ 7249 # if NETINET 7250 else if (sm_strcasecmp(p, "inet") == 0) 7251 addr.sa.sa_family = AF_INET; 7252 # endif /* NETINET */ 7253 # if NETINET6 7254 else if (sm_strcasecmp(p, "inet6") == 0) 7255 addr.sa.sa_family = AF_INET6; 7256 # endif /* NETINET6 */ 7257 else 7258 { 7259 # ifdef EPROTONOSUPPORT 7260 errno = EPROTONOSUPPORT; 7261 # else /* EPROTONOSUPPORT */ 7262 errno = EINVAL; 7263 # endif /* EPROTONOSUPPORT */ 7264 syserr("socket map \"%s\": unknown socket type %s", 7265 map->map_mname, p); 7266 return false; 7267 } 7268 *colon++ = ':'; 7269 } 7270 else 7271 { 7272 colon = p; 7273 #if NETUNIX 7274 /* default to AF_UNIX */ 7275 addr.sa.sa_family = AF_UNIX; 7276 #else /* NETUNIX */ 7277 # if NETINET 7278 /* default to AF_INET */ 7279 addr.sa.sa_family = AF_INET; 7280 # else /* NETINET */ 7281 # if NETINET6 7282 /* default to AF_INET6 */ 7283 addr.sa.sa_family = AF_INET6; 7284 # else /* NETINET6 */ 7285 syserr("socket map \"%s\": unknown socket type %s", 7286 map->map_mname, p); 7287 return false; 7288 # endif /* NETINET6 */ 7289 # endif /* NETINET */ 7290 #endif /* NETUNIX */ 7291 } 7292 7293 # if NETUNIX 7294 if (addr.sa.sa_family == AF_UNIX) 7295 { 7296 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK; 7297 7298 at = colon; 7299 if (strlen(colon) >= sizeof addr.sunix.sun_path) 7300 { 7301 syserr("socket map \"%s\": local socket name %s too long", 7302 map->map_mname, colon); 7303 return false; 7304 } 7305 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 7306 S_IRUSR|S_IWUSR, NULL); 7307 7308 if (errno != 0) 7309 { 7310 /* if not safe, don't create */ 7311 syserr("socket map \"%s\": local socket name %s unsafe", 7312 map->map_mname, colon); 7313 return false; 7314 } 7315 7316 (void) sm_strlcpy(addr.sunix.sun_path, colon, 7317 sizeof addr.sunix.sun_path); 7318 addrlen = sizeof (struct sockaddr_un); 7319 } 7320 else 7321 # endif /* NETUNIX */ 7322 # if NETINET || NETINET6 7323 if (false 7324 # if NETINET 7325 || addr.sa.sa_family == AF_INET 7326 # endif /* NETINET */ 7327 # if NETINET6 7328 || addr.sa.sa_family == AF_INET6 7329 # endif /* NETINET6 */ 7330 ) 7331 { 7332 unsigned short port; 7333 7334 /* Parse port@host */ 7335 at = strchr(colon, '@'); 7336 if (at == NULL) 7337 { 7338 syserr("socket map \"%s\": bad address %s (expected port@host)", 7339 map->map_mname, colon); 7340 return false; 7341 } 7342 *at = '\0'; 7343 if (isascii(*colon) && isdigit(*colon)) 7344 port = htons((unsigned short) atoi(colon)); 7345 else 7346 { 7347 # ifdef NO_GETSERVBYNAME 7348 syserr("socket map \"%s\": invalid port number %s", 7349 map->map_mname, colon); 7350 return false; 7351 # else /* NO_GETSERVBYNAME */ 7352 register struct servent *sp; 7353 7354 sp = getservbyname(colon, "tcp"); 7355 if (sp == NULL) 7356 { 7357 syserr("socket map \"%s\": unknown port name %s", 7358 map->map_mname, colon); 7359 return false; 7360 } 7361 port = sp->s_port; 7362 # endif /* NO_GETSERVBYNAME */ 7363 } 7364 *at++ = '@'; 7365 if (*at == '[') 7366 { 7367 char *end; 7368 7369 end = strchr(at, ']'); 7370 if (end != NULL) 7371 { 7372 bool found = false; 7373 # if NETINET 7374 unsigned long hid = INADDR_NONE; 7375 # endif /* NETINET */ 7376 # if NETINET6 7377 struct sockaddr_in6 hid6; 7378 # endif /* NETINET6 */ 7379 7380 *end = '\0'; 7381 # if NETINET 7382 if (addr.sa.sa_family == AF_INET && 7383 (hid = inet_addr(&at[1])) != INADDR_NONE) 7384 { 7385 addr.sin.sin_addr.s_addr = hid; 7386 addr.sin.sin_port = port; 7387 found = true; 7388 } 7389 # endif /* NETINET */ 7390 # if NETINET6 7391 (void) memset(&hid6, '\0', sizeof hid6); 7392 if (addr.sa.sa_family == AF_INET6 && 7393 anynet_pton(AF_INET6, &at[1], 7394 &hid6.sin6_addr) == 1) 7395 { 7396 addr.sin6.sin6_addr = hid6.sin6_addr; 7397 addr.sin6.sin6_port = port; 7398 found = true; 7399 } 7400 # endif /* NETINET6 */ 7401 *end = ']'; 7402 if (!found) 7403 { 7404 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"", 7405 map->map_mname, at); 7406 return false; 7407 } 7408 } 7409 else 7410 { 7411 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"", 7412 map->map_mname, at); 7413 return false; 7414 } 7415 } 7416 else 7417 { 7418 hp = sm_gethostbyname(at, addr.sa.sa_family); 7419 if (hp == NULL) 7420 { 7421 syserr("socket map \"%s\": Unknown host name %s", 7422 map->map_mname, at); 7423 return false; 7424 } 7425 addr.sa.sa_family = hp->h_addrtype; 7426 switch (hp->h_addrtype) 7427 { 7428 # if NETINET 7429 case AF_INET: 7430 memmove(&addr.sin.sin_addr, 7431 hp->h_addr, INADDRSZ); 7432 addr.sin.sin_port = port; 7433 addrlen = sizeof (struct sockaddr_in); 7434 addrno = 1; 7435 break; 7436 # endif /* NETINET */ 7437 7438 # if NETINET6 7439 case AF_INET6: 7440 memmove(&addr.sin6.sin6_addr, 7441 hp->h_addr, IN6ADDRSZ); 7442 addr.sin6.sin6_port = port; 7443 addrlen = sizeof (struct sockaddr_in6); 7444 addrno = 1; 7445 break; 7446 # endif /* NETINET6 */ 7447 7448 default: 7449 syserr("socket map \"%s\": Unknown protocol for %s (%d)", 7450 map->map_mname, at, hp->h_addrtype); 7451 # if NETINET6 7452 freehostent(hp); 7453 # endif /* NETINET6 */ 7454 return false; 7455 } 7456 } 7457 } 7458 else 7459 # endif /* NETINET || NETINET6 */ 7460 { 7461 syserr("socket map \"%s\": unknown socket protocol", 7462 map->map_mname); 7463 return false; 7464 } 7465 7466 /* nope, actually connecting */ 7467 for (;;) 7468 { 7469 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 7470 if (sock < 0) 7471 { 7472 save_errno = errno; 7473 if (tTd(38, 5)) 7474 sm_dprintf("socket map \"%s\": error creating socket: %s\n", 7475 map->map_mname, 7476 sm_errstring(save_errno)); 7477 # if NETINET6 7478 if (hp != NULL) 7479 freehostent(hp); 7480 # endif /* NETINET6 */ 7481 return false; 7482 } 7483 7484 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0) 7485 break; 7486 7487 /* couldn't connect.... try next address */ 7488 save_errno = errno; 7489 p = CurHostName; 7490 CurHostName = at; 7491 if (tTd(38, 5)) 7492 sm_dprintf("socket_open (%s): open %s failed: %s\n", 7493 map->map_mname, at, sm_errstring(save_errno)); 7494 CurHostName = p; 7495 (void) close(sock); 7496 7497 /* try next address */ 7498 if (hp != NULL && hp->h_addr_list[addrno] != NULL) 7499 { 7500 switch (addr.sa.sa_family) 7501 { 7502 # if NETINET 7503 case AF_INET: 7504 memmove(&addr.sin.sin_addr, 7505 hp->h_addr_list[addrno++], 7506 INADDRSZ); 7507 break; 7508 # endif /* NETINET */ 7509 7510 # if NETINET6 7511 case AF_INET6: 7512 memmove(&addr.sin6.sin6_addr, 7513 hp->h_addr_list[addrno++], 7514 IN6ADDRSZ); 7515 break; 7516 # endif /* NETINET6 */ 7517 7518 default: 7519 if (tTd(38, 5)) 7520 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n", 7521 map->map_mname, at, 7522 hp->h_addrtype); 7523 # if NETINET6 7524 freehostent(hp); 7525 # endif /* NETINET6 */ 7526 return false; 7527 } 7528 continue; 7529 } 7530 p = CurHostName; 7531 CurHostName = at; 7532 if (tTd(38, 5)) 7533 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n", 7534 map->map_mname, sm_errstring(save_errno)); 7535 CurHostName = p; 7536 # if NETINET6 7537 if (hp != NULL) 7538 freehostent(hp); 7539 # endif /* NETINET6 */ 7540 return false; 7541 } 7542 # if NETINET6 7543 if (hp != NULL) 7544 { 7545 freehostent(hp); 7546 hp = NULL; 7547 } 7548 # endif /* NETINET6 */ 7549 if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd, 7550 SM_TIME_DEFAULT, 7551 (void *) &sock, 7552 SM_IO_RDWR, 7553 NULL)) == NULL) 7554 { 7555 close(sock); 7556 if (tTd(38, 2)) 7557 sm_dprintf("socket_open (%s): failed to create stream: %s\n", 7558 map->map_mname, sm_errstring(errno)); 7559 return false; 7560 } 7561 7562 /* Save connection for reuse */ 7563 s->s_socketmap = map; 7564 return true; 7565 } 7566 7567 /* 7568 ** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server 7569 ** 7570 ** Cache SOCKET connections based on the connection specifier 7571 ** and PID so we don't have multiple connections open to 7572 ** the same server for different maps. Need a separate connection 7573 ** per PID since a parent process may close the map before the 7574 ** child is done with it. 7575 ** 7576 ** Parameters: 7577 ** conn -- SOCKET map connection specifier 7578 ** 7579 ** Returns: 7580 ** Symbol table entry for the SOCKET connection. 7581 */ 7582 7583 static STAB * 7584 socket_map_findconn(conn) 7585 const char *conn; 7586 { 7587 char *nbuf; 7588 STAB *SM_NONVOLATILE s = NULL; 7589 7590 nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid); 7591 SM_TRY 7592 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER); 7593 SM_FINALLY 7594 sm_free(nbuf); 7595 SM_END_TRY 7596 return s; 7597 } 7598 7599 /* 7600 ** SOCKET_MAP_CLOSE -- close the socket 7601 */ 7602 7603 void 7604 socket_map_close(map) 7605 MAP *map; 7606 { 7607 STAB *s; 7608 MAP *smap; 7609 7610 if (tTd(38, 20)) 7611 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file, 7612 (long) CurrentPid); 7613 7614 /* Check if already closed */ 7615 if (map->map_db1 == NULL) 7616 { 7617 if (tTd(38, 20)) 7618 sm_dprintf("socket_map_close(%s) already closed\n", 7619 map->map_file); 7620 return; 7621 } 7622 sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT); 7623 7624 /* Mark all the maps that share the connection as closed */ 7625 s = socket_map_findconn(map->map_file); 7626 smap = s->s_socketmap; 7627 while (smap != NULL) 7628 { 7629 MAP *next; 7630 7631 if (tTd(38, 2) && smap != map) 7632 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n", 7633 map->map_mname, smap->map_mname); 7634 7635 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 7636 smap->map_db1 = NULL; 7637 next = smap->socket_map_next; 7638 smap->socket_map_next = NULL; 7639 smap = next; 7640 } 7641 s->s_socketmap = NULL; 7642 } 7643 7644 /* 7645 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table 7646 */ 7647 7648 char * 7649 socket_map_lookup(map, name, av, statp) 7650 MAP *map; 7651 char *name; 7652 char **av; 7653 int *statp; 7654 { 7655 unsigned int nettolen, replylen, recvlen; 7656 char *replybuf, *rval, *value, *status, *key; 7657 SM_FILE_T *f; 7658 char keybuf[MAXNAME + 1]; 7659 7660 replybuf = NULL; 7661 rval = NULL; 7662 f = (SM_FILE_T *)map->map_db1; 7663 if (tTd(38, 20)) 7664 sm_dprintf("socket_map_lookup(%s, %s) %s\n", 7665 map->map_mname, name, map->map_file); 7666 7667 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 7668 { 7669 nettolen = strlen(name); 7670 if (nettolen > sizeof keybuf - 1) 7671 nettolen = sizeof keybuf - 1; 7672 memmove(keybuf, name, nettolen); 7673 keybuf[nettolen] = '\0'; 7674 makelower(keybuf); 7675 key = keybuf; 7676 } 7677 else 7678 key = name; 7679 7680 nettolen = strlen(map->map_mname) + 1 + strlen(key); 7681 SM_ASSERT(nettolen > strlen(map->map_mname)); 7682 SM_ASSERT(nettolen > strlen(key)); 7683 if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,", 7684 nettolen, map->map_mname, key) == SM_IO_EOF) || 7685 (sm_io_flush(f, SM_TIME_DEFAULT) != 0) || 7686 (sm_io_error(f))) 7687 { 7688 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request", 7689 map->map_mname); 7690 *statp = EX_TEMPFAIL; 7691 goto errcl; 7692 } 7693 7694 if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1) 7695 { 7696 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply", 7697 map->map_mname); 7698 *statp = EX_TEMPFAIL; 7699 goto errcl; 7700 } 7701 if (replylen > SOCKETMAP_MAXL) 7702 { 7703 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u", 7704 map->map_mname, replylen); 7705 *statp = EX_TEMPFAIL; 7706 goto errcl; 7707 } 7708 if (sm_io_getc(f, SM_TIME_DEFAULT) != ':') 7709 { 7710 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply", 7711 map->map_mname); 7712 *statp = EX_TEMPFAIL; 7713 goto error; 7714 } 7715 7716 replybuf = (char *) sm_malloc(replylen + 1); 7717 if (replybuf == NULL) 7718 { 7719 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes", 7720 map->map_mname, replylen + 1); 7721 *statp = EX_OSERR; 7722 goto error; 7723 } 7724 7725 recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen); 7726 if (recvlen < replylen) 7727 { 7728 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters", 7729 map->map_mname, recvlen, replylen); 7730 *statp = EX_TEMPFAIL; 7731 goto errcl; 7732 } 7733 if (sm_io_getc(f, SM_TIME_DEFAULT) != ',') 7734 { 7735 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply", 7736 map->map_mname); 7737 *statp = EX_TEMPFAIL; 7738 goto errcl; 7739 } 7740 status = replybuf; 7741 replybuf[recvlen] = '\0'; 7742 value = strchr(replybuf, ' '); 7743 if (value != NULL) 7744 { 7745 *value = '\0'; 7746 value++; 7747 } 7748 if (strcmp(status, "OK") == 0) 7749 { 7750 *statp = EX_OK; 7751 7752 /* collect the return value */ 7753 if (bitset(MF_MATCHONLY, map->map_mflags)) 7754 rval = map_rewrite(map, key, strlen(key), NULL); 7755 else 7756 rval = map_rewrite(map, value, strlen(value), av); 7757 } 7758 else if (strcmp(status, "NOTFOUND") == 0) 7759 { 7760 *statp = EX_NOTFOUND; 7761 if (tTd(38, 20)) 7762 sm_dprintf("socket_map_lookup(%s): %s not found\n", 7763 map->map_mname, key); 7764 } 7765 else 7766 { 7767 if (tTd(38, 5)) 7768 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n", 7769 map->map_mname, key, status, 7770 value ? value : ""); 7771 if ((strcmp(status, "TEMP") == 0) || 7772 (strcmp(status, "TIMEOUT") == 0)) 7773 *statp = EX_TEMPFAIL; 7774 else if(strcmp(status, "PERM") == 0) 7775 *statp = EX_UNAVAILABLE; 7776 else 7777 *statp = EX_PROTOCOL; 7778 } 7779 7780 if (replybuf != NULL) 7781 sm_free(replybuf); 7782 return rval; 7783 7784 errcl: 7785 socket_map_close(map); 7786 error: 7787 if (replybuf != NULL) 7788 sm_free(replybuf); 7789 return rval; 7790 } 7791 #endif /* SOCKETMAP */ 7792