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