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