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