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