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