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