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