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