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