1 /* 2 * Copyright (c) 1998-2007 Sendmail, Inc. and its suppliers. 3 * All rights reserved. 4 * Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved. 5 * Copyright (c) 1992, 1993 6 * The Regents of the University of California. All rights reserved. 7 * 8 * By using this file, you agree to the terms and conditions set 9 * forth in the LICENSE file which can be found at the top level of 10 * the sendmail distribution. 11 * 12 */ 13 14 /* 15 * Copyright 1996-2007 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.699 2007/10/10 00:06:45 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 int rc; 3579 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2) 3580 extern char *__getldapaliasbyname(); 3581 char *answer; 3582 3583 answer = __getldapaliasbyname(name, &rc); 3584 #else 3585 char answer[MAXNAME + 1]; 3586 3587 rc = __getldapaliasbyname(name, answer, sizeof(answer)); 3588 #endif 3589 if (rc != 0) 3590 { 3591 if (tTd(38, 20)) 3592 sm_dprintf("getldapaliasbyname(%.100s) failed, errno=%d\n", 3593 name, errno); 3594 *statp = EX_NOTFOUND; 3595 return NULL; 3596 } 3597 *statp = EX_OK; 3598 if (tTd(38, 20)) 3599 sm_dprintf("getldapaliasbyname(%.100s) => %s\n", name, 3600 answer); 3601 if (bitset(MF_MATCHONLY, map->map_mflags)) 3602 result = map_rewrite(map, name, strlen(name), NULL); 3603 else 3604 result = map_rewrite(map, answer, strlen(answer), av); 3605 #if defined(GETLDAPALIASBYNAME_VERSION) && (GETLDAPALIASBYNAME_VERSION >= 2) 3606 free(answer); 3607 #endif 3608 return result; 3609 } 3610 #endif /* defined(SUN_EXTENSIONS) && defined(SUN_SIMPLIFIED_LDAP) && ... */ 3611 3612 /* Get ldap struct pointer from map */ 3613 lmap = (SM_LDAP_STRUCT *) map->map_db1; 3614 sm_ldap_setopts(lmap->ldap_ld, lmap); 3615 3616 if (lmap->ldap_multi_args) 3617 { 3618 SM_REQUIRE(av != NULL); 3619 memset(argv, '\0', sizeof(argv)); 3620 for (i = 0; i < SM_LDAP_ARGS && av[i] != NULL; i++) 3621 { 3622 argv[i] = sm_strdup(av[i]); 3623 if (argv[i] == NULL) 3624 { 3625 int save_errno, j; 3626 3627 save_errno = errno; 3628 for (j = 0; j < i && argv[j] != NULL; j++) 3629 SM_FREE(argv[j]); 3630 *statp = EX_TEMPFAIL; 3631 errno = save_errno; 3632 return NULL; 3633 } 3634 3635 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3636 SM_CONVERT_ID(av[i]); 3637 } 3638 } 3639 else 3640 { 3641 (void) sm_strlcpy(keybuf, name, sizeof(keybuf)); 3642 3643 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3644 SM_CONVERT_ID(keybuf); 3645 } 3646 3647 if (tTd(38, 20)) 3648 { 3649 if (lmap->ldap_multi_args) 3650 { 3651 sm_dprintf("ldapmap_lookup(%s, argv)\n", 3652 map->map_mname); 3653 for (i = 0; i < SM_LDAP_ARGS; i++) 3654 { 3655 sm_dprintf(" argv[%d] = %s\n", i, 3656 argv[i] == NULL ? "NULL" : argv[i]); 3657 } 3658 } 3659 else 3660 { 3661 sm_dprintf("ldapmap_lookup(%s, %s)\n", 3662 map->map_mname, name); 3663 } 3664 } 3665 3666 if (lmap->ldap_multi_args) 3667 { 3668 msgid = sm_ldap_search_m(lmap, argv); 3669 3670 /* free the argv array and its content, no longer needed */ 3671 for (i = 0; i < SM_LDAP_ARGS && argv[i] != NULL; i++) 3672 SM_FREE(argv[i]); 3673 } 3674 else 3675 msgid = sm_ldap_search(lmap, keybuf); 3676 if (msgid == SM_LDAP_ERR) 3677 { 3678 errno = sm_ldap_geterrno(lmap->ldap_ld) + E_LDAPBASE; 3679 save_errno = errno; 3680 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3681 { 3682 /* 3683 ** Do not include keybuf as this error may be shown 3684 ** to outsiders. 3685 */ 3686 3687 if (bitset(MF_NODEFER, map->map_mflags)) 3688 syserr("Error in ldap_search in map %s", 3689 map->map_mname); 3690 else 3691 syserr("451 4.3.5 Error in ldap_search in map %s", 3692 map->map_mname); 3693 } 3694 *statp = EX_TEMPFAIL; 3695 switch (save_errno - E_LDAPBASE) 3696 { 3697 # ifdef LDAP_SERVER_DOWN 3698 case LDAP_SERVER_DOWN: 3699 # endif /* LDAP_SERVER_DOWN */ 3700 case LDAP_TIMEOUT: 3701 case LDAP_UNAVAILABLE: 3702 /* server disappeared, try reopen on next search */ 3703 ldapmap_close(map); 3704 break; 3705 } 3706 errno = save_errno; 3707 return NULL; 3708 } 3709 #if SM_LDAP_ERROR_ON_MISSING_ARGS 3710 else if (msgid == SM_LDAP_ERR_ARG_MISS) 3711 { 3712 if (bitset(MF_NODEFER, map->map_mflags)) 3713 syserr("Error in ldap_search in map %s, too few arguments", 3714 map->map_mname); 3715 else 3716 syserr("554 5.3.5 Error in ldap_search in map %s, too few arguments", 3717 map->map_mname); 3718 *statp = EX_CONFIG; 3719 return NULL; 3720 } 3721 #endif /* SM_LDAP_ERROR_ON_MISSING_ARGS */ 3722 3723 *statp = EX_NOTFOUND; 3724 vp = NULL; 3725 3726 flags = 0; 3727 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 3728 flags |= SM_LDAP_SINGLEMATCH; 3729 if (bitset(MF_MATCHONLY, map->map_mflags)) 3730 flags |= SM_LDAP_MATCHONLY; 3731 # if _FFR_LDAP_SINGLEDN 3732 if (bitset(MF_SINGLEDN, map->map_mflags)) 3733 flags |= SM_LDAP_SINGLEDN; 3734 # endif /* _FFR_LDAP_SINGLEDN */ 3735 3736 /* Create an rpool for search related memory usage */ 3737 rpool = sm_rpool_new_x(NULL); 3738 3739 p = NULL; 3740 *statp = sm_ldap_results(lmap, msgid, flags, map->map_coldelim, 3741 rpool, &p, &plen, &psize, NULL); 3742 save_errno = errno; 3743 3744 /* Copy result so rpool can be freed */ 3745 if (*statp == EX_OK && p != NULL) 3746 vp = newstr(p); 3747 sm_rpool_free(rpool); 3748 3749 /* need to restart LDAP connection? */ 3750 if (*statp == EX_RESTART) 3751 { 3752 *statp = EX_TEMPFAIL; 3753 ldapmap_close(map); 3754 } 3755 3756 errno = save_errno; 3757 if (*statp != EX_OK && *statp != EX_NOTFOUND) 3758 { 3759 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3760 { 3761 if (bitset(MF_NODEFER, map->map_mflags)) 3762 syserr("Error getting LDAP results in map %s", 3763 map->map_mname); 3764 else 3765 syserr("451 4.3.5 Error getting LDAP results in map %s", 3766 map->map_mname); 3767 } 3768 errno = save_errno; 3769 return NULL; 3770 } 3771 3772 /* Did we match anything? */ 3773 if (vp == NULL && !bitset(MF_MATCHONLY, map->map_mflags)) 3774 return NULL; 3775 3776 if (*statp == EX_OK) 3777 { 3778 if (LogLevel > 9) 3779 sm_syslog(LOG_INFO, CurEnv->e_id, 3780 "ldap %.100s => %s", name, 3781 vp == NULL ? "<NULL>" : vp); 3782 if (bitset(MF_MATCHONLY, map->map_mflags)) 3783 result = map_rewrite(map, name, strlen(name), NULL); 3784 else 3785 { 3786 /* vp != NULL according to test above */ 3787 result = map_rewrite(map, vp, strlen(vp), av); 3788 } 3789 if (vp != NULL) 3790 sm_free(vp); /* XXX */ 3791 } 3792 return result; 3793 } 3794 3795 /* 3796 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server 3797 ** 3798 ** Cache LDAP connections based on the host, port, bind DN, 3799 ** secret, and PID so we don't have multiple connections open to 3800 ** the same server for different maps. Need a separate connection 3801 ** per PID since a parent process may close the map before the 3802 ** child is done with it. 3803 ** 3804 ** Parameters: 3805 ** lmap -- LDAP map information 3806 ** 3807 ** Returns: 3808 ** Symbol table entry for the LDAP connection. 3809 */ 3810 3811 static STAB * 3812 ldapmap_findconn(lmap) 3813 SM_LDAP_STRUCT *lmap; 3814 { 3815 char *format; 3816 char *nbuf; 3817 char *id; 3818 STAB *SM_NONVOLATILE s = NULL; 3819 3820 if (lmap->ldap_host != NULL) 3821 id = lmap->ldap_host; 3822 else if (lmap->ldap_uri != NULL) 3823 id = lmap->ldap_uri; 3824 else 3825 id = "localhost"; 3826 3827 format = "%s%c%d%c%d%c%s%c%s%d"; 3828 nbuf = sm_stringf_x(format, 3829 id, 3830 CONDELSE, 3831 lmap->ldap_port, 3832 CONDELSE, 3833 lmap->ldap_version, 3834 CONDELSE, 3835 (lmap->ldap_binddn == NULL ? "" 3836 : lmap->ldap_binddn), 3837 CONDELSE, 3838 (lmap->ldap_secret == NULL ? "" 3839 : lmap->ldap_secret), 3840 (int) CurrentPid); 3841 SM_TRY 3842 s = stab(nbuf, ST_LMAP, ST_ENTER); 3843 SM_FINALLY 3844 sm_free(nbuf); 3845 SM_END_TRY 3846 return s; 3847 } 3848 /* 3849 ** LDAPMAP_PARSEARGS -- parse ldap map definition args. 3850 */ 3851 3852 static struct lamvalues LDAPAuthMethods[] = 3853 { 3854 { "none", LDAP_AUTH_NONE }, 3855 { "simple", LDAP_AUTH_SIMPLE }, 3856 # ifdef LDAP_AUTH_KRBV4 3857 { "krbv4", LDAP_AUTH_KRBV4 }, 3858 # endif /* LDAP_AUTH_KRBV4 */ 3859 { NULL, 0 } 3860 }; 3861 3862 static struct ladvalues LDAPAliasDereference[] = 3863 { 3864 { "never", LDAP_DEREF_NEVER }, 3865 { "always", LDAP_DEREF_ALWAYS }, 3866 { "search", LDAP_DEREF_SEARCHING }, 3867 { "find", LDAP_DEREF_FINDING }, 3868 { NULL, 0 } 3869 }; 3870 3871 static struct lssvalues LDAPSearchScope[] = 3872 { 3873 { "base", LDAP_SCOPE_BASE }, 3874 { "one", LDAP_SCOPE_ONELEVEL }, 3875 { "sub", LDAP_SCOPE_SUBTREE }, 3876 { NULL, 0 } 3877 }; 3878 3879 bool 3880 ldapmap_parseargs(map, args) 3881 MAP *map; 3882 char *args; 3883 { 3884 bool secretread = true; 3885 bool attrssetup = false; 3886 int i; 3887 register char *p = args; 3888 SM_LDAP_STRUCT *lmap; 3889 struct lamvalues *lam; 3890 struct ladvalues *lad; 3891 struct lssvalues *lss; 3892 char ldapfilt[MAXLINE]; 3893 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD]; 3894 3895 /* Get ldap struct pointer from map */ 3896 lmap = (SM_LDAP_STRUCT *) map->map_db1; 3897 3898 /* Check if setting the initial LDAP defaults */ 3899 if (lmap == NULL || lmap != LDAPDefaults) 3900 { 3901 /* We need to alloc an SM_LDAP_STRUCT struct */ 3902 lmap = (SM_LDAP_STRUCT *) xalloc(sizeof(*lmap)); 3903 if (LDAPDefaults == NULL) 3904 sm_ldap_clear(lmap); 3905 else 3906 STRUCTCOPY(*LDAPDefaults, *lmap); 3907 } 3908 3909 /* there is no check whether there is really an argument */ 3910 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 3911 map->map_spacesub = SpaceSub; /* default value */ 3912 3913 /* Check if setting up an alias or file class LDAP map */ 3914 if (bitset(MF_ALIAS, map->map_mflags)) 3915 { 3916 /* Comma separate if used as an alias file */ 3917 map->map_coldelim = ','; 3918 if (*args == '\0') 3919 { 3920 int n; 3921 char *lc; 3922 char jbuf[MAXHOSTNAMELEN]; 3923 char lcbuf[MAXLINE]; 3924 3925 /* Get $j */ 3926 expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope); 3927 if (jbuf[0] == '\0') 3928 { 3929 (void) sm_strlcpy(jbuf, "localhost", 3930 sizeof(jbuf)); 3931 } 3932 3933 lc = macvalue(macid("{sendmailMTACluster}"), CurEnv); 3934 if (lc == NULL) 3935 lc = ""; 3936 else 3937 { 3938 expand(lc, lcbuf, sizeof(lcbuf), CurEnv); 3939 lc = lcbuf; 3940 } 3941 3942 n = sm_snprintf(ldapfilt, sizeof(ldapfilt), 3943 "(&(objectClass=sendmailMTAAliasObject)(sendmailMTAAliasGrouping=aliases)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))(sendmailMTAKey=%%0))", 3944 lc, jbuf); 3945 if (n >= sizeof(ldapfilt)) 3946 { 3947 syserr("%s: Default LDAP string too long", 3948 map->map_mname); 3949 return false; 3950 } 3951 3952 /* default args for an alias LDAP entry */ 3953 lmap->ldap_filter = ldapfilt; 3954 lmap->ldap_attr[0] = "objectClass"; 3955 lmap->ldap_attr_type[0] = SM_LDAP_ATTR_OBJCLASS; 3956 lmap->ldap_attr_needobjclass[0] = NULL; 3957 lmap->ldap_attr[1] = "sendmailMTAAliasValue"; 3958 lmap->ldap_attr_type[1] = SM_LDAP_ATTR_NORMAL; 3959 lmap->ldap_attr_needobjclass[1] = NULL; 3960 lmap->ldap_attr[2] = "sendmailMTAAliasSearch"; 3961 lmap->ldap_attr_type[2] = SM_LDAP_ATTR_FILTER; 3962 lmap->ldap_attr_needobjclass[2] = "sendmailMTAMapObject"; 3963 lmap->ldap_attr[3] = "sendmailMTAAliasURL"; 3964 lmap->ldap_attr_type[3] = SM_LDAP_ATTR_URL; 3965 lmap->ldap_attr_needobjclass[3] = "sendmailMTAMapObject"; 3966 lmap->ldap_attr[4] = NULL; 3967 lmap->ldap_attr_type[4] = SM_LDAP_ATTR_NONE; 3968 lmap->ldap_attr_needobjclass[4] = NULL; 3969 attrssetup = true; 3970 } 3971 } 3972 else if (bitset(MF_FILECLASS, map->map_mflags)) 3973 { 3974 /* Space separate if used as a file class file */ 3975 map->map_coldelim = ' '; 3976 } 3977 3978 for (;;) 3979 { 3980 while (isascii(*p) && isspace(*p)) 3981 p++; 3982 if (*p != '-') 3983 break; 3984 switch (*++p) 3985 { 3986 case 'A': 3987 map->map_mflags |= MF_APPEND; 3988 break; 3989 3990 case 'a': 3991 map->map_app = ++p; 3992 break; 3993 3994 case 'D': 3995 map->map_mflags |= MF_DEFER; 3996 break; 3997 3998 case 'f': 3999 map->map_mflags |= MF_NOFOLDCASE; 4000 break; 4001 4002 case 'm': 4003 map->map_mflags |= MF_MATCHONLY; 4004 break; 4005 4006 case 'N': 4007 map->map_mflags |= MF_INCLNULL; 4008 map->map_mflags &= ~MF_TRY0NULL; 4009 break; 4010 4011 case 'O': 4012 map->map_mflags &= ~MF_TRY1NULL; 4013 break; 4014 4015 case 'o': 4016 map->map_mflags |= MF_OPTIONAL; 4017 break; 4018 4019 case 'q': 4020 map->map_mflags |= MF_KEEPQUOTES; 4021 break; 4022 4023 case 'S': 4024 map->map_spacesub = *++p; 4025 break; 4026 4027 case 'T': 4028 map->map_tapp = ++p; 4029 break; 4030 4031 case 't': 4032 map->map_mflags |= MF_NODEFER; 4033 break; 4034 4035 case 'z': 4036 if (*++p != '\\') 4037 map->map_coldelim = *p; 4038 else 4039 { 4040 switch (*++p) 4041 { 4042 case 'n': 4043 map->map_coldelim = '\n'; 4044 break; 4045 4046 case 't': 4047 map->map_coldelim = '\t'; 4048 break; 4049 4050 default: 4051 map->map_coldelim = '\\'; 4052 } 4053 } 4054 break; 4055 4056 /* Start of ldapmap specific args */ 4057 case '1': 4058 map->map_mflags |= MF_SINGLEMATCH; 4059 break; 4060 4061 # if _FFR_LDAP_SINGLEDN 4062 case '2': 4063 map->map_mflags |= MF_SINGLEDN; 4064 break; 4065 # endif /* _FFR_LDAP_SINGLEDN */ 4066 4067 case 'b': /* search base */ 4068 while (isascii(*++p) && isspace(*p)) 4069 continue; 4070 lmap->ldap_base = p; 4071 break; 4072 4073 # if _FFR_LDAP_NETWORK_TIMEOUT 4074 case 'c': /* network (connect) timeout */ 4075 while (isascii(*++p) && isspace(*p)) 4076 continue; 4077 lmap->ldap_networktmo.tv_sec = atoi(p); 4078 break; 4079 # endif /* _FFR_LDAP_NETWORK_TIMEOUT */ 4080 4081 case 'd': /* Dn to bind to server as */ 4082 while (isascii(*++p) && isspace(*p)) 4083 continue; 4084 lmap->ldap_binddn = p; 4085 break; 4086 4087 case 'H': /* Use LDAP URI */ 4088 # if !USE_LDAP_INIT 4089 syserr("Must compile with -DUSE_LDAP_INIT to use LDAP URIs (-H) in map %s", 4090 map->map_mname); 4091 return false; 4092 # else /* !USE_LDAP_INIT */ 4093 if (lmap->ldap_host != NULL) 4094 { 4095 syserr("Can not specify both an LDAP host and an LDAP URI in map %s", 4096 map->map_mname); 4097 return false; 4098 } 4099 while (isascii(*++p) && isspace(*p)) 4100 continue; 4101 lmap->ldap_uri = p; 4102 break; 4103 # endif /* !USE_LDAP_INIT */ 4104 4105 case 'h': /* ldap host */ 4106 while (isascii(*++p) && isspace(*p)) 4107 continue; 4108 if (lmap->ldap_uri != NULL) 4109 { 4110 syserr("Can not specify both an LDAP host and an LDAP URI in map %s", 4111 map->map_mname); 4112 return false; 4113 } 4114 lmap->ldap_host = p; 4115 break; 4116 4117 case 'K': 4118 lmap->ldap_multi_args = true; 4119 break; 4120 4121 case 'k': /* search field */ 4122 while (isascii(*++p) && isspace(*p)) 4123 continue; 4124 lmap->ldap_filter = p; 4125 break; 4126 4127 case 'l': /* time limit */ 4128 while (isascii(*++p) && isspace(*p)) 4129 continue; 4130 lmap->ldap_timelimit = atoi(p); 4131 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit; 4132 break; 4133 4134 case 'M': /* Method for binding */ 4135 while (isascii(*++p) && isspace(*p)) 4136 continue; 4137 4138 if (sm_strncasecmp(p, "LDAP_AUTH_", 10) == 0) 4139 p += 10; 4140 4141 for (lam = LDAPAuthMethods; 4142 lam != NULL && lam->lam_name != NULL; lam++) 4143 { 4144 if (sm_strncasecmp(p, lam->lam_name, 4145 strlen(lam->lam_name)) == 0) 4146 break; 4147 } 4148 if (lam->lam_name != NULL) 4149 lmap->ldap_method = lam->lam_code; 4150 else 4151 { 4152 /* bad config line */ 4153 if (!bitset(MCF_OPTFILE, 4154 map->map_class->map_cflags)) 4155 { 4156 char *ptr; 4157 4158 if ((ptr = strchr(p, ' ')) != NULL) 4159 *ptr = '\0'; 4160 syserr("Method for binding must be [none|simple|krbv4] (not %s) in map %s", 4161 p, map->map_mname); 4162 if (ptr != NULL) 4163 *ptr = ' '; 4164 return false; 4165 } 4166 } 4167 break; 4168 4169 case 'n': /* retrieve attribute names only */ 4170 lmap->ldap_attrsonly = LDAPMAP_TRUE; 4171 break; 4172 4173 /* 4174 ** This is a string that is dependent on the 4175 ** method used defined by 'M'. 4176 */ 4177 4178 case 'P': /* Secret password for binding */ 4179 while (isascii(*++p) && isspace(*p)) 4180 continue; 4181 lmap->ldap_secret = p; 4182 secretread = false; 4183 break; 4184 4185 case 'p': /* ldap port */ 4186 while (isascii(*++p) && isspace(*p)) 4187 continue; 4188 lmap->ldap_port = atoi(p); 4189 break; 4190 4191 /* args stolen from ldapsearch.c */ 4192 case 'R': /* don't auto chase referrals */ 4193 # ifdef LDAP_REFERRALS 4194 lmap->ldap_options &= ~LDAP_OPT_REFERRALS; 4195 # else /* LDAP_REFERRALS */ 4196 syserr("compile with -DLDAP_REFERRALS for referral support"); 4197 # endif /* LDAP_REFERRALS */ 4198 break; 4199 4200 case 'r': /* alias dereferencing */ 4201 while (isascii(*++p) && isspace(*p)) 4202 continue; 4203 4204 if (sm_strncasecmp(p, "LDAP_DEREF_", 11) == 0) 4205 p += 11; 4206 4207 for (lad = LDAPAliasDereference; 4208 lad != NULL && lad->lad_name != NULL; lad++) 4209 { 4210 if (sm_strncasecmp(p, lad->lad_name, 4211 strlen(lad->lad_name)) == 0) 4212 break; 4213 } 4214 if (lad->lad_name != NULL) 4215 lmap->ldap_deref = lad->lad_code; 4216 else 4217 { 4218 /* bad config line */ 4219 if (!bitset(MCF_OPTFILE, 4220 map->map_class->map_cflags)) 4221 { 4222 char *ptr; 4223 4224 if ((ptr = strchr(p, ' ')) != NULL) 4225 *ptr = '\0'; 4226 syserr("Deref must be [never|always|search|find] (not %s) in map %s", 4227 p, map->map_mname); 4228 if (ptr != NULL) 4229 *ptr = ' '; 4230 return false; 4231 } 4232 } 4233 break; 4234 4235 case 's': /* search scope */ 4236 while (isascii(*++p) && isspace(*p)) 4237 continue; 4238 4239 if (sm_strncasecmp(p, "LDAP_SCOPE_", 11) == 0) 4240 p += 11; 4241 4242 for (lss = LDAPSearchScope; 4243 lss != NULL && lss->lss_name != NULL; lss++) 4244 { 4245 if (sm_strncasecmp(p, lss->lss_name, 4246 strlen(lss->lss_name)) == 0) 4247 break; 4248 } 4249 if (lss->lss_name != NULL) 4250 lmap->ldap_scope = lss->lss_code; 4251 else 4252 { 4253 /* bad config line */ 4254 if (!bitset(MCF_OPTFILE, 4255 map->map_class->map_cflags)) 4256 { 4257 char *ptr; 4258 4259 if ((ptr = strchr(p, ' ')) != NULL) 4260 *ptr = '\0'; 4261 syserr("Scope must be [base|one|sub] (not %s) in map %s", 4262 p, map->map_mname); 4263 if (ptr != NULL) 4264 *ptr = ' '; 4265 return false; 4266 } 4267 } 4268 break; 4269 4270 case 'V': 4271 if (*++p != '\\') 4272 lmap->ldap_attrsep = *p; 4273 else 4274 { 4275 switch (*++p) 4276 { 4277 case 'n': 4278 lmap->ldap_attrsep = '\n'; 4279 break; 4280 4281 case 't': 4282 lmap->ldap_attrsep = '\t'; 4283 break; 4284 4285 default: 4286 lmap->ldap_attrsep = '\\'; 4287 } 4288 } 4289 break; 4290 4291 case 'v': /* attr to return */ 4292 while (isascii(*++p) && isspace(*p)) 4293 continue; 4294 lmap->ldap_attr[0] = p; 4295 lmap->ldap_attr[1] = NULL; 4296 break; 4297 4298 case 'w': 4299 /* -w should be for passwd, -P should be for version */ 4300 while (isascii(*++p) && isspace(*p)) 4301 continue; 4302 lmap->ldap_version = atoi(p); 4303 # ifdef LDAP_VERSION_MAX 4304 if (lmap->ldap_version > LDAP_VERSION_MAX) 4305 { 4306 syserr("LDAP version %d exceeds max of %d in map %s", 4307 lmap->ldap_version, LDAP_VERSION_MAX, 4308 map->map_mname); 4309 return false; 4310 } 4311 # endif /* LDAP_VERSION_MAX */ 4312 # ifdef LDAP_VERSION_MIN 4313 if (lmap->ldap_version < LDAP_VERSION_MIN) 4314 { 4315 syserr("LDAP version %d is lower than min of %d in map %s", 4316 lmap->ldap_version, LDAP_VERSION_MIN, 4317 map->map_mname); 4318 return false; 4319 } 4320 # endif /* LDAP_VERSION_MIN */ 4321 break; 4322 4323 case 'Z': 4324 while (isascii(*++p) && isspace(*p)) 4325 continue; 4326 lmap->ldap_sizelimit = atoi(p); 4327 break; 4328 4329 default: 4330 syserr("Illegal option %c map %s", *p, map->map_mname); 4331 break; 4332 } 4333 4334 /* need to account for quoted strings here */ 4335 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 4336 { 4337 if (*p == '"') 4338 { 4339 while (*++p != '"' && *p != '\0') 4340 continue; 4341 if (*p != '\0') 4342 p++; 4343 } 4344 else 4345 p++; 4346 } 4347 4348 if (*p != '\0') 4349 *p++ = '\0'; 4350 } 4351 4352 if (map->map_app != NULL) 4353 map->map_app = newstr(ldapmap_dequote(map->map_app)); 4354 if (map->map_tapp != NULL) 4355 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp)); 4356 4357 /* 4358 ** We need to swallow up all the stuff into a struct 4359 ** and dump it into map->map_dbptr1 4360 */ 4361 4362 if (lmap->ldap_host != NULL && 4363 (LDAPDefaults == NULL || 4364 LDAPDefaults == lmap || 4365 LDAPDefaults->ldap_host != lmap->ldap_host)) 4366 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host)); 4367 map->map_domain = lmap->ldap_host; 4368 4369 if (lmap->ldap_uri != NULL && 4370 (LDAPDefaults == NULL || 4371 LDAPDefaults == lmap || 4372 LDAPDefaults->ldap_uri != lmap->ldap_uri)) 4373 lmap->ldap_uri = newstr(ldapmap_dequote(lmap->ldap_uri)); 4374 map->map_domain = lmap->ldap_uri; 4375 4376 if (lmap->ldap_binddn != NULL && 4377 (LDAPDefaults == NULL || 4378 LDAPDefaults == lmap || 4379 LDAPDefaults->ldap_binddn != lmap->ldap_binddn)) 4380 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn)); 4381 4382 if (lmap->ldap_secret != NULL && 4383 (LDAPDefaults == NULL || 4384 LDAPDefaults == lmap || 4385 LDAPDefaults->ldap_secret != lmap->ldap_secret)) 4386 { 4387 SM_FILE_T *sfd; 4388 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES; 4389 4390 if (DontLockReadFiles) 4391 sff |= SFF_NOLOCK; 4392 4393 /* need to use method to map secret to passwd string */ 4394 switch (lmap->ldap_method) 4395 { 4396 case LDAP_AUTH_NONE: 4397 /* Do nothing */ 4398 break; 4399 4400 case LDAP_AUTH_SIMPLE: 4401 4402 /* 4403 ** Secret is the name of a file with 4404 ** the first line as the password. 4405 */ 4406 4407 /* Already read in the secret? */ 4408 if (secretread) 4409 break; 4410 4411 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret), 4412 O_RDONLY, 0, sff); 4413 if (sfd == NULL) 4414 { 4415 syserr("LDAP map: cannot open secret %s", 4416 ldapmap_dequote(lmap->ldap_secret)); 4417 return false; 4418 } 4419 lmap->ldap_secret = sfgets(m_tmp, sizeof(m_tmp), 4420 sfd, TimeOuts.to_fileopen, 4421 "ldapmap_parseargs"); 4422 (void) sm_io_close(sfd, SM_TIME_DEFAULT); 4423 if (strlen(m_tmp) > LDAPMAP_MAX_PASSWD) 4424 { 4425 syserr("LDAP map: secret in %s too long", 4426 ldapmap_dequote(lmap->ldap_secret)); 4427 return false; 4428 } 4429 if (lmap->ldap_secret != NULL && 4430 strlen(m_tmp) > 0) 4431 { 4432 /* chomp newline */ 4433 if (m_tmp[strlen(m_tmp) - 1] == '\n') 4434 m_tmp[strlen(m_tmp) - 1] = '\0'; 4435 4436 lmap->ldap_secret = m_tmp; 4437 } 4438 break; 4439 4440 # ifdef LDAP_AUTH_KRBV4 4441 case LDAP_AUTH_KRBV4: 4442 4443 /* 4444 ** Secret is where the ticket file is 4445 ** stashed 4446 */ 4447 4448 (void) sm_snprintf(m_tmp, sizeof(m_tmp), 4449 "KRBTKFILE=%s", 4450 ldapmap_dequote(lmap->ldap_secret)); 4451 lmap->ldap_secret = m_tmp; 4452 break; 4453 # endif /* LDAP_AUTH_KRBV4 */ 4454 4455 default: /* Should NEVER get here */ 4456 syserr("LDAP map: Illegal value in lmap method"); 4457 return false; 4458 /* NOTREACHED */ 4459 break; 4460 } 4461 } 4462 4463 if (lmap->ldap_secret != NULL && 4464 (LDAPDefaults == NULL || 4465 LDAPDefaults == lmap || 4466 LDAPDefaults->ldap_secret != lmap->ldap_secret)) 4467 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret)); 4468 4469 if (lmap->ldap_base != NULL && 4470 (LDAPDefaults == NULL || 4471 LDAPDefaults == lmap || 4472 LDAPDefaults->ldap_base != lmap->ldap_base)) 4473 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base)); 4474 4475 /* 4476 ** Save the server from extra work. If request is for a single 4477 ** match, tell the server to only return enough records to 4478 ** determine if there is a single match or not. This can not 4479 ** be one since the server would only return one and we wouldn't 4480 ** know if there were others available. 4481 */ 4482 4483 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 4484 lmap->ldap_sizelimit = 2; 4485 4486 /* If setting defaults, don't process ldap_filter and ldap_attr */ 4487 if (lmap == LDAPDefaults) 4488 return true; 4489 4490 if (lmap->ldap_filter != NULL) 4491 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter)); 4492 else 4493 { 4494 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 4495 { 4496 syserr("No filter given in map %s", map->map_mname); 4497 return false; 4498 } 4499 } 4500 4501 if (!attrssetup && lmap->ldap_attr[0] != NULL) 4502 { 4503 bool recurse = false; 4504 bool normalseen = false; 4505 4506 i = 0; 4507 p = ldapmap_dequote(lmap->ldap_attr[0]); 4508 lmap->ldap_attr[0] = NULL; 4509 4510 /* Prime the attr list with the objectClass attribute */ 4511 lmap->ldap_attr[i] = "objectClass"; 4512 lmap->ldap_attr_type[i] = SM_LDAP_ATTR_OBJCLASS; 4513 lmap->ldap_attr_needobjclass[i] = NULL; 4514 i++; 4515 4516 while (p != NULL) 4517 { 4518 char *v; 4519 4520 while (isascii(*p) && isspace(*p)) 4521 p++; 4522 if (*p == '\0') 4523 break; 4524 v = p; 4525 p = strchr(v, ','); 4526 if (p != NULL) 4527 *p++ = '\0'; 4528 4529 if (i >= LDAPMAP_MAX_ATTR) 4530 { 4531 syserr("Too many return attributes in %s (max %d)", 4532 map->map_mname, LDAPMAP_MAX_ATTR); 4533 return false; 4534 } 4535 if (*v != '\0') 4536 { 4537 int j; 4538 int use; 4539 char *type; 4540 char *needobjclass; 4541 4542 type = strchr(v, ':'); 4543 if (type != NULL) 4544 { 4545 *type++ = '\0'; 4546 needobjclass = strchr(type, ':'); 4547 if (needobjclass != NULL) 4548 *needobjclass++ = '\0'; 4549 } 4550 else 4551 { 4552 needobjclass = NULL; 4553 } 4554 4555 use = i; 4556 4557 /* allow override on "objectClass" type */ 4558 if (sm_strcasecmp(v, "objectClass") == 0 && 4559 lmap->ldap_attr_type[0] == SM_LDAP_ATTR_OBJCLASS) 4560 { 4561 use = 0; 4562 } 4563 else 4564 { 4565 /* 4566 ** Don't add something to attribute 4567 ** list twice. 4568 */ 4569 4570 for (j = 1; j < i; j++) 4571 { 4572 if (sm_strcasecmp(v, lmap->ldap_attr[j]) == 0) 4573 { 4574 syserr("Duplicate attribute (%s) in %s", 4575 v, map->map_mname); 4576 return false; 4577 } 4578 } 4579 4580 lmap->ldap_attr[use] = newstr(v); 4581 if (needobjclass != NULL && 4582 *needobjclass != '\0' && 4583 *needobjclass != '*') 4584 { 4585 lmap->ldap_attr_needobjclass[use] = newstr(needobjclass); 4586 } 4587 else 4588 { 4589 lmap->ldap_attr_needobjclass[use] = NULL; 4590 } 4591 4592 } 4593 4594 if (type != NULL && *type != '\0') 4595 { 4596 if (sm_strcasecmp(type, "dn") == 0) 4597 { 4598 recurse = true; 4599 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_DN; 4600 } 4601 else if (sm_strcasecmp(type, "filter") == 0) 4602 { 4603 recurse = true; 4604 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_FILTER; 4605 } 4606 else if (sm_strcasecmp(type, "url") == 0) 4607 { 4608 recurse = true; 4609 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_URL; 4610 } 4611 else if (sm_strcasecmp(type, "normal") == 0) 4612 { 4613 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL; 4614 normalseen = true; 4615 } 4616 else 4617 { 4618 syserr("Unknown attribute type (%s) in %s", 4619 type, map->map_mname); 4620 return false; 4621 } 4622 } 4623 else 4624 { 4625 lmap->ldap_attr_type[use] = SM_LDAP_ATTR_NORMAL; 4626 normalseen = true; 4627 } 4628 i++; 4629 } 4630 } 4631 lmap->ldap_attr[i] = NULL; 4632 4633 /* Set in case needed in future code */ 4634 attrssetup = true; 4635 4636 if (recurse && !normalseen) 4637 { 4638 syserr("LDAP recursion requested in %s but no returnable attribute given", 4639 map->map_mname); 4640 return false; 4641 } 4642 if (recurse && lmap->ldap_attrsonly == LDAPMAP_TRUE) 4643 { 4644 syserr("LDAP recursion requested in %s can not be used with -n", 4645 map->map_mname); 4646 return false; 4647 } 4648 } 4649 map->map_db1 = (ARBPTR_T) lmap; 4650 return true; 4651 } 4652 4653 /* 4654 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf 4655 ** 4656 ** Parameters: 4657 ** spec -- map argument string from LDAPDefaults option 4658 ** 4659 ** Returns: 4660 ** None. 4661 */ 4662 4663 void 4664 ldapmap_set_defaults(spec) 4665 char *spec; 4666 { 4667 STAB *class; 4668 MAP map; 4669 4670 /* Allocate and set the default values */ 4671 if (LDAPDefaults == NULL) 4672 LDAPDefaults = (SM_LDAP_STRUCT *) xalloc(sizeof(*LDAPDefaults)); 4673 sm_ldap_clear(LDAPDefaults); 4674 4675 memset(&map, '\0', sizeof(map)); 4676 4677 /* look up the class */ 4678 class = stab("ldap", ST_MAPCLASS, ST_FIND); 4679 if (class == NULL) 4680 { 4681 syserr("readcf: LDAPDefaultSpec: class ldap not available"); 4682 return; 4683 } 4684 map.map_class = &class->s_mapclass; 4685 map.map_db1 = (ARBPTR_T) LDAPDefaults; 4686 map.map_mname = "O LDAPDefaultSpec"; 4687 4688 (void) ldapmap_parseargs(&map, spec); 4689 4690 /* These should never be set in LDAPDefaults */ 4691 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) || 4692 map.map_spacesub != SpaceSub || 4693 map.map_app != NULL || 4694 map.map_tapp != NULL) 4695 { 4696 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags"); 4697 SM_FREE_CLR(map.map_app); 4698 SM_FREE_CLR(map.map_tapp); 4699 } 4700 4701 if (LDAPDefaults->ldap_filter != NULL) 4702 { 4703 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter"); 4704 4705 /* don't free, it isn't malloc'ed in parseargs */ 4706 LDAPDefaults->ldap_filter = NULL; 4707 } 4708 4709 if (LDAPDefaults->ldap_attr[0] != NULL) 4710 { 4711 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes"); 4712 /* don't free, they aren't malloc'ed in parseargs */ 4713 LDAPDefaults->ldap_attr[0] = NULL; 4714 } 4715 } 4716 #endif /* LDAPMAP */ 4717 /* 4718 ** PH map 4719 */ 4720 4721 #if PH_MAP 4722 4723 /* 4724 ** Support for the CCSO Nameserver (ph/qi). 4725 ** This code is intended to replace the so-called "ph mailer". 4726 ** Contributed by Mark D. Roth. Contact him for support. 4727 */ 4728 4729 /* what version of the ph map code we're running */ 4730 static char phmap_id[128]; 4731 4732 /* sendmail version for phmap id string */ 4733 extern const char Version[]; 4734 4735 /* assume we're using nph-1.2.x if not specified */ 4736 # ifndef NPH_VERSION 4737 # define NPH_VERSION 10200 4738 # endif 4739 4740 /* compatibility for versions older than nph-1.2.0 */ 4741 # if NPH_VERSION < 10200 4742 # define PH_OPEN_ROUNDROBIN PH_ROUNDROBIN 4743 # define PH_OPEN_DONTID PH_DONTID 4744 # define PH_CLOSE_FAST PH_FASTCLOSE 4745 # define PH_ERR_DATAERR PH_DATAERR 4746 # define PH_ERR_NOMATCH PH_NOMATCH 4747 # endif /* NPH_VERSION < 10200 */ 4748 4749 /* 4750 ** PH_MAP_PARSEARGS -- parse ph map definition args. 4751 */ 4752 4753 bool 4754 ph_map_parseargs(map, args) 4755 MAP *map; 4756 char *args; 4757 { 4758 register bool done; 4759 register char *p = args; 4760 PH_MAP_STRUCT *pmap = NULL; 4761 4762 /* initialize version string */ 4763 (void) sm_snprintf(phmap_id, sizeof(phmap_id), 4764 "sendmail-%s phmap-20010529 libphclient-%s", 4765 Version, libphclient_version); 4766 4767 pmap = (PH_MAP_STRUCT *) xalloc(sizeof(*pmap)); 4768 4769 /* defaults */ 4770 pmap->ph_servers = NULL; 4771 pmap->ph_field_list = NULL; 4772 pmap->ph = NULL; 4773 pmap->ph_timeout = 0; 4774 pmap->ph_fastclose = 0; 4775 4776 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 4777 for (;;) 4778 { 4779 while (isascii(*p) && isspace(*p)) 4780 p++; 4781 if (*p != '-') 4782 break; 4783 switch (*++p) 4784 { 4785 case 'N': 4786 map->map_mflags |= MF_INCLNULL; 4787 map->map_mflags &= ~MF_TRY0NULL; 4788 break; 4789 4790 case 'O': 4791 map->map_mflags &= ~MF_TRY1NULL; 4792 break; 4793 4794 case 'o': 4795 map->map_mflags |= MF_OPTIONAL; 4796 break; 4797 4798 case 'f': 4799 map->map_mflags |= MF_NOFOLDCASE; 4800 break; 4801 4802 case 'm': 4803 map->map_mflags |= MF_MATCHONLY; 4804 break; 4805 4806 case 'A': 4807 map->map_mflags |= MF_APPEND; 4808 break; 4809 4810 case 'q': 4811 map->map_mflags |= MF_KEEPQUOTES; 4812 break; 4813 4814 case 't': 4815 map->map_mflags |= MF_NODEFER; 4816 break; 4817 4818 case 'a': 4819 map->map_app = ++p; 4820 break; 4821 4822 case 'T': 4823 map->map_tapp = ++p; 4824 break; 4825 4826 case 'l': 4827 while (isascii(*++p) && isspace(*p)) 4828 continue; 4829 pmap->ph_timeout = atoi(p); 4830 break; 4831 4832 case 'S': 4833 map->map_spacesub = *++p; 4834 break; 4835 4836 case 'D': 4837 map->map_mflags |= MF_DEFER; 4838 break; 4839 4840 case 'h': /* PH server list */ 4841 while (isascii(*++p) && isspace(*p)) 4842 continue; 4843 pmap->ph_servers = p; 4844 break; 4845 4846 case 'k': /* fields to search for */ 4847 while (isascii(*++p) && isspace(*p)) 4848 continue; 4849 pmap->ph_field_list = p; 4850 break; 4851 4852 default: 4853 syserr("ph_map_parseargs: unknown option -%c", *p); 4854 } 4855 4856 /* try to account for quoted strings */ 4857 done = isascii(*p) && isspace(*p); 4858 while (*p != '\0' && !done) 4859 { 4860 if (*p == '"') 4861 { 4862 while (*++p != '"' && *p != '\0') 4863 continue; 4864 if (*p != '\0') 4865 p++; 4866 } 4867 else 4868 p++; 4869 done = isascii(*p) && isspace(*p); 4870 } 4871 4872 if (*p != '\0') 4873 *p++ = '\0'; 4874 } 4875 4876 if (map->map_app != NULL) 4877 map->map_app = newstr(ph_map_dequote(map->map_app)); 4878 if (map->map_tapp != NULL) 4879 map->map_tapp = newstr(ph_map_dequote(map->map_tapp)); 4880 4881 if (pmap->ph_field_list != NULL) 4882 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list)); 4883 4884 if (pmap->ph_servers != NULL) 4885 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers)); 4886 else 4887 { 4888 syserr("ph_map_parseargs: -h flag is required"); 4889 return false; 4890 } 4891 4892 map->map_db1 = (ARBPTR_T) pmap; 4893 return true; 4894 } 4895 4896 /* 4897 ** PH_MAP_CLOSE -- close the connection to the ph server 4898 */ 4899 4900 void 4901 ph_map_close(map) 4902 MAP *map; 4903 { 4904 PH_MAP_STRUCT *pmap; 4905 4906 pmap = (PH_MAP_STRUCT *)map->map_db1; 4907 if (tTd(38, 9)) 4908 sm_dprintf("ph_map_close(%s): pmap->ph_fastclose=%d\n", 4909 map->map_mname, pmap->ph_fastclose); 4910 4911 4912 if (pmap->ph != NULL) 4913 { 4914 ph_set_sendhook(pmap->ph, NULL); 4915 ph_set_recvhook(pmap->ph, NULL); 4916 ph_close(pmap->ph, pmap->ph_fastclose); 4917 } 4918 4919 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 4920 } 4921 4922 static jmp_buf PHTimeout; 4923 4924 /* ARGSUSED */ 4925 static void 4926 ph_timeout(unused) 4927 int unused; 4928 { 4929 /* 4930 ** NOTE: THIS CAN BE CALLED FROM A SIGNAL HANDLER. DO NOT ADD 4931 ** ANYTHING TO THIS ROUTINE UNLESS YOU KNOW WHAT YOU ARE 4932 ** DOING. 4933 */ 4934 4935 errno = ETIMEDOUT; 4936 longjmp(PHTimeout, 1); 4937 } 4938 4939 static void 4940 #if NPH_VERSION >= 10200 4941 ph_map_send_debug(appdata, text) 4942 void *appdata; 4943 #else 4944 ph_map_send_debug(text) 4945 #endif 4946 char *text; 4947 { 4948 if (LogLevel > 9) 4949 sm_syslog(LOG_NOTICE, CurEnv->e_id, 4950 "ph_map_send_debug: ==> %s", text); 4951 if (tTd(38, 20)) 4952 sm_dprintf("ph_map_send_debug: ==> %s\n", text); 4953 } 4954 4955 static void 4956 #if NPH_VERSION >= 10200 4957 ph_map_recv_debug(appdata, text) 4958 void *appdata; 4959 #else 4960 ph_map_recv_debug(text) 4961 #endif 4962 char *text; 4963 { 4964 if (LogLevel > 10) 4965 sm_syslog(LOG_NOTICE, CurEnv->e_id, 4966 "ph_map_recv_debug: <== %s", text); 4967 if (tTd(38, 21)) 4968 sm_dprintf("ph_map_recv_debug: <== %s\n", text); 4969 } 4970 4971 /* 4972 ** PH_MAP_OPEN -- sub for opening PH map 4973 */ 4974 bool 4975 ph_map_open(map, mode) 4976 MAP *map; 4977 int mode; 4978 { 4979 PH_MAP_STRUCT *pmap; 4980 register SM_EVENT *ev = NULL; 4981 int save_errno = 0; 4982 char *hostlist, *host; 4983 4984 if (tTd(38, 2)) 4985 sm_dprintf("ph_map_open(%s)\n", map->map_mname); 4986 4987 mode &= O_ACCMODE; 4988 if (mode != O_RDONLY) 4989 { 4990 /* issue a pseudo-error message */ 4991 errno = SM_EMAPCANTWRITE; 4992 return false; 4993 } 4994 4995 if (CurEnv != NULL && CurEnv->e_sendmode == SM_DEFER && 4996 bitset(MF_DEFER, map->map_mflags)) 4997 { 4998 if (tTd(9, 1)) 4999 sm_dprintf("ph_map_open(%s) => DEFERRED\n", 5000 map->map_mname); 5001 5002 /* 5003 ** Unset MF_DEFER here so that map_lookup() returns 5004 ** a temporary failure using the bogus map and 5005 ** map->map_tapp instead of the default permanent error. 5006 */ 5007 5008 map->map_mflags &= ~MF_DEFER; 5009 return false; 5010 } 5011 5012 pmap = (PH_MAP_STRUCT *)map->map_db1; 5013 pmap->ph_fastclose = 0; /* refresh field for reopen */ 5014 5015 /* try each host in the list */ 5016 hostlist = newstr(pmap->ph_servers); 5017 for (host = strtok(hostlist, " "); 5018 host != NULL; 5019 host = strtok(NULL, " ")) 5020 { 5021 /* set timeout */ 5022 if (pmap->ph_timeout != 0) 5023 { 5024 if (setjmp(PHTimeout) != 0) 5025 { 5026 ev = NULL; 5027 if (LogLevel > 1) 5028 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5029 "timeout connecting to PH server %.100s", 5030 host); 5031 errno = ETIMEDOUT; 5032 goto ph_map_open_abort; 5033 } 5034 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0); 5035 } 5036 5037 /* open connection to server */ 5038 if (ph_open(&(pmap->ph), host, 5039 PH_OPEN_ROUNDROBIN|PH_OPEN_DONTID, 5040 ph_map_send_debug, ph_map_recv_debug 5041 #if NPH_VERSION >= 10200 5042 , NULL 5043 #endif 5044 ) == 0 5045 && ph_id(pmap->ph, phmap_id) == 0) 5046 { 5047 if (ev != NULL) 5048 sm_clrevent(ev); 5049 sm_free(hostlist); /* XXX */ 5050 return true; 5051 } 5052 5053 ph_map_open_abort: 5054 save_errno = errno; 5055 if (ev != NULL) 5056 sm_clrevent(ev); 5057 pmap->ph_fastclose = PH_CLOSE_FAST; 5058 ph_map_close(map); 5059 errno = save_errno; 5060 } 5061 5062 if (bitset(MF_NODEFER, map->map_mflags)) 5063 { 5064 if (errno == 0) 5065 errno = EAGAIN; 5066 syserr("ph_map_open: %s: cannot connect to PH server", 5067 map->map_mname); 5068 } 5069 else if (!bitset(MF_OPTIONAL, map->map_mflags) && LogLevel > 1) 5070 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5071 "ph_map_open: %s: cannot connect to PH server", 5072 map->map_mname); 5073 sm_free(hostlist); /* XXX */ 5074 return false; 5075 } 5076 5077 /* 5078 ** PH_MAP_LOOKUP -- look up key from ph server 5079 */ 5080 5081 char * 5082 ph_map_lookup(map, key, args, pstat) 5083 MAP *map; 5084 char *key; 5085 char **args; 5086 int *pstat; 5087 { 5088 int i, save_errno = 0; 5089 register SM_EVENT *ev = NULL; 5090 PH_MAP_STRUCT *pmap; 5091 char *value = NULL; 5092 5093 pmap = (PH_MAP_STRUCT *)map->map_db1; 5094 5095 *pstat = EX_OK; 5096 5097 /* set timeout */ 5098 if (pmap->ph_timeout != 0) 5099 { 5100 if (setjmp(PHTimeout) != 0) 5101 { 5102 ev = NULL; 5103 if (LogLevel > 1) 5104 sm_syslog(LOG_NOTICE, CurEnv->e_id, 5105 "timeout during PH lookup of %.100s", 5106 key); 5107 errno = ETIMEDOUT; 5108 *pstat = EX_TEMPFAIL; 5109 goto ph_map_lookup_abort; 5110 } 5111 ev = sm_setevent(pmap->ph_timeout, ph_timeout, 0); 5112 } 5113 5114 /* perform lookup */ 5115 i = ph_email_resolve(pmap->ph, key, pmap->ph_field_list, &value); 5116 if (i == -1) 5117 *pstat = EX_TEMPFAIL; 5118 else if (i == PH_ERR_NOMATCH || i == PH_ERR_DATAERR) 5119 *pstat = EX_UNAVAILABLE; 5120 5121 ph_map_lookup_abort: 5122 if (ev != NULL) 5123 sm_clrevent(ev); 5124 5125 /* 5126 ** Close the connection if the timer popped 5127 ** or we got a temporary PH error 5128 */ 5129 5130 if (*pstat == EX_TEMPFAIL) 5131 { 5132 save_errno = errno; 5133 pmap->ph_fastclose = PH_CLOSE_FAST; 5134 ph_map_close(map); 5135 errno = save_errno; 5136 } 5137 5138 if (*pstat == EX_OK) 5139 { 5140 if (tTd(38,20)) 5141 sm_dprintf("ph_map_lookup: %s => %s\n", key, value); 5142 5143 if (bitset(MF_MATCHONLY, map->map_mflags)) 5144 return map_rewrite(map, key, strlen(key), NULL); 5145 else 5146 return map_rewrite(map, value, strlen(value), args); 5147 } 5148 5149 return NULL; 5150 } 5151 #endif /* PH_MAP */ 5152 5153 /* 5154 ** syslog map 5155 */ 5156 5157 #define map_prio map_lockfd /* overload field */ 5158 5159 /* 5160 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages. 5161 */ 5162 5163 bool 5164 syslog_map_parseargs(map, args) 5165 MAP *map; 5166 char *args; 5167 { 5168 char *p = args; 5169 char *priority = NULL; 5170 5171 /* there is no check whether there is really an argument */ 5172 while (*p != '\0') 5173 { 5174 while (isascii(*p) && isspace(*p)) 5175 p++; 5176 if (*p != '-') 5177 break; 5178 ++p; 5179 if (*p == 'D') 5180 { 5181 map->map_mflags |= MF_DEFER; 5182 ++p; 5183 } 5184 else if (*p == 'S') 5185 { 5186 map->map_spacesub = *++p; 5187 if (*p != '\0') 5188 p++; 5189 } 5190 else if (*p == 'L') 5191 { 5192 while (*++p != '\0' && isascii(*p) && isspace(*p)) 5193 continue; 5194 if (*p == '\0') 5195 break; 5196 priority = p; 5197 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 5198 p++; 5199 if (*p != '\0') 5200 *p++ = '\0'; 5201 } 5202 else 5203 { 5204 syserr("Illegal option %c map syslog", *p); 5205 ++p; 5206 } 5207 } 5208 5209 if (priority == NULL) 5210 map->map_prio = LOG_INFO; 5211 else 5212 { 5213 if (sm_strncasecmp("LOG_", priority, 4) == 0) 5214 priority += 4; 5215 5216 #ifdef LOG_EMERG 5217 if (sm_strcasecmp("EMERG", priority) == 0) 5218 map->map_prio = LOG_EMERG; 5219 else 5220 #endif /* LOG_EMERG */ 5221 #ifdef LOG_ALERT 5222 if (sm_strcasecmp("ALERT", priority) == 0) 5223 map->map_prio = LOG_ALERT; 5224 else 5225 #endif /* LOG_ALERT */ 5226 #ifdef LOG_CRIT 5227 if (sm_strcasecmp("CRIT", priority) == 0) 5228 map->map_prio = LOG_CRIT; 5229 else 5230 #endif /* LOG_CRIT */ 5231 #ifdef LOG_ERR 5232 if (sm_strcasecmp("ERR", priority) == 0) 5233 map->map_prio = LOG_ERR; 5234 else 5235 #endif /* LOG_ERR */ 5236 #ifdef LOG_WARNING 5237 if (sm_strcasecmp("WARNING", priority) == 0) 5238 map->map_prio = LOG_WARNING; 5239 else 5240 #endif /* LOG_WARNING */ 5241 #ifdef LOG_NOTICE 5242 if (sm_strcasecmp("NOTICE", priority) == 0) 5243 map->map_prio = LOG_NOTICE; 5244 else 5245 #endif /* LOG_NOTICE */ 5246 #ifdef LOG_INFO 5247 if (sm_strcasecmp("INFO", priority) == 0) 5248 map->map_prio = LOG_INFO; 5249 else 5250 #endif /* LOG_INFO */ 5251 #ifdef LOG_DEBUG 5252 if (sm_strcasecmp("DEBUG", priority) == 0) 5253 map->map_prio = LOG_DEBUG; 5254 else 5255 #endif /* LOG_DEBUG */ 5256 { 5257 syserr("syslog_map_parseargs: Unknown priority %s", 5258 priority); 5259 return false; 5260 } 5261 } 5262 return true; 5263 } 5264 5265 /* 5266 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string 5267 */ 5268 5269 char * 5270 syslog_map_lookup(map, string, args, statp) 5271 MAP *map; 5272 char *string; 5273 char **args; 5274 int *statp; 5275 { 5276 char *ptr = map_rewrite(map, string, strlen(string), args); 5277 5278 if (ptr != NULL) 5279 { 5280 if (tTd(38, 20)) 5281 sm_dprintf("syslog_map_lookup(%s (priority %d): %s\n", 5282 map->map_mname, map->map_prio, ptr); 5283 5284 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr); 5285 } 5286 5287 *statp = EX_OK; 5288 return ""; 5289 } 5290 5291 #if _FFR_DPRINTF_MAP 5292 /* 5293 ** dprintf map 5294 */ 5295 5296 #define map_dbg_level map_lockfd /* overload field */ 5297 5298 /* 5299 ** DPRINTF_MAP_PARSEARGS -- check for priority level to dprintf messages. 5300 */ 5301 5302 bool 5303 dprintf_map_parseargs(map, args) 5304 MAP *map; 5305 char *args; 5306 { 5307 char *p = args; 5308 char *dbg_level = NULL; 5309 5310 /* there is no check whether there is really an argument */ 5311 while (*p != '\0') 5312 { 5313 while (isascii(*p) && isspace(*p)) 5314 p++; 5315 if (*p != '-') 5316 break; 5317 ++p; 5318 if (*p == 'D') 5319 { 5320 map->map_mflags |= MF_DEFER; 5321 ++p; 5322 } 5323 else if (*p == 'S') 5324 { 5325 map->map_spacesub = *++p; 5326 if (*p != '\0') 5327 p++; 5328 } 5329 else if (*p == 'd') 5330 { 5331 while (*++p != '\0' && isascii(*p) && isspace(*p)) 5332 continue; 5333 if (*p == '\0') 5334 break; 5335 dbg_level = p; 5336 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 5337 p++; 5338 if (*p != '\0') 5339 *p++ = '\0'; 5340 } 5341 else 5342 { 5343 syserr("Illegal option %c map dprintf", *p); 5344 ++p; 5345 } 5346 } 5347 5348 if (dbg_level == NULL) 5349 map->map_dbg_level = 0; 5350 else 5351 { 5352 if (!(isascii(*dbg_level) && isdigit(*dbg_level))) 5353 { 5354 syserr("dprintf map \"%s\", file %s: -d should specify a number, not %s", 5355 map->map_mname, map->map_file, 5356 dbg_level); 5357 return false; 5358 } 5359 map->map_dbg_level = atoi(dbg_level); 5360 } 5361 return true; 5362 } 5363 5364 /* 5365 ** DPRINTF_MAP_LOOKUP -- rewrite and print message. Always return empty string 5366 */ 5367 5368 char * 5369 dprintf_map_lookup(map, string, args, statp) 5370 MAP *map; 5371 char *string; 5372 char **args; 5373 int *statp; 5374 { 5375 char *ptr = map_rewrite(map, string, strlen(string), args); 5376 5377 if (ptr != NULL && tTd(85, map->map_dbg_level)) 5378 sm_dprintf("%s\n", ptr); 5379 *statp = EX_OK; 5380 return ""; 5381 } 5382 #endif /* _FFR_DPRINTF_MAP */ 5383 5384 /* 5385 ** HESIOD Modules 5386 */ 5387 5388 #if HESIOD 5389 5390 bool 5391 hes_map_open(map, mode) 5392 MAP *map; 5393 int mode; 5394 { 5395 if (tTd(38, 2)) 5396 sm_dprintf("hes_map_open(%s, %s, %d)\n", 5397 map->map_mname, map->map_file, mode); 5398 5399 if (mode != O_RDONLY) 5400 { 5401 /* issue a pseudo-error message */ 5402 errno = SM_EMAPCANTWRITE; 5403 return false; 5404 } 5405 5406 # ifdef HESIOD_INIT 5407 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0) 5408 return true; 5409 5410 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5411 syserr("451 4.3.5 cannot initialize Hesiod map (%s)", 5412 sm_errstring(errno)); 5413 return false; 5414 # else /* HESIOD_INIT */ 5415 if (hes_error() == HES_ER_UNINIT) 5416 hes_init(); 5417 switch (hes_error()) 5418 { 5419 case HES_ER_OK: 5420 case HES_ER_NOTFOUND: 5421 return true; 5422 } 5423 5424 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5425 syserr("451 4.3.5 cannot initialize Hesiod map (%d)", hes_error()); 5426 5427 return false; 5428 # endif /* HESIOD_INIT */ 5429 } 5430 5431 char * 5432 hes_map_lookup(map, name, av, statp) 5433 MAP *map; 5434 char *name; 5435 char **av; 5436 int *statp; 5437 { 5438 char **hp; 5439 5440 if (tTd(38, 20)) 5441 sm_dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name); 5442 5443 if (name[0] == '\\') 5444 { 5445 char *np; 5446 int nl; 5447 int save_errno; 5448 char nbuf[MAXNAME]; 5449 5450 nl = strlen(name); 5451 if (nl < sizeof(nbuf) - 1) 5452 np = nbuf; 5453 else 5454 np = xalloc(strlen(name) + 2); 5455 np[0] = '\\'; 5456 (void) sm_strlcpy(&np[1], name, (sizeof(nbuf)) - 1); 5457 # ifdef HESIOD_INIT 5458 hp = hesiod_resolve(HesiodContext, np, map->map_file); 5459 # else /* HESIOD_INIT */ 5460 hp = hes_resolve(np, map->map_file); 5461 # endif /* HESIOD_INIT */ 5462 save_errno = errno; 5463 if (np != nbuf) 5464 sm_free(np); /* XXX */ 5465 errno = save_errno; 5466 } 5467 else 5468 { 5469 # ifdef HESIOD_INIT 5470 hp = hesiod_resolve(HesiodContext, name, map->map_file); 5471 # else /* HESIOD_INIT */ 5472 hp = hes_resolve(name, map->map_file); 5473 # endif /* HESIOD_INIT */ 5474 } 5475 # ifdef HESIOD_INIT 5476 if (hp == NULL || *hp == NULL) 5477 { 5478 switch (errno) 5479 { 5480 case ENOENT: 5481 *statp = EX_NOTFOUND; 5482 break; 5483 case ECONNREFUSED: 5484 *statp = EX_TEMPFAIL; 5485 break; 5486 case EMSGSIZE: 5487 case ENOMEM: 5488 default: 5489 *statp = EX_UNAVAILABLE; 5490 break; 5491 } 5492 if (hp != NULL) 5493 hesiod_free_list(HesiodContext, hp); 5494 return NULL; 5495 } 5496 # else /* HESIOD_INIT */ 5497 if (hp == NULL || hp[0] == NULL) 5498 { 5499 switch (hes_error()) 5500 { 5501 case HES_ER_OK: 5502 *statp = EX_OK; 5503 break; 5504 5505 case HES_ER_NOTFOUND: 5506 *statp = EX_NOTFOUND; 5507 break; 5508 5509 case HES_ER_CONFIG: 5510 *statp = EX_UNAVAILABLE; 5511 break; 5512 5513 case HES_ER_NET: 5514 *statp = EX_TEMPFAIL; 5515 break; 5516 } 5517 return NULL; 5518 } 5519 # endif /* HESIOD_INIT */ 5520 5521 if (bitset(MF_MATCHONLY, map->map_mflags)) 5522 return map_rewrite(map, name, strlen(name), NULL); 5523 else 5524 return map_rewrite(map, hp[0], strlen(hp[0]), av); 5525 } 5526 5527 /* 5528 ** HES_MAP_CLOSE -- free the Hesiod context 5529 */ 5530 5531 void 5532 hes_map_close(map) 5533 MAP *map; 5534 { 5535 if (tTd(38, 20)) 5536 sm_dprintf("hes_map_close(%s)\n", map->map_file); 5537 5538 # ifdef HESIOD_INIT 5539 /* Free the hesiod context */ 5540 if (HesiodContext != NULL) 5541 { 5542 hesiod_end(HesiodContext); 5543 HesiodContext = NULL; 5544 } 5545 # endif /* HESIOD_INIT */ 5546 } 5547 5548 #endif /* HESIOD */ 5549 /* 5550 ** NeXT NETINFO Modules 5551 */ 5552 5553 #if NETINFO 5554 5555 # define NETINFO_DEFAULT_DIR "/aliases" 5556 # define NETINFO_DEFAULT_PROPERTY "members" 5557 5558 /* 5559 ** NI_MAP_OPEN -- open NetInfo Aliases 5560 */ 5561 5562 bool 5563 ni_map_open(map, mode) 5564 MAP *map; 5565 int mode; 5566 { 5567 if (tTd(38, 2)) 5568 sm_dprintf("ni_map_open(%s, %s, %d)\n", 5569 map->map_mname, map->map_file, mode); 5570 mode &= O_ACCMODE; 5571 5572 if (*map->map_file == '\0') 5573 map->map_file = NETINFO_DEFAULT_DIR; 5574 5575 if (map->map_valcolnm == NULL) 5576 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY; 5577 5578 if (map->map_coldelim == '\0') 5579 { 5580 if (bitset(MF_ALIAS, map->map_mflags)) 5581 map->map_coldelim = ','; 5582 else if (bitset(MF_FILECLASS, map->map_mflags)) 5583 map->map_coldelim = ' '; 5584 } 5585 return true; 5586 } 5587 5588 5589 /* 5590 ** NI_MAP_LOOKUP -- look up a datum in NetInfo 5591 */ 5592 5593 char * 5594 ni_map_lookup(map, name, av, statp) 5595 MAP *map; 5596 char *name; 5597 char **av; 5598 int *statp; 5599 { 5600 char *res; 5601 char *propval; 5602 5603 if (tTd(38, 20)) 5604 sm_dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name); 5605 5606 propval = ni_propval(map->map_file, map->map_keycolnm, name, 5607 map->map_valcolnm, map->map_coldelim); 5608 5609 if (propval == NULL) 5610 return NULL; 5611 5612 SM_TRY 5613 if (bitset(MF_MATCHONLY, map->map_mflags)) 5614 res = map_rewrite(map, name, strlen(name), NULL); 5615 else 5616 res = map_rewrite(map, propval, strlen(propval), av); 5617 SM_FINALLY 5618 sm_free(propval); 5619 SM_END_TRY 5620 return res; 5621 } 5622 5623 5624 static bool 5625 ni_getcanonname(name, hbsize, statp) 5626 char *name; 5627 int hbsize; 5628 int *statp; 5629 { 5630 char *vptr; 5631 char *ptr; 5632 char nbuf[MAXNAME + 1]; 5633 5634 if (tTd(38, 20)) 5635 sm_dprintf("ni_getcanonname(%s)\n", name); 5636 5637 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf)) 5638 { 5639 *statp = EX_UNAVAILABLE; 5640 return false; 5641 } 5642 (void) shorten_hostname(nbuf); 5643 5644 /* we only accept single token search key */ 5645 if (strchr(nbuf, '.')) 5646 { 5647 *statp = EX_NOHOST; 5648 return false; 5649 } 5650 5651 /* Do the search */ 5652 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n'); 5653 5654 if (vptr == NULL) 5655 { 5656 *statp = EX_NOHOST; 5657 return false; 5658 } 5659 5660 /* Only want the first machine name */ 5661 if ((ptr = strchr(vptr, '\n')) != NULL) 5662 *ptr = '\0'; 5663 5664 if (sm_strlcpy(name, vptr, hbsize) >= hbsize) 5665 { 5666 sm_free(vptr); 5667 *statp = EX_UNAVAILABLE; 5668 return true; 5669 } 5670 sm_free(vptr); 5671 *statp = EX_OK; 5672 return false; 5673 } 5674 #endif /* NETINFO */ 5675 /* 5676 ** TEXT (unindexed text file) Modules 5677 ** 5678 ** This code donated by Sun Microsystems. 5679 */ 5680 5681 #define map_sff map_lockfd /* overload field */ 5682 5683 5684 /* 5685 ** TEXT_MAP_OPEN -- open text table 5686 */ 5687 5688 bool 5689 text_map_open(map, mode) 5690 MAP *map; 5691 int mode; 5692 { 5693 long sff; 5694 int i; 5695 5696 if (tTd(38, 2)) 5697 sm_dprintf("text_map_open(%s, %s, %d)\n", 5698 map->map_mname, map->map_file, mode); 5699 5700 mode &= O_ACCMODE; 5701 if (mode != O_RDONLY) 5702 { 5703 errno = EPERM; 5704 return false; 5705 } 5706 5707 if (*map->map_file == '\0') 5708 { 5709 syserr("text map \"%s\": file name required", 5710 map->map_mname); 5711 return false; 5712 } 5713 5714 if (map->map_file[0] != '/') 5715 { 5716 syserr("text map \"%s\": file name must be fully qualified", 5717 map->map_mname); 5718 return false; 5719 } 5720 5721 sff = SFF_ROOTOK|SFF_REGONLY; 5722 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 5723 sff |= SFF_NOWLINK; 5724 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 5725 sff |= SFF_SAFEDIRPATH; 5726 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName, 5727 sff, S_IRUSR, NULL)) != 0) 5728 { 5729 int save_errno = errno; 5730 5731 /* cannot open this map */ 5732 if (tTd(38, 2)) 5733 sm_dprintf("\tunsafe map file: %d\n", i); 5734 errno = save_errno; 5735 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5736 syserr("text map \"%s\": unsafe map file %s", 5737 map->map_mname, map->map_file); 5738 return false; 5739 } 5740 5741 if (map->map_keycolnm == NULL) 5742 map->map_keycolno = 0; 5743 else 5744 { 5745 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm))) 5746 { 5747 syserr("text map \"%s\", file %s: -k should specify a number, not %s", 5748 map->map_mname, map->map_file, 5749 map->map_keycolnm); 5750 return false; 5751 } 5752 map->map_keycolno = atoi(map->map_keycolnm); 5753 } 5754 5755 if (map->map_valcolnm == NULL) 5756 map->map_valcolno = 0; 5757 else 5758 { 5759 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm))) 5760 { 5761 syserr("text map \"%s\", file %s: -v should specify a number, not %s", 5762 map->map_mname, map->map_file, 5763 map->map_valcolnm); 5764 return false; 5765 } 5766 map->map_valcolno = atoi(map->map_valcolnm); 5767 } 5768 5769 if (tTd(38, 2)) 5770 { 5771 sm_dprintf("text_map_open(%s, %s): delimiter = ", 5772 map->map_mname, map->map_file); 5773 if (map->map_coldelim == '\0') 5774 sm_dprintf("(white space)\n"); 5775 else 5776 sm_dprintf("%c\n", map->map_coldelim); 5777 } 5778 5779 map->map_sff = sff; 5780 return true; 5781 } 5782 5783 5784 /* 5785 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table 5786 */ 5787 5788 char * 5789 text_map_lookup(map, name, av, statp) 5790 MAP *map; 5791 char *name; 5792 char **av; 5793 int *statp; 5794 { 5795 char *vp; 5796 auto int vsize; 5797 int buflen; 5798 SM_FILE_T *f; 5799 char delim; 5800 int key_idx; 5801 bool found_it; 5802 long sff = map->map_sff; 5803 char search_key[MAXNAME + 1]; 5804 char linebuf[MAXLINE]; 5805 char buf[MAXNAME + 1]; 5806 5807 found_it = false; 5808 if (tTd(38, 20)) 5809 sm_dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name); 5810 5811 buflen = strlen(name); 5812 if (buflen > sizeof(search_key) - 1) 5813 buflen = sizeof(search_key) - 1; /* XXX just cut if off? */ 5814 memmove(search_key, name, buflen); 5815 search_key[buflen] = '\0'; 5816 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 5817 makelower(search_key); 5818 5819 f = safefopen(map->map_file, O_RDONLY, FileMode, sff); 5820 if (f == NULL) 5821 { 5822 map->map_mflags &= ~(MF_VALID|MF_OPEN); 5823 *statp = EX_UNAVAILABLE; 5824 return NULL; 5825 } 5826 key_idx = map->map_keycolno; 5827 delim = map->map_coldelim; 5828 while (sm_io_fgets(f, SM_TIME_DEFAULT, 5829 linebuf, sizeof(linebuf)) != NULL) 5830 { 5831 char *p; 5832 5833 /* skip comment line */ 5834 if (linebuf[0] == '#') 5835 continue; 5836 p = strchr(linebuf, '\n'); 5837 if (p != NULL) 5838 *p = '\0'; 5839 p = get_column(linebuf, key_idx, delim, buf, sizeof(buf)); 5840 if (p != NULL && sm_strcasecmp(search_key, p) == 0) 5841 { 5842 found_it = true; 5843 break; 5844 } 5845 } 5846 (void) sm_io_close(f, SM_TIME_DEFAULT); 5847 if (!found_it) 5848 { 5849 *statp = EX_NOTFOUND; 5850 return NULL; 5851 } 5852 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof(buf)); 5853 if (vp == NULL) 5854 { 5855 *statp = EX_NOTFOUND; 5856 return NULL; 5857 } 5858 vsize = strlen(vp); 5859 *statp = EX_OK; 5860 if (bitset(MF_MATCHONLY, map->map_mflags)) 5861 return map_rewrite(map, name, strlen(name), NULL); 5862 else 5863 return map_rewrite(map, vp, vsize, av); 5864 } 5865 5866 /* 5867 ** TEXT_GETCANONNAME -- look up canonical name in hosts file 5868 */ 5869 5870 static bool 5871 text_getcanonname(name, hbsize, statp) 5872 char *name; 5873 int hbsize; 5874 int *statp; 5875 { 5876 bool found; 5877 char *dot; 5878 SM_FILE_T *f; 5879 char linebuf[MAXLINE]; 5880 char cbuf[MAXNAME + 1]; 5881 char nbuf[MAXNAME + 1]; 5882 5883 if (tTd(38, 20)) 5884 sm_dprintf("text_getcanonname(%s)\n", name); 5885 5886 if (sm_strlcpy(nbuf, name, sizeof(nbuf)) >= sizeof(nbuf)) 5887 { 5888 *statp = EX_UNAVAILABLE; 5889 return false; 5890 } 5891 dot = shorten_hostname(nbuf); 5892 5893 f = sm_io_open(SmFtStdio, SM_TIME_DEFAULT, HostsFile, SM_IO_RDONLY, 5894 NULL); 5895 if (f == NULL) 5896 { 5897 *statp = EX_UNAVAILABLE; 5898 return false; 5899 } 5900 found = false; 5901 while (!found && 5902 sm_io_fgets(f, SM_TIME_DEFAULT, 5903 linebuf, sizeof(linebuf)) != NULL) 5904 { 5905 char *p = strpbrk(linebuf, "#\n"); 5906 5907 if (p != NULL) 5908 *p = '\0'; 5909 if (linebuf[0] != '\0') 5910 found = extract_canonname(nbuf, dot, linebuf, 5911 cbuf, sizeof(cbuf)); 5912 } 5913 (void) sm_io_close(f, SM_TIME_DEFAULT); 5914 if (!found) 5915 { 5916 *statp = EX_NOHOST; 5917 return false; 5918 } 5919 5920 if (sm_strlcpy(name, cbuf, hbsize) >= hbsize) 5921 { 5922 *statp = EX_UNAVAILABLE; 5923 return false; 5924 } 5925 *statp = EX_OK; 5926 return true; 5927 } 5928 /* 5929 ** STAB (Symbol Table) Modules 5930 */ 5931 5932 5933 /* 5934 ** STAB_MAP_LOOKUP -- look up alias in symbol table 5935 */ 5936 5937 /* ARGSUSED2 */ 5938 char * 5939 stab_map_lookup(map, name, av, pstat) 5940 register MAP *map; 5941 char *name; 5942 char **av; 5943 int *pstat; 5944 { 5945 register STAB *s; 5946 5947 if (tTd(38, 20)) 5948 sm_dprintf("stab_lookup(%s, %s)\n", 5949 map->map_mname, name); 5950 5951 s = stab(name, ST_ALIAS, ST_FIND); 5952 if (s == NULL) 5953 return NULL; 5954 if (bitset(MF_MATCHONLY, map->map_mflags)) 5955 return map_rewrite(map, name, strlen(name), NULL); 5956 else 5957 return map_rewrite(map, s->s_alias, strlen(s->s_alias), av); 5958 } 5959 5960 /* 5961 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild) 5962 */ 5963 5964 void 5965 stab_map_store(map, lhs, rhs) 5966 register MAP *map; 5967 char *lhs; 5968 char *rhs; 5969 { 5970 register STAB *s; 5971 5972 s = stab(lhs, ST_ALIAS, ST_ENTER); 5973 s->s_alias = newstr(rhs); 5974 } 5975 5976 5977 /* 5978 ** STAB_MAP_OPEN -- initialize (reads data file) 5979 ** 5980 ** This is a wierd case -- it is only intended as a fallback for 5981 ** aliases. For this reason, opens for write (only during a 5982 ** "newaliases") always fails, and opens for read open the 5983 ** actual underlying text file instead of the database. 5984 */ 5985 5986 bool 5987 stab_map_open(map, mode) 5988 register MAP *map; 5989 int mode; 5990 { 5991 SM_FILE_T *af; 5992 long sff; 5993 struct stat st; 5994 5995 if (tTd(38, 2)) 5996 sm_dprintf("stab_map_open(%s, %s, %d)\n", 5997 map->map_mname, map->map_file, mode); 5998 5999 mode &= O_ACCMODE; 6000 if (mode != O_RDONLY) 6001 { 6002 errno = EPERM; 6003 return false; 6004 } 6005 6006 sff = SFF_ROOTOK|SFF_REGONLY; 6007 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 6008 sff |= SFF_NOWLINK; 6009 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 6010 sff |= SFF_SAFEDIRPATH; 6011 af = safefopen(map->map_file, O_RDONLY, 0444, sff); 6012 if (af == NULL) 6013 return false; 6014 readaliases(map, af, false, false); 6015 6016 if (fstat(sm_io_getinfo(af, SM_IO_WHAT_FD, NULL), &st) >= 0) 6017 map->map_mtime = st.st_mtime; 6018 (void) sm_io_close(af, SM_TIME_DEFAULT); 6019 6020 return true; 6021 } 6022 /* 6023 ** Implicit Modules 6024 ** 6025 ** Tries several types. For back compatibility of aliases. 6026 */ 6027 6028 6029 /* 6030 ** IMPL_MAP_LOOKUP -- lookup in best open database 6031 */ 6032 6033 char * 6034 impl_map_lookup(map, name, av, pstat) 6035 MAP *map; 6036 char *name; 6037 char **av; 6038 int *pstat; 6039 { 6040 if (tTd(38, 20)) 6041 sm_dprintf("impl_map_lookup(%s, %s)\n", 6042 map->map_mname, name); 6043 6044 #if NEWDB 6045 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6046 return db_map_lookup(map, name, av, pstat); 6047 #endif /* NEWDB */ 6048 #if NDBM 6049 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6050 return ndbm_map_lookup(map, name, av, pstat); 6051 #endif /* NDBM */ 6052 return stab_map_lookup(map, name, av, pstat); 6053 } 6054 6055 /* 6056 ** IMPL_MAP_STORE -- store in open databases 6057 */ 6058 6059 void 6060 impl_map_store(map, lhs, rhs) 6061 MAP *map; 6062 char *lhs; 6063 char *rhs; 6064 { 6065 if (tTd(38, 12)) 6066 sm_dprintf("impl_map_store(%s, %s, %s)\n", 6067 map->map_mname, lhs, rhs); 6068 #if NEWDB 6069 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6070 db_map_store(map, lhs, rhs); 6071 #endif /* NEWDB */ 6072 #if NDBM 6073 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6074 ndbm_map_store(map, lhs, rhs); 6075 #endif /* NDBM */ 6076 stab_map_store(map, lhs, rhs); 6077 } 6078 6079 /* 6080 ** IMPL_MAP_OPEN -- implicit database open 6081 */ 6082 6083 bool 6084 impl_map_open(map, mode) 6085 MAP *map; 6086 int mode; 6087 { 6088 if (tTd(38, 2)) 6089 sm_dprintf("impl_map_open(%s, %s, %d)\n", 6090 map->map_mname, map->map_file, mode); 6091 6092 mode &= O_ACCMODE; 6093 #if NEWDB 6094 map->map_mflags |= MF_IMPL_HASH; 6095 if (hash_map_open(map, mode)) 6096 { 6097 # ifdef NDBM_YP_COMPAT 6098 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL) 6099 # endif /* NDBM_YP_COMPAT */ 6100 return true; 6101 } 6102 else 6103 map->map_mflags &= ~MF_IMPL_HASH; 6104 #endif /* NEWDB */ 6105 #if NDBM 6106 map->map_mflags |= MF_IMPL_NDBM; 6107 if (ndbm_map_open(map, mode)) 6108 { 6109 return true; 6110 } 6111 else 6112 map->map_mflags &= ~MF_IMPL_NDBM; 6113 #endif /* NDBM */ 6114 6115 #if defined(NEWDB) || defined(NDBM) 6116 if (Verbose) 6117 message("WARNING: cannot open alias database %s%s", 6118 map->map_file, 6119 mode == O_RDONLY ? "; reading text version" : ""); 6120 #else /* defined(NEWDB) || defined(NDBM) */ 6121 if (mode != O_RDONLY) 6122 usrerr("Cannot rebuild aliases: no database format defined"); 6123 #endif /* defined(NEWDB) || defined(NDBM) */ 6124 6125 if (mode == O_RDONLY) 6126 return stab_map_open(map, mode); 6127 else 6128 return false; 6129 } 6130 6131 6132 /* 6133 ** IMPL_MAP_CLOSE -- close any open database(s) 6134 */ 6135 6136 void 6137 impl_map_close(map) 6138 MAP *map; 6139 { 6140 if (tTd(38, 9)) 6141 sm_dprintf("impl_map_close(%s, %s, %lx)\n", 6142 map->map_mname, map->map_file, map->map_mflags); 6143 #if NEWDB 6144 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6145 { 6146 db_map_close(map); 6147 map->map_mflags &= ~MF_IMPL_HASH; 6148 } 6149 #endif /* NEWDB */ 6150 6151 #if NDBM 6152 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6153 { 6154 ndbm_map_close(map); 6155 map->map_mflags &= ~MF_IMPL_NDBM; 6156 } 6157 #endif /* NDBM */ 6158 } 6159 /* 6160 ** User map class. 6161 ** 6162 ** Provides access to the system password file. 6163 */ 6164 6165 /* 6166 ** USER_MAP_OPEN -- open user map 6167 ** 6168 ** Really just binds field names to field numbers. 6169 */ 6170 6171 bool 6172 user_map_open(map, mode) 6173 MAP *map; 6174 int mode; 6175 { 6176 if (tTd(38, 2)) 6177 sm_dprintf("user_map_open(%s, %d)\n", 6178 map->map_mname, mode); 6179 6180 mode &= O_ACCMODE; 6181 if (mode != O_RDONLY) 6182 { 6183 /* issue a pseudo-error message */ 6184 errno = SM_EMAPCANTWRITE; 6185 return false; 6186 } 6187 if (map->map_valcolnm == NULL) 6188 /* EMPTY */ 6189 /* nothing */ ; 6190 else if (sm_strcasecmp(map->map_valcolnm, "name") == 0) 6191 map->map_valcolno = 1; 6192 else if (sm_strcasecmp(map->map_valcolnm, "passwd") == 0) 6193 map->map_valcolno = 2; 6194 else if (sm_strcasecmp(map->map_valcolnm, "uid") == 0) 6195 map->map_valcolno = 3; 6196 else if (sm_strcasecmp(map->map_valcolnm, "gid") == 0) 6197 map->map_valcolno = 4; 6198 else if (sm_strcasecmp(map->map_valcolnm, "gecos") == 0) 6199 map->map_valcolno = 5; 6200 else if (sm_strcasecmp(map->map_valcolnm, "dir") == 0) 6201 map->map_valcolno = 6; 6202 else if (sm_strcasecmp(map->map_valcolnm, "shell") == 0) 6203 map->map_valcolno = 7; 6204 else 6205 { 6206 syserr("User map %s: unknown column name %s", 6207 map->map_mname, map->map_valcolnm); 6208 return false; 6209 } 6210 return true; 6211 } 6212 6213 6214 /* 6215 ** USER_MAP_LOOKUP -- look up a user in the passwd file. 6216 */ 6217 6218 /* ARGSUSED3 */ 6219 char * 6220 user_map_lookup(map, key, av, statp) 6221 MAP *map; 6222 char *key; 6223 char **av; 6224 int *statp; 6225 { 6226 auto bool fuzzy; 6227 SM_MBDB_T user; 6228 6229 if (tTd(38, 20)) 6230 sm_dprintf("user_map_lookup(%s, %s)\n", 6231 map->map_mname, key); 6232 6233 *statp = finduser(key, &fuzzy, &user); 6234 if (*statp != EX_OK) 6235 return NULL; 6236 if (bitset(MF_MATCHONLY, map->map_mflags)) 6237 return map_rewrite(map, key, strlen(key), NULL); 6238 else 6239 { 6240 char *rwval = NULL; 6241 char buf[30]; 6242 6243 switch (map->map_valcolno) 6244 { 6245 case 0: 6246 case 1: 6247 rwval = user.mbdb_name; 6248 break; 6249 6250 case 2: 6251 rwval = "x"; /* passwd no longer supported */ 6252 break; 6253 6254 case 3: 6255 (void) sm_snprintf(buf, sizeof(buf), "%d", 6256 (int) user.mbdb_uid); 6257 rwval = buf; 6258 break; 6259 6260 case 4: 6261 (void) sm_snprintf(buf, sizeof(buf), "%d", 6262 (int) user.mbdb_gid); 6263 rwval = buf; 6264 break; 6265 6266 case 5: 6267 rwval = user.mbdb_fullname; 6268 break; 6269 6270 case 6: 6271 rwval = user.mbdb_homedir; 6272 break; 6273 6274 case 7: 6275 rwval = user.mbdb_shell; 6276 break; 6277 default: 6278 syserr("user_map %s: bogus field %d", 6279 map->map_mname, map->map_valcolno); 6280 return NULL; 6281 } 6282 return map_rewrite(map, rwval, strlen(rwval), av); 6283 } 6284 } 6285 /* 6286 ** Program map type. 6287 ** 6288 ** This provides access to arbitrary programs. It should be used 6289 ** only very sparingly, since there is no way to bound the cost 6290 ** of invoking an arbitrary program. 6291 */ 6292 6293 char * 6294 prog_map_lookup(map, name, av, statp) 6295 MAP *map; 6296 char *name; 6297 char **av; 6298 int *statp; 6299 { 6300 int i; 6301 int save_errno; 6302 int fd; 6303 int status; 6304 auto pid_t pid; 6305 register char *p; 6306 char *rval; 6307 char *argv[MAXPV + 1]; 6308 char buf[MAXLINE]; 6309 6310 if (tTd(38, 20)) 6311 sm_dprintf("prog_map_lookup(%s, %s) %s\n", 6312 map->map_mname, name, map->map_file); 6313 6314 i = 0; 6315 argv[i++] = map->map_file; 6316 if (map->map_rebuild != NULL) 6317 { 6318 (void) sm_strlcpy(buf, map->map_rebuild, sizeof(buf)); 6319 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) 6320 { 6321 if (i >= MAXPV - 1) 6322 break; 6323 argv[i++] = p; 6324 } 6325 } 6326 argv[i++] = name; 6327 argv[i] = NULL; 6328 if (tTd(38, 21)) 6329 { 6330 sm_dprintf("prog_open:"); 6331 for (i = 0; argv[i] != NULL; i++) 6332 sm_dprintf(" %s", argv[i]); 6333 sm_dprintf("\n"); 6334 } 6335 (void) sm_blocksignal(SIGCHLD); 6336 pid = prog_open(argv, &fd, CurEnv); 6337 if (pid < 0) 6338 { 6339 if (!bitset(MF_OPTIONAL, map->map_mflags)) 6340 syserr("prog_map_lookup(%s) failed (%s) -- closing", 6341 map->map_mname, sm_errstring(errno)); 6342 else if (tTd(38, 9)) 6343 sm_dprintf("prog_map_lookup(%s) failed (%s) -- closing", 6344 map->map_mname, sm_errstring(errno)); 6345 map->map_mflags &= ~(MF_VALID|MF_OPEN); 6346 *statp = EX_OSFILE; 6347 return NULL; 6348 } 6349 i = read(fd, buf, sizeof(buf) - 1); 6350 if (i < 0) 6351 { 6352 syserr("prog_map_lookup(%s): read error %s", 6353 map->map_mname, sm_errstring(errno)); 6354 rval = NULL; 6355 } 6356 else if (i == 0) 6357 { 6358 if (tTd(38, 20)) 6359 sm_dprintf("prog_map_lookup(%s): empty answer\n", 6360 map->map_mname); 6361 rval = NULL; 6362 } 6363 else 6364 { 6365 buf[i] = '\0'; 6366 p = strchr(buf, '\n'); 6367 if (p != NULL) 6368 *p = '\0'; 6369 6370 /* collect the return value */ 6371 if (bitset(MF_MATCHONLY, map->map_mflags)) 6372 rval = map_rewrite(map, name, strlen(name), NULL); 6373 else 6374 rval = map_rewrite(map, buf, strlen(buf), av); 6375 6376 /* now flush any additional output */ 6377 while ((i = read(fd, buf, sizeof(buf))) > 0) 6378 continue; 6379 } 6380 6381 /* wait for the process to terminate */ 6382 (void) close(fd); 6383 status = waitfor(pid); 6384 save_errno = errno; 6385 (void) sm_releasesignal(SIGCHLD); 6386 errno = save_errno; 6387 6388 if (status == -1) 6389 { 6390 syserr("prog_map_lookup(%s): wait error %s", 6391 map->map_mname, sm_errstring(errno)); 6392 *statp = EX_SOFTWARE; 6393 rval = NULL; 6394 } 6395 else if (WIFEXITED(status)) 6396 { 6397 if ((*statp = WEXITSTATUS(status)) != EX_OK) 6398 rval = NULL; 6399 } 6400 else 6401 { 6402 syserr("prog_map_lookup(%s): child died on signal %d", 6403 map->map_mname, status); 6404 *statp = EX_UNAVAILABLE; 6405 rval = NULL; 6406 } 6407 return rval; 6408 } 6409 /* 6410 ** Sequenced map type. 6411 ** 6412 ** Tries each map in order until something matches, much like 6413 ** implicit. Stores go to the first map in the list that can 6414 ** support storing. 6415 ** 6416 ** This is slightly unusual in that there are two interfaces. 6417 ** The "sequence" interface lets you stack maps arbitrarily. 6418 ** The "switch" interface builds a sequence map by looking 6419 ** at a system-dependent configuration file such as 6420 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. 6421 ** 6422 ** We don't need an explicit open, since all maps are 6423 ** opened on demand. 6424 */ 6425 6426 /* 6427 ** SEQ_MAP_PARSE -- Sequenced map parsing 6428 */ 6429 6430 bool 6431 seq_map_parse(map, ap) 6432 MAP *map; 6433 char *ap; 6434 { 6435 int maxmap; 6436 6437 if (tTd(38, 2)) 6438 sm_dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap); 6439 maxmap = 0; 6440 while (*ap != '\0') 6441 { 6442 register char *p; 6443 STAB *s; 6444 6445 /* find beginning of map name */ 6446 while (isascii(*ap) && isspace(*ap)) 6447 ap++; 6448 for (p = ap; 6449 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.'; 6450 p++) 6451 continue; 6452 if (*p != '\0') 6453 *p++ = '\0'; 6454 while (*p != '\0' && (!isascii(*p) || !isalnum(*p))) 6455 p++; 6456 if (*ap == '\0') 6457 { 6458 ap = p; 6459 continue; 6460 } 6461 s = stab(ap, ST_MAP, ST_FIND); 6462 if (s == NULL) 6463 { 6464 syserr("Sequence map %s: unknown member map %s", 6465 map->map_mname, ap); 6466 } 6467 else if (maxmap >= MAXMAPSTACK) 6468 { 6469 syserr("Sequence map %s: too many member maps (%d max)", 6470 map->map_mname, MAXMAPSTACK); 6471 maxmap++; 6472 } 6473 else if (maxmap < MAXMAPSTACK) 6474 { 6475 map->map_stack[maxmap++] = &s->s_map; 6476 } 6477 ap = p; 6478 } 6479 return true; 6480 } 6481 6482 /* 6483 ** SWITCH_MAP_OPEN -- open a switched map 6484 ** 6485 ** This looks at the system-dependent configuration and builds 6486 ** a sequence map that does the same thing. 6487 ** 6488 ** Every system must define a switch_map_find routine in conf.c 6489 ** that will return the list of service types associated with a 6490 ** given service class. 6491 */ 6492 6493 bool 6494 switch_map_open(map, mode) 6495 MAP *map; 6496 int mode; 6497 { 6498 int mapno; 6499 int nmaps; 6500 char *maptype[MAXMAPSTACK]; 6501 6502 if (tTd(38, 2)) 6503 sm_dprintf("switch_map_open(%s, %s, %d)\n", 6504 map->map_mname, map->map_file, mode); 6505 6506 mode &= O_ACCMODE; 6507 nmaps = switch_map_find(map->map_file, maptype, map->map_return); 6508 if (tTd(38, 19)) 6509 { 6510 sm_dprintf("\tswitch_map_find => %d\n", nmaps); 6511 for (mapno = 0; mapno < nmaps; mapno++) 6512 sm_dprintf("\t\t%s\n", maptype[mapno]); 6513 } 6514 if (nmaps <= 0 || nmaps > MAXMAPSTACK) 6515 return false; 6516 6517 for (mapno = 0; mapno < nmaps; mapno++) 6518 { 6519 register STAB *s; 6520 char nbuf[MAXNAME + 1]; 6521 6522 if (maptype[mapno] == NULL) 6523 continue; 6524 (void) sm_strlcpyn(nbuf, sizeof(nbuf), 3, 6525 map->map_mname, ".", maptype[mapno]); 6526 s = stab(nbuf, ST_MAP, ST_FIND); 6527 if (s == NULL) 6528 { 6529 syserr("Switch map %s: unknown member map %s", 6530 map->map_mname, nbuf); 6531 } 6532 else 6533 { 6534 map->map_stack[mapno] = &s->s_map; 6535 if (tTd(38, 4)) 6536 sm_dprintf("\tmap_stack[%d] = %s:%s\n", 6537 mapno, 6538 s->s_map.map_class->map_cname, 6539 nbuf); 6540 } 6541 } 6542 return true; 6543 } 6544 6545 #if 0 6546 /* 6547 ** SEQ_MAP_CLOSE -- close all underlying maps 6548 */ 6549 6550 void 6551 seq_map_close(map) 6552 MAP *map; 6553 { 6554 int mapno; 6555 6556 if (tTd(38, 9)) 6557 sm_dprintf("seq_map_close(%s)\n", map->map_mname); 6558 6559 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6560 { 6561 MAP *mm = map->map_stack[mapno]; 6562 6563 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags)) 6564 continue; 6565 mm->map_mflags |= MF_CLOSING; 6566 mm->map_class->map_close(mm); 6567 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 6568 } 6569 } 6570 #endif /* 0 */ 6571 6572 /* 6573 ** SEQ_MAP_LOOKUP -- sequenced map lookup 6574 */ 6575 6576 char * 6577 seq_map_lookup(map, key, args, pstat) 6578 MAP *map; 6579 char *key; 6580 char **args; 6581 int *pstat; 6582 { 6583 int mapno; 6584 int mapbit = 0x01; 6585 bool tempfail = false; 6586 6587 if (tTd(38, 20)) 6588 sm_dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key); 6589 6590 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) 6591 { 6592 MAP *mm = map->map_stack[mapno]; 6593 char *rv; 6594 6595 if (mm == NULL) 6596 continue; 6597 if (!bitset(MF_OPEN, mm->map_mflags) && 6598 !openmap(mm)) 6599 { 6600 if (bitset(mapbit, map->map_return[MA_UNAVAIL])) 6601 { 6602 *pstat = EX_UNAVAILABLE; 6603 return NULL; 6604 } 6605 continue; 6606 } 6607 *pstat = EX_OK; 6608 rv = mm->map_class->map_lookup(mm, key, args, pstat); 6609 if (rv != NULL) 6610 return rv; 6611 if (*pstat == EX_TEMPFAIL) 6612 { 6613 if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) 6614 return NULL; 6615 tempfail = true; 6616 } 6617 else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) 6618 break; 6619 } 6620 if (tempfail) 6621 *pstat = EX_TEMPFAIL; 6622 else if (*pstat == EX_OK) 6623 *pstat = EX_NOTFOUND; 6624 return NULL; 6625 } 6626 6627 /* 6628 ** SEQ_MAP_STORE -- sequenced map store 6629 */ 6630 6631 void 6632 seq_map_store(map, key, val) 6633 MAP *map; 6634 char *key; 6635 char *val; 6636 { 6637 int mapno; 6638 6639 if (tTd(38, 12)) 6640 sm_dprintf("seq_map_store(%s, %s, %s)\n", 6641 map->map_mname, key, val); 6642 6643 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6644 { 6645 MAP *mm = map->map_stack[mapno]; 6646 6647 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags)) 6648 continue; 6649 6650 mm->map_class->map_store(mm, key, val); 6651 return; 6652 } 6653 syserr("seq_map_store(%s, %s, %s): no writable map", 6654 map->map_mname, key, val); 6655 } 6656 /* 6657 ** NULL stubs 6658 */ 6659 6660 /* ARGSUSED */ 6661 bool 6662 null_map_open(map, mode) 6663 MAP *map; 6664 int mode; 6665 { 6666 return true; 6667 } 6668 6669 /* ARGSUSED */ 6670 void 6671 null_map_close(map) 6672 MAP *map; 6673 { 6674 return; 6675 } 6676 6677 char * 6678 null_map_lookup(map, key, args, pstat) 6679 MAP *map; 6680 char *key; 6681 char **args; 6682 int *pstat; 6683 { 6684 *pstat = EX_NOTFOUND; 6685 return NULL; 6686 } 6687 6688 /* ARGSUSED */ 6689 void 6690 null_map_store(map, key, val) 6691 MAP *map; 6692 char *key; 6693 char *val; 6694 { 6695 return; 6696 } 6697 6698 /* 6699 ** BOGUS stubs 6700 */ 6701 6702 char * 6703 bogus_map_lookup(map, key, args, pstat) 6704 MAP *map; 6705 char *key; 6706 char **args; 6707 int *pstat; 6708 { 6709 *pstat = EX_TEMPFAIL; 6710 return NULL; 6711 } 6712 6713 MAPCLASS BogusMapClass = 6714 { 6715 "bogus-map", NULL, 0, 6716 NULL, bogus_map_lookup, null_map_store, 6717 null_map_open, null_map_close, 6718 }; 6719 /* 6720 ** MACRO modules 6721 */ 6722 6723 char * 6724 macro_map_lookup(map, name, av, statp) 6725 MAP *map; 6726 char *name; 6727 char **av; 6728 int *statp; 6729 { 6730 int mid; 6731 6732 if (tTd(38, 20)) 6733 sm_dprintf("macro_map_lookup(%s, %s)\n", map->map_mname, 6734 name == NULL ? "NULL" : name); 6735 6736 if (name == NULL || 6737 *name == '\0' || 6738 (mid = macid(name)) == 0) 6739 { 6740 *statp = EX_CONFIG; 6741 return NULL; 6742 } 6743 6744 if (av[1] == NULL) 6745 macdefine(&CurEnv->e_macro, A_PERM, mid, NULL); 6746 else 6747 macdefine(&CurEnv->e_macro, A_TEMP, mid, av[1]); 6748 6749 *statp = EX_OK; 6750 return ""; 6751 } 6752 /* 6753 ** REGEX modules 6754 */ 6755 6756 #if MAP_REGEX 6757 6758 # include <regex.h> 6759 6760 # define DEFAULT_DELIM CONDELSE 6761 # define END_OF_FIELDS -1 6762 # define ERRBUF_SIZE 80 6763 # define MAX_MATCH 32 6764 6765 # define xnalloc(s) memset(xalloc(s), '\0', s); 6766 6767 struct regex_map 6768 { 6769 regex_t *regex_pattern_buf; /* xalloc it */ 6770 int *regex_subfields; /* move to type MAP */ 6771 char *regex_delim; /* move to type MAP */ 6772 }; 6773 6774 static int parse_fields __P((char *, int *, int, int)); 6775 static char *regex_map_rewrite __P((MAP *, const char*, size_t, char **)); 6776 6777 static int 6778 parse_fields(s, ibuf, blen, nr_substrings) 6779 char *s; 6780 int *ibuf; /* array */ 6781 int blen; /* number of elements in ibuf */ 6782 int nr_substrings; /* number of substrings in the pattern */ 6783 { 6784 register char *cp; 6785 int i = 0; 6786 bool lastone = false; 6787 6788 blen--; /* for terminating END_OF_FIELDS */ 6789 cp = s; 6790 do 6791 { 6792 for (;; cp++) 6793 { 6794 if (*cp == ',') 6795 { 6796 *cp = '\0'; 6797 break; 6798 } 6799 if (*cp == '\0') 6800 { 6801 lastone = true; 6802 break; 6803 } 6804 } 6805 if (i < blen) 6806 { 6807 int val = atoi(s); 6808 6809 if (val < 0 || val >= nr_substrings) 6810 { 6811 syserr("field (%d) out of range, only %d substrings in pattern", 6812 val, nr_substrings); 6813 return -1; 6814 } 6815 ibuf[i++] = val; 6816 } 6817 else 6818 { 6819 syserr("too many fields, %d max", blen); 6820 return -1; 6821 } 6822 s = ++cp; 6823 } while (!lastone); 6824 ibuf[i] = END_OF_FIELDS; 6825 return i; 6826 } 6827 6828 bool 6829 regex_map_init(map, ap) 6830 MAP *map; 6831 char *ap; 6832 { 6833 int regerr; 6834 struct regex_map *map_p; 6835 register char *p; 6836 char *sub_param = NULL; 6837 int pflags; 6838 static char defdstr[] = { (char) DEFAULT_DELIM, '\0' }; 6839 6840 if (tTd(38, 2)) 6841 sm_dprintf("regex_map_init: mapname '%s', args '%s'\n", 6842 map->map_mname, ap); 6843 6844 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; 6845 p = ap; 6846 map_p = (struct regex_map *) xnalloc(sizeof(*map_p)); 6847 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t)); 6848 6849 for (;;) 6850 { 6851 while (isascii(*p) && isspace(*p)) 6852 p++; 6853 if (*p != '-') 6854 break; 6855 switch (*++p) 6856 { 6857 case 'n': /* not */ 6858 map->map_mflags |= MF_REGEX_NOT; 6859 break; 6860 6861 case 'f': /* case sensitive */ 6862 map->map_mflags |= MF_NOFOLDCASE; 6863 pflags &= ~REG_ICASE; 6864 break; 6865 6866 case 'b': /* basic regular expressions */ 6867 pflags &= ~REG_EXTENDED; 6868 break; 6869 6870 case 's': /* substring match () syntax */ 6871 sub_param = ++p; 6872 pflags &= ~REG_NOSUB; 6873 break; 6874 6875 case 'd': /* delimiter */ 6876 map_p->regex_delim = ++p; 6877 break; 6878 6879 case 'a': /* map append */ 6880 map->map_app = ++p; 6881 break; 6882 6883 case 'm': /* matchonly */ 6884 map->map_mflags |= MF_MATCHONLY; 6885 break; 6886 6887 case 'q': 6888 map->map_mflags |= MF_KEEPQUOTES; 6889 break; 6890 6891 case 'S': 6892 map->map_spacesub = *++p; 6893 break; 6894 6895 case 'D': 6896 map->map_mflags |= MF_DEFER; 6897 break; 6898 6899 } 6900 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 6901 p++; 6902 if (*p != '\0') 6903 *p++ = '\0'; 6904 } 6905 if (tTd(38, 3)) 6906 sm_dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags); 6907 6908 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0) 6909 { 6910 /* Errorhandling */ 6911 char errbuf[ERRBUF_SIZE]; 6912 6913 (void) regerror(regerr, map_p->regex_pattern_buf, 6914 errbuf, sizeof(errbuf)); 6915 syserr("pattern-compile-error: %s", errbuf); 6916 sm_free(map_p->regex_pattern_buf); /* XXX */ 6917 sm_free(map_p); /* XXX */ 6918 return false; 6919 } 6920 6921 if (map->map_app != NULL) 6922 map->map_app = newstr(map->map_app); 6923 if (map_p->regex_delim != NULL) 6924 map_p->regex_delim = newstr(map_p->regex_delim); 6925 else 6926 map_p->regex_delim = defdstr; 6927 6928 if (!bitset(REG_NOSUB, pflags)) 6929 { 6930 /* substring matching */ 6931 int substrings; 6932 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1)); 6933 6934 substrings = map_p->regex_pattern_buf->re_nsub + 1; 6935 6936 if (tTd(38, 3)) 6937 sm_dprintf("regex_map_init: nr of substrings %d\n", 6938 substrings); 6939 6940 if (substrings >= MAX_MATCH) 6941 { 6942 syserr("too many substrings, %d max", MAX_MATCH); 6943 sm_free(map_p->regex_pattern_buf); /* XXX */ 6944 sm_free(map_p); /* XXX */ 6945 return false; 6946 } 6947 if (sub_param != NULL && sub_param[0] != '\0') 6948 { 6949 /* optional parameter -sfields */ 6950 if (parse_fields(sub_param, fields, 6951 MAX_MATCH + 1, substrings) == -1) 6952 return false; 6953 } 6954 else 6955 { 6956 int i; 6957 6958 /* set default fields */ 6959 for (i = 0; i < substrings; i++) 6960 fields[i] = i; 6961 fields[i] = END_OF_FIELDS; 6962 } 6963 map_p->regex_subfields = fields; 6964 if (tTd(38, 3)) 6965 { 6966 int *ip; 6967 6968 sm_dprintf("regex_map_init: subfields"); 6969 for (ip = fields; *ip != END_OF_FIELDS; ip++) 6970 sm_dprintf(" %d", *ip); 6971 sm_dprintf("\n"); 6972 } 6973 } 6974 map->map_db1 = (ARBPTR_T) map_p; /* dirty hack */ 6975 return true; 6976 } 6977 6978 static char * 6979 regex_map_rewrite(map, s, slen, av) 6980 MAP *map; 6981 const char *s; 6982 size_t slen; 6983 char **av; 6984 { 6985 if (bitset(MF_MATCHONLY, map->map_mflags)) 6986 return map_rewrite(map, av[0], strlen(av[0]), NULL); 6987 else 6988 return map_rewrite(map, s, slen, av); 6989 } 6990 6991 char * 6992 regex_map_lookup(map, name, av, statp) 6993 MAP *map; 6994 char *name; 6995 char **av; 6996 int *statp; 6997 { 6998 int reg_res; 6999 struct regex_map *map_p; 7000 regmatch_t pmatch[MAX_MATCH]; 7001 7002 if (tTd(38, 20)) 7003 { 7004 char **cpp; 7005 7006 sm_dprintf("regex_map_lookup: key '%s'\n", name); 7007 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7008 sm_dprintf("regex_map_lookup: arg '%s'\n", *cpp); 7009 } 7010 7011 map_p = (struct regex_map *)(map->map_db1); 7012 reg_res = regexec(map_p->regex_pattern_buf, 7013 name, MAX_MATCH, pmatch, 0); 7014 7015 if (bitset(MF_REGEX_NOT, map->map_mflags)) 7016 { 7017 /* option -n */ 7018 if (reg_res == REG_NOMATCH) 7019 return regex_map_rewrite(map, "", (size_t) 0, av); 7020 else 7021 return NULL; 7022 } 7023 if (reg_res == REG_NOMATCH) 7024 return NULL; 7025 7026 if (map_p->regex_subfields != NULL) 7027 { 7028 /* option -s */ 7029 static char retbuf[MAXNAME]; 7030 int fields[MAX_MATCH + 1]; 7031 bool first = true; 7032 int anglecnt = 0, cmntcnt = 0, spacecnt = 0; 7033 bool quotemode = false, bslashmode = false; 7034 register char *dp, *sp; 7035 char *endp, *ldp; 7036 int *ip; 7037 7038 dp = retbuf; 7039 ldp = retbuf + sizeof(retbuf) - 1; 7040 7041 if (av[1] != NULL) 7042 { 7043 if (parse_fields(av[1], fields, MAX_MATCH + 1, 7044 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1) 7045 { 7046 *statp = EX_CONFIG; 7047 return NULL; 7048 } 7049 ip = fields; 7050 } 7051 else 7052 ip = map_p->regex_subfields; 7053 7054 for ( ; *ip != END_OF_FIELDS; ip++) 7055 { 7056 if (!first) 7057 { 7058 for (sp = map_p->regex_delim; *sp; sp++) 7059 { 7060 if (dp < ldp) 7061 *dp++ = *sp; 7062 } 7063 } 7064 else 7065 first = false; 7066 7067 if (*ip >= MAX_MATCH || 7068 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0) 7069 continue; 7070 7071 sp = name + pmatch[*ip].rm_so; 7072 endp = name + pmatch[*ip].rm_eo; 7073 for (; endp > sp; sp++) 7074 { 7075 if (dp < ldp) 7076 { 7077 if (bslashmode) 7078 { 7079 *dp++ = *sp; 7080 bslashmode = false; 7081 } 7082 else if (quotemode && *sp != '"' && 7083 *sp != '\\') 7084 { 7085 *dp++ = *sp; 7086 } 7087 else switch (*dp++ = *sp) 7088 { 7089 case '\\': 7090 bslashmode = true; 7091 break; 7092 7093 case '(': 7094 cmntcnt++; 7095 break; 7096 7097 case ')': 7098 cmntcnt--; 7099 break; 7100 7101 case '<': 7102 anglecnt++; 7103 break; 7104 7105 case '>': 7106 anglecnt--; 7107 break; 7108 7109 case ' ': 7110 spacecnt++; 7111 break; 7112 7113 case '"': 7114 quotemode = !quotemode; 7115 break; 7116 } 7117 } 7118 } 7119 } 7120 if (anglecnt != 0 || cmntcnt != 0 || quotemode || 7121 bslashmode || spacecnt != 0) 7122 { 7123 sm_syslog(LOG_WARNING, NOQID, 7124 "Warning: regex may cause prescan() failure map=%s lookup=%s", 7125 map->map_mname, name); 7126 return NULL; 7127 } 7128 7129 *dp = '\0'; 7130 7131 return regex_map_rewrite(map, retbuf, strlen(retbuf), av); 7132 } 7133 return regex_map_rewrite(map, "", (size_t)0, av); 7134 } 7135 #endif /* MAP_REGEX */ 7136 /* 7137 ** NSD modules 7138 */ 7139 #if MAP_NSD 7140 7141 # include <ndbm.h> 7142 # define _DATUM_DEFINED 7143 # include <ns_api.h> 7144 7145 typedef struct ns_map_list 7146 { 7147 ns_map_t *map; /* XXX ns_ ? */ 7148 char *mapname; 7149 struct ns_map_list *next; 7150 } ns_map_list_t; 7151 7152 static ns_map_t * 7153 ns_map_t_find(mapname) 7154 char *mapname; 7155 { 7156 static ns_map_list_t *ns_maps = NULL; 7157 ns_map_list_t *ns_map; 7158 7159 /* walk the list of maps looking for the correctly named map */ 7160 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next) 7161 { 7162 if (strcmp(ns_map->mapname, mapname) == 0) 7163 break; 7164 } 7165 7166 /* if we are looking at a NULL ns_map_list_t, then create a new one */ 7167 if (ns_map == NULL) 7168 { 7169 ns_map = (ns_map_list_t *) xalloc(sizeof(*ns_map)); 7170 ns_map->mapname = newstr(mapname); 7171 ns_map->map = (ns_map_t *) xalloc(sizeof(*ns_map->map)); 7172 memset(ns_map->map, '\0', sizeof(*ns_map->map)); 7173 ns_map->next = ns_maps; 7174 ns_maps = ns_map; 7175 } 7176 return ns_map->map; 7177 } 7178 7179 char * 7180 nsd_map_lookup(map, name, av, statp) 7181 MAP *map; 7182 char *name; 7183 char **av; 7184 int *statp; 7185 { 7186 int buflen, r; 7187 char *p; 7188 ns_map_t *ns_map; 7189 char keybuf[MAXNAME + 1]; 7190 char buf[MAXLINE]; 7191 7192 if (tTd(38, 20)) 7193 sm_dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name); 7194 7195 buflen = strlen(name); 7196 if (buflen > sizeof(keybuf) - 1) 7197 buflen = sizeof(keybuf) - 1; /* XXX simply cut off? */ 7198 memmove(keybuf, name, buflen); 7199 keybuf[buflen] = '\0'; 7200 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 7201 makelower(keybuf); 7202 7203 ns_map = ns_map_t_find(map->map_file); 7204 if (ns_map == NULL) 7205 { 7206 if (tTd(38, 20)) 7207 sm_dprintf("nsd_map_t_find failed\n"); 7208 *statp = EX_UNAVAILABLE; 7209 return NULL; 7210 } 7211 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL, 7212 buf, sizeof(buf)); 7213 if (r == NS_UNAVAIL || r == NS_TRYAGAIN) 7214 { 7215 *statp = EX_TEMPFAIL; 7216 return NULL; 7217 } 7218 if (r == NS_BADREQ 7219 # ifdef NS_NOPERM 7220 || r == NS_NOPERM 7221 # endif /* NS_NOPERM */ 7222 ) 7223 { 7224 *statp = EX_CONFIG; 7225 return NULL; 7226 } 7227 if (r != NS_SUCCESS) 7228 { 7229 *statp = EX_NOTFOUND; 7230 return NULL; 7231 } 7232 7233 *statp = EX_OK; 7234 7235 /* Null out trailing \n */ 7236 if ((p = strchr(buf, '\n')) != NULL) 7237 *p = '\0'; 7238 7239 return map_rewrite(map, buf, strlen(buf), av); 7240 } 7241 #endif /* MAP_NSD */ 7242 7243 char * 7244 arith_map_lookup(map, name, av, statp) 7245 MAP *map; 7246 char *name; 7247 char **av; 7248 int *statp; 7249 { 7250 long r; 7251 long v[2]; 7252 bool res = false; 7253 bool boolres; 7254 static char result[16]; 7255 char **cpp; 7256 7257 if (tTd(38, 2)) 7258 { 7259 sm_dprintf("arith_map_lookup: key '%s'\n", name); 7260 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7261 sm_dprintf("arith_map_lookup: arg '%s'\n", *cpp); 7262 } 7263 r = 0; 7264 boolres = false; 7265 cpp = av; 7266 *statp = EX_OK; 7267 7268 /* 7269 ** read arguments for arith map 7270 ** - no check is made whether they are really numbers 7271 ** - just ignores args after the second 7272 */ 7273 7274 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++) 7275 v[r++] = strtol(*cpp, NULL, 0); 7276 7277 /* operator and (at least) two operands given? */ 7278 if (name != NULL && r == 2) 7279 { 7280 switch (*name) 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 case '+': 7296 r = v[0] + v[1]; 7297 break; 7298 7299 case '-': 7300 r = v[0] - v[1]; 7301 break; 7302 7303 case '*': 7304 r = v[0] * v[1]; 7305 break; 7306 7307 case '/': 7308 if (v[1] == 0) 7309 return NULL; 7310 r = v[0] / v[1]; 7311 break; 7312 7313 case 'l': 7314 res = v[0] < v[1]; 7315 boolres = true; 7316 break; 7317 7318 case '=': 7319 res = v[0] == v[1]; 7320 boolres = true; 7321 break; 7322 7323 case 'r': 7324 r = v[1] - v[0] + 1; 7325 if (r <= 0) 7326 return NULL; 7327 r = get_random() % r + v[0]; 7328 break; 7329 7330 default: 7331 /* XXX */ 7332 *statp = EX_CONFIG; 7333 if (LogLevel > 10) 7334 sm_syslog(LOG_WARNING, NOQID, 7335 "arith_map: unknown operator %c", 7336 isprint(*name) ? *name : '?'); 7337 return NULL; 7338 } 7339 if (boolres) 7340 (void) sm_snprintf(result, sizeof(result), 7341 res ? "TRUE" : "FALSE"); 7342 else 7343 (void) sm_snprintf(result, sizeof(result), "%ld", r); 7344 return result; 7345 } 7346 *statp = EX_CONFIG; 7347 return NULL; 7348 } 7349 7350 #if SOCKETMAP 7351 7352 # if NETINET || NETINET6 7353 # include <arpa/inet.h> 7354 # endif /* NETINET || NETINET6 */ 7355 7356 # define socket_map_next map_stack[0] 7357 7358 /* 7359 ** SOCKET_MAP_OPEN -- open socket table 7360 */ 7361 7362 bool 7363 socket_map_open(map, mode) 7364 MAP *map; 7365 int mode; 7366 { 7367 STAB *s; 7368 int sock = 0; 7369 SOCKADDR_LEN_T addrlen = 0; 7370 int addrno = 0; 7371 int save_errno; 7372 char *p; 7373 char *colon; 7374 char *at; 7375 struct hostent *hp = NULL; 7376 SOCKADDR addr; 7377 7378 if (tTd(38, 2)) 7379 sm_dprintf("socket_map_open(%s, %s, %d)\n", 7380 map->map_mname, map->map_file, mode); 7381 7382 mode &= O_ACCMODE; 7383 7384 /* sendmail doesn't have the ability to write to SOCKET (yet) */ 7385 if (mode != O_RDONLY) 7386 { 7387 /* issue a pseudo-error message */ 7388 errno = SM_EMAPCANTWRITE; 7389 return false; 7390 } 7391 7392 if (*map->map_file == '\0') 7393 { 7394 syserr("socket map \"%s\": empty or missing socket information", 7395 map->map_mname); 7396 return false; 7397 } 7398 7399 s = socket_map_findconn(map->map_file); 7400 if (s->s_socketmap != NULL) 7401 { 7402 /* Copy open connection */ 7403 map->map_db1 = s->s_socketmap->map_db1; 7404 7405 /* Add this map as head of linked list */ 7406 map->socket_map_next = s->s_socketmap; 7407 s->s_socketmap = map; 7408 7409 if (tTd(38, 2)) 7410 sm_dprintf("using cached connection\n"); 7411 return true; 7412 } 7413 7414 if (tTd(38, 2)) 7415 sm_dprintf("opening new connection\n"); 7416 7417 /* following code is ripped from milter.c */ 7418 /* XXX It should be put in a library... */ 7419 7420 /* protocol:filename or protocol:port@host */ 7421 memset(&addr, '\0', sizeof(addr)); 7422 p = map->map_file; 7423 colon = strchr(p, ':'); 7424 if (colon != NULL) 7425 { 7426 *colon = '\0'; 7427 7428 if (*p == '\0') 7429 { 7430 # if NETUNIX 7431 /* default to AF_UNIX */ 7432 addr.sa.sa_family = AF_UNIX; 7433 # else /* NETUNIX */ 7434 # if NETINET 7435 /* default to AF_INET */ 7436 addr.sa.sa_family = AF_INET; 7437 # else /* NETINET */ 7438 # if NETINET6 7439 /* default to AF_INET6 */ 7440 addr.sa.sa_family = AF_INET6; 7441 # else /* NETINET6 */ 7442 /* no protocols available */ 7443 syserr("socket map \"%s\": no valid socket protocols available", 7444 map->map_mname); 7445 return false; 7446 # endif /* NETINET6 */ 7447 # endif /* NETINET */ 7448 # endif /* NETUNIX */ 7449 } 7450 # if NETUNIX 7451 else if (sm_strcasecmp(p, "unix") == 0 || 7452 sm_strcasecmp(p, "local") == 0) 7453 addr.sa.sa_family = AF_UNIX; 7454 # endif /* NETUNIX */ 7455 # if NETINET 7456 else if (sm_strcasecmp(p, "inet") == 0) 7457 addr.sa.sa_family = AF_INET; 7458 # endif /* NETINET */ 7459 # if NETINET6 7460 else if (sm_strcasecmp(p, "inet6") == 0) 7461 addr.sa.sa_family = AF_INET6; 7462 # endif /* NETINET6 */ 7463 else 7464 { 7465 # ifdef EPROTONOSUPPORT 7466 errno = EPROTONOSUPPORT; 7467 # else /* EPROTONOSUPPORT */ 7468 errno = EINVAL; 7469 # endif /* EPROTONOSUPPORT */ 7470 syserr("socket map \"%s\": unknown socket type %s", 7471 map->map_mname, p); 7472 return false; 7473 } 7474 *colon++ = ':'; 7475 } 7476 else 7477 { 7478 colon = p; 7479 #if NETUNIX 7480 /* default to AF_UNIX */ 7481 addr.sa.sa_family = AF_UNIX; 7482 #else /* NETUNIX */ 7483 # if NETINET 7484 /* default to AF_INET */ 7485 addr.sa.sa_family = AF_INET; 7486 # else /* NETINET */ 7487 # if NETINET6 7488 /* default to AF_INET6 */ 7489 addr.sa.sa_family = AF_INET6; 7490 # else /* NETINET6 */ 7491 syserr("socket map \"%s\": unknown socket type %s", 7492 map->map_mname, p); 7493 return false; 7494 # endif /* NETINET6 */ 7495 # endif /* NETINET */ 7496 #endif /* NETUNIX */ 7497 } 7498 7499 # if NETUNIX 7500 if (addr.sa.sa_family == AF_UNIX) 7501 { 7502 long sff = SFF_SAFEDIRPATH|SFF_OPENASROOT|SFF_NOLINK|SFF_EXECOK; 7503 7504 at = colon; 7505 if (strlen(colon) >= sizeof(addr.sunix.sun_path)) 7506 { 7507 syserr("socket map \"%s\": local socket name %s too long", 7508 map->map_mname, colon); 7509 return false; 7510 } 7511 errno = safefile(colon, RunAsUid, RunAsGid, RunAsUserName, sff, 7512 S_IRUSR|S_IWUSR, NULL); 7513 7514 if (errno != 0) 7515 { 7516 /* if not safe, don't create */ 7517 syserr("socket map \"%s\": local socket name %s unsafe", 7518 map->map_mname, colon); 7519 return false; 7520 } 7521 7522 (void) sm_strlcpy(addr.sunix.sun_path, colon, 7523 sizeof(addr.sunix.sun_path)); 7524 addrlen = sizeof(struct sockaddr_un); 7525 } 7526 else 7527 # endif /* NETUNIX */ 7528 # if NETINET || NETINET6 7529 if (false 7530 # if NETINET 7531 || addr.sa.sa_family == AF_INET 7532 # endif /* NETINET */ 7533 # if NETINET6 7534 || addr.sa.sa_family == AF_INET6 7535 # endif /* NETINET6 */ 7536 ) 7537 { 7538 unsigned short port; 7539 7540 /* Parse port@host */ 7541 at = strchr(colon, '@'); 7542 if (at == NULL) 7543 { 7544 syserr("socket map \"%s\": bad address %s (expected port@host)", 7545 map->map_mname, colon); 7546 return false; 7547 } 7548 *at = '\0'; 7549 if (isascii(*colon) && isdigit(*colon)) 7550 port = htons((unsigned short) atoi(colon)); 7551 else 7552 { 7553 # ifdef NO_GETSERVBYNAME 7554 syserr("socket map \"%s\": invalid port number %s", 7555 map->map_mname, colon); 7556 return false; 7557 # else /* NO_GETSERVBYNAME */ 7558 register struct servent *sp; 7559 7560 sp = getservbyname(colon, "tcp"); 7561 if (sp == NULL) 7562 { 7563 syserr("socket map \"%s\": unknown port name %s", 7564 map->map_mname, colon); 7565 return false; 7566 } 7567 port = sp->s_port; 7568 # endif /* NO_GETSERVBYNAME */ 7569 } 7570 *at++ = '@'; 7571 if (*at == '[') 7572 { 7573 char *end; 7574 7575 end = strchr(at, ']'); 7576 if (end != NULL) 7577 { 7578 bool found = false; 7579 # if NETINET 7580 unsigned long hid = INADDR_NONE; 7581 # endif /* NETINET */ 7582 # if NETINET6 7583 struct sockaddr_in6 hid6; 7584 # endif /* NETINET6 */ 7585 7586 *end = '\0'; 7587 # if NETINET 7588 if (addr.sa.sa_family == AF_INET && 7589 (hid = inet_addr(&at[1])) != INADDR_NONE) 7590 { 7591 addr.sin.sin_addr.s_addr = hid; 7592 addr.sin.sin_port = port; 7593 found = true; 7594 } 7595 # endif /* NETINET */ 7596 # if NETINET6 7597 (void) memset(&hid6, '\0', sizeof(hid6)); 7598 if (addr.sa.sa_family == AF_INET6 && 7599 anynet_pton(AF_INET6, &at[1], 7600 &hid6.sin6_addr) == 1) 7601 { 7602 addr.sin6.sin6_addr = hid6.sin6_addr; 7603 addr.sin6.sin6_port = port; 7604 found = true; 7605 } 7606 # endif /* NETINET6 */ 7607 *end = ']'; 7608 if (!found) 7609 { 7610 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"", 7611 map->map_mname, at); 7612 return false; 7613 } 7614 } 7615 else 7616 { 7617 syserr("socket map \"%s\": Invalid numeric domain spec \"%s\"", 7618 map->map_mname, at); 7619 return false; 7620 } 7621 } 7622 else 7623 { 7624 hp = sm_gethostbyname(at, addr.sa.sa_family); 7625 if (hp == NULL) 7626 { 7627 syserr("socket map \"%s\": Unknown host name %s", 7628 map->map_mname, at); 7629 return false; 7630 } 7631 addr.sa.sa_family = hp->h_addrtype; 7632 switch (hp->h_addrtype) 7633 { 7634 # if NETINET 7635 case AF_INET: 7636 memmove(&addr.sin.sin_addr, 7637 hp->h_addr, INADDRSZ); 7638 addr.sin.sin_port = port; 7639 addrlen = sizeof(struct sockaddr_in); 7640 addrno = 1; 7641 break; 7642 # endif /* NETINET */ 7643 7644 # if NETINET6 7645 case AF_INET6: 7646 memmove(&addr.sin6.sin6_addr, 7647 hp->h_addr, IN6ADDRSZ); 7648 addr.sin6.sin6_port = port; 7649 addrlen = sizeof(struct sockaddr_in6); 7650 addrno = 1; 7651 break; 7652 # endif /* NETINET6 */ 7653 7654 default: 7655 syserr("socket map \"%s\": Unknown protocol for %s (%d)", 7656 map->map_mname, at, hp->h_addrtype); 7657 # if NETINET6 7658 freehostent(hp); 7659 # endif /* NETINET6 */ 7660 return false; 7661 } 7662 } 7663 } 7664 else 7665 # endif /* NETINET || NETINET6 */ 7666 { 7667 syserr("socket map \"%s\": unknown socket protocol", 7668 map->map_mname); 7669 return false; 7670 } 7671 7672 /* nope, actually connecting */ 7673 for (;;) 7674 { 7675 sock = socket(addr.sa.sa_family, SOCK_STREAM, 0); 7676 if (sock < 0) 7677 { 7678 save_errno = errno; 7679 if (tTd(38, 5)) 7680 sm_dprintf("socket map \"%s\": error creating socket: %s\n", 7681 map->map_mname, 7682 sm_errstring(save_errno)); 7683 # if NETINET6 7684 if (hp != NULL) 7685 freehostent(hp); 7686 # endif /* NETINET6 */ 7687 return false; 7688 } 7689 7690 if (connect(sock, (struct sockaddr *) &addr, addrlen) >= 0) 7691 break; 7692 7693 /* couldn't connect.... try next address */ 7694 save_errno = errno; 7695 p = CurHostName; 7696 CurHostName = at; 7697 if (tTd(38, 5)) 7698 sm_dprintf("socket_open (%s): open %s failed: %s\n", 7699 map->map_mname, at, sm_errstring(save_errno)); 7700 CurHostName = p; 7701 (void) close(sock); 7702 7703 /* try next address */ 7704 if (hp != NULL && hp->h_addr_list[addrno] != NULL) 7705 { 7706 switch (addr.sa.sa_family) 7707 { 7708 # if NETINET 7709 case AF_INET: 7710 memmove(&addr.sin.sin_addr, 7711 hp->h_addr_list[addrno++], 7712 INADDRSZ); 7713 break; 7714 # endif /* NETINET */ 7715 7716 # if NETINET6 7717 case AF_INET6: 7718 memmove(&addr.sin6.sin6_addr, 7719 hp->h_addr_list[addrno++], 7720 IN6ADDRSZ); 7721 break; 7722 # endif /* NETINET6 */ 7723 7724 default: 7725 if (tTd(38, 5)) 7726 sm_dprintf("socket map \"%s\": Unknown protocol for %s (%d)\n", 7727 map->map_mname, at, 7728 hp->h_addrtype); 7729 # if NETINET6 7730 freehostent(hp); 7731 # endif /* NETINET6 */ 7732 return false; 7733 } 7734 continue; 7735 } 7736 p = CurHostName; 7737 CurHostName = at; 7738 if (tTd(38, 5)) 7739 sm_dprintf("socket map \"%s\": error connecting to socket map: %s\n", 7740 map->map_mname, sm_errstring(save_errno)); 7741 CurHostName = p; 7742 # if NETINET6 7743 if (hp != NULL) 7744 freehostent(hp); 7745 # endif /* NETINET6 */ 7746 return false; 7747 } 7748 # if NETINET6 7749 if (hp != NULL) 7750 { 7751 freehostent(hp); 7752 hp = NULL; 7753 } 7754 # endif /* NETINET6 */ 7755 if ((map->map_db1 = (ARBPTR_T) sm_io_open(SmFtStdiofd, 7756 SM_TIME_DEFAULT, 7757 (void *) &sock, 7758 SM_IO_RDWR, 7759 NULL)) == NULL) 7760 { 7761 close(sock); 7762 if (tTd(38, 2)) 7763 sm_dprintf("socket_open (%s): failed to create stream: %s\n", 7764 map->map_mname, sm_errstring(errno)); 7765 return false; 7766 } 7767 7768 /* Save connection for reuse */ 7769 s->s_socketmap = map; 7770 return true; 7771 } 7772 7773 /* 7774 ** SOCKET_MAP_FINDCONN -- find a SOCKET connection to the server 7775 ** 7776 ** Cache SOCKET connections based on the connection specifier 7777 ** and PID so we don't have multiple connections open to 7778 ** the same server for different maps. Need a separate connection 7779 ** per PID since a parent process may close the map before the 7780 ** child is done with it. 7781 ** 7782 ** Parameters: 7783 ** conn -- SOCKET map connection specifier 7784 ** 7785 ** Returns: 7786 ** Symbol table entry for the SOCKET connection. 7787 */ 7788 7789 static STAB * 7790 socket_map_findconn(conn) 7791 const char *conn; 7792 { 7793 char *nbuf; 7794 STAB *SM_NONVOLATILE s = NULL; 7795 7796 nbuf = sm_stringf_x("%s%c%d", conn, CONDELSE, (int) CurrentPid); 7797 SM_TRY 7798 s = stab(nbuf, ST_SOCKETMAP, ST_ENTER); 7799 SM_FINALLY 7800 sm_free(nbuf); 7801 SM_END_TRY 7802 return s; 7803 } 7804 7805 /* 7806 ** SOCKET_MAP_CLOSE -- close the socket 7807 */ 7808 7809 void 7810 socket_map_close(map) 7811 MAP *map; 7812 { 7813 STAB *s; 7814 MAP *smap; 7815 7816 if (tTd(38, 20)) 7817 sm_dprintf("socket_map_close(%s), pid=%ld\n", map->map_file, 7818 (long) CurrentPid); 7819 7820 /* Check if already closed */ 7821 if (map->map_db1 == NULL) 7822 { 7823 if (tTd(38, 20)) 7824 sm_dprintf("socket_map_close(%s) already closed\n", 7825 map->map_file); 7826 return; 7827 } 7828 sm_io_close((SM_FILE_T *)map->map_db1, SM_TIME_DEFAULT); 7829 7830 /* Mark all the maps that share the connection as closed */ 7831 s = socket_map_findconn(map->map_file); 7832 smap = s->s_socketmap; 7833 while (smap != NULL) 7834 { 7835 MAP *next; 7836 7837 if (tTd(38, 2) && smap != map) 7838 sm_dprintf("socket_map_close(%s): closed %s (shared SOCKET connection)\n", 7839 map->map_mname, smap->map_mname); 7840 7841 smap->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 7842 smap->map_db1 = NULL; 7843 next = smap->socket_map_next; 7844 smap->socket_map_next = NULL; 7845 smap = next; 7846 } 7847 s->s_socketmap = NULL; 7848 } 7849 7850 /* 7851 ** SOCKET_MAP_LOOKUP -- look up a datum in a SOCKET table 7852 */ 7853 7854 char * 7855 socket_map_lookup(map, name, av, statp) 7856 MAP *map; 7857 char *name; 7858 char **av; 7859 int *statp; 7860 { 7861 unsigned int nettolen, replylen, recvlen; 7862 char *replybuf, *rval, *value, *status, *key; 7863 SM_FILE_T *f; 7864 char keybuf[MAXNAME + 1]; 7865 7866 replybuf = NULL; 7867 rval = NULL; 7868 f = (SM_FILE_T *)map->map_db1; 7869 if (tTd(38, 20)) 7870 sm_dprintf("socket_map_lookup(%s, %s) %s\n", 7871 map->map_mname, name, map->map_file); 7872 7873 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 7874 { 7875 nettolen = strlen(name); 7876 if (nettolen > sizeof(keybuf) - 1) 7877 nettolen = sizeof(keybuf) - 1; 7878 memmove(keybuf, name, nettolen); 7879 keybuf[nettolen] = '\0'; 7880 makelower(keybuf); 7881 key = keybuf; 7882 } 7883 else 7884 key = name; 7885 7886 nettolen = strlen(map->map_mname) + 1 + strlen(key); 7887 SM_ASSERT(nettolen > strlen(map->map_mname)); 7888 SM_ASSERT(nettolen > strlen(key)); 7889 if ((sm_io_fprintf(f, SM_TIME_DEFAULT, "%u:%s %s,", 7890 nettolen, map->map_mname, key) == SM_IO_EOF) || 7891 (sm_io_flush(f, SM_TIME_DEFAULT) != 0) || 7892 (sm_io_error(f))) 7893 { 7894 syserr("451 4.3.0 socket_map_lookup(%s): failed to send lookup request", 7895 map->map_mname); 7896 *statp = EX_TEMPFAIL; 7897 goto errcl; 7898 } 7899 7900 if (sm_io_fscanf(f, SM_TIME_DEFAULT, "%9u", &replylen) != 1) 7901 { 7902 syserr("451 4.3.0 socket_map_lookup(%s): failed to read length parameter of reply", 7903 map->map_mname); 7904 *statp = EX_TEMPFAIL; 7905 goto errcl; 7906 } 7907 if (replylen > SOCKETMAP_MAXL) 7908 { 7909 syserr("451 4.3.0 socket_map_lookup(%s): reply too long: %u", 7910 map->map_mname, replylen); 7911 *statp = EX_TEMPFAIL; 7912 goto errcl; 7913 } 7914 if (sm_io_getc(f, SM_TIME_DEFAULT) != ':') 7915 { 7916 syserr("451 4.3.0 socket_map_lookup(%s): missing ':' in reply", 7917 map->map_mname); 7918 *statp = EX_TEMPFAIL; 7919 goto error; 7920 } 7921 7922 replybuf = (char *) sm_malloc(replylen + 1); 7923 if (replybuf == NULL) 7924 { 7925 syserr("451 4.3.0 socket_map_lookup(%s): can't allocate %u bytes", 7926 map->map_mname, replylen + 1); 7927 *statp = EX_OSERR; 7928 goto error; 7929 } 7930 7931 recvlen = sm_io_read(f, SM_TIME_DEFAULT, replybuf, replylen); 7932 if (recvlen < replylen) 7933 { 7934 syserr("451 4.3.0 socket_map_lookup(%s): received only %u of %u reply characters", 7935 map->map_mname, recvlen, replylen); 7936 *statp = EX_TEMPFAIL; 7937 goto errcl; 7938 } 7939 if (sm_io_getc(f, SM_TIME_DEFAULT) != ',') 7940 { 7941 syserr("451 4.3.0 socket_map_lookup(%s): missing ',' in reply", 7942 map->map_mname); 7943 *statp = EX_TEMPFAIL; 7944 goto errcl; 7945 } 7946 status = replybuf; 7947 replybuf[recvlen] = '\0'; 7948 value = strchr(replybuf, ' '); 7949 if (value != NULL) 7950 { 7951 *value = '\0'; 7952 value++; 7953 } 7954 if (strcmp(status, "OK") == 0) 7955 { 7956 *statp = EX_OK; 7957 7958 /* collect the return value */ 7959 if (bitset(MF_MATCHONLY, map->map_mflags)) 7960 rval = map_rewrite(map, key, strlen(key), NULL); 7961 else 7962 rval = map_rewrite(map, value, strlen(value), av); 7963 } 7964 else if (strcmp(status, "NOTFOUND") == 0) 7965 { 7966 *statp = EX_NOTFOUND; 7967 if (tTd(38, 20)) 7968 sm_dprintf("socket_map_lookup(%s): %s not found\n", 7969 map->map_mname, key); 7970 } 7971 else 7972 { 7973 if (tTd(38, 5)) 7974 sm_dprintf("socket_map_lookup(%s, %s): server returned error: type=%s, reason=%s\n", 7975 map->map_mname, key, status, 7976 value ? value : ""); 7977 if ((strcmp(status, "TEMP") == 0) || 7978 (strcmp(status, "TIMEOUT") == 0)) 7979 *statp = EX_TEMPFAIL; 7980 else if(strcmp(status, "PERM") == 0) 7981 *statp = EX_UNAVAILABLE; 7982 else 7983 *statp = EX_PROTOCOL; 7984 } 7985 7986 if (replybuf != NULL) 7987 sm_free(replybuf); 7988 return rval; 7989 7990 errcl: 7991 socket_map_close(map); 7992 error: 7993 if (replybuf != NULL) 7994 sm_free(replybuf); 7995 return rval; 7996 } 7997 #endif /* SOCKETMAP */ 7998