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