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