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