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