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