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