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