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.55 2001/08/15 22:08:58 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 if (hp != NULL) 5363 hesiod_free_list(HesiodContext, hp); 5364 return NULL; 5365 } 5366 # else /* HESIOD_INIT */ 5367 if (hp == NULL || hp[0] == NULL) 5368 { 5369 switch (hes_error()) 5370 { 5371 case HES_ER_OK: 5372 *statp = EX_OK; 5373 break; 5374 5375 case HES_ER_NOTFOUND: 5376 *statp = EX_NOTFOUND; 5377 break; 5378 5379 case HES_ER_CONFIG: 5380 *statp = EX_UNAVAILABLE; 5381 break; 5382 5383 case HES_ER_NET: 5384 *statp = EX_TEMPFAIL; 5385 break; 5386 } 5387 return NULL; 5388 } 5389 # endif /* HESIOD_INIT */ 5390 5391 if (bitset(MF_MATCHONLY, map->map_mflags)) 5392 return map_rewrite(map, name, strlen(name), NULL); 5393 else 5394 return map_rewrite(map, hp[0], strlen(hp[0]), av); 5395 } 5396 5397 #endif /* HESIOD */ 5398 /* 5399 ** NeXT NETINFO Modules 5400 */ 5401 5402 #if NETINFO 5403 5404 # define NETINFO_DEFAULT_DIR "/aliases" 5405 # define NETINFO_DEFAULT_PROPERTY "members" 5406 5407 /* 5408 ** NI_MAP_OPEN -- open NetInfo Aliases 5409 */ 5410 5411 bool 5412 ni_map_open(map, mode) 5413 MAP *map; 5414 int mode; 5415 { 5416 if (tTd(38, 2)) 5417 dprintf("ni_map_open(%s, %s, %d)\n", 5418 map->map_mname, map->map_file, mode); 5419 mode &= O_ACCMODE; 5420 5421 if (*map->map_file == '\0') 5422 map->map_file = NETINFO_DEFAULT_DIR; 5423 5424 if (map->map_valcolnm == NULL) 5425 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY; 5426 5427 if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags)) 5428 map->map_coldelim = ','; 5429 5430 return TRUE; 5431 } 5432 5433 5434 /* 5435 ** NI_MAP_LOOKUP -- look up a datum in NetInfo 5436 */ 5437 5438 char * 5439 ni_map_lookup(map, name, av, statp) 5440 MAP *map; 5441 char *name; 5442 char **av; 5443 int *statp; 5444 { 5445 char *res; 5446 char *propval; 5447 5448 if (tTd(38, 20)) 5449 dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name); 5450 5451 propval = ni_propval(map->map_file, map->map_keycolnm, name, 5452 map->map_valcolnm, map->map_coldelim); 5453 5454 if (propval == NULL) 5455 return NULL; 5456 5457 if (bitset(MF_MATCHONLY, map->map_mflags)) 5458 res = map_rewrite(map, name, strlen(name), NULL); 5459 else 5460 res = map_rewrite(map, propval, strlen(propval), av); 5461 sm_free(propval); 5462 return res; 5463 } 5464 5465 5466 static bool 5467 ni_getcanonname(name, hbsize, statp) 5468 char *name; 5469 int hbsize; 5470 int *statp; 5471 { 5472 char *vptr; 5473 char *ptr; 5474 char nbuf[MAXNAME + 1]; 5475 5476 if (tTd(38, 20)) 5477 dprintf("ni_getcanonname(%s)\n", name); 5478 5479 if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) 5480 { 5481 *statp = EX_UNAVAILABLE; 5482 return FALSE; 5483 } 5484 (void) shorten_hostname(nbuf); 5485 5486 /* we only accept single token search key */ 5487 if (strchr(nbuf, '.')) 5488 { 5489 *statp = EX_NOHOST; 5490 return FALSE; 5491 } 5492 5493 /* Do the search */ 5494 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n'); 5495 5496 if (vptr == NULL) 5497 { 5498 *statp = EX_NOHOST; 5499 return FALSE; 5500 } 5501 5502 /* Only want the first machine name */ 5503 if ((ptr = strchr(vptr, '\n')) != NULL) 5504 *ptr = '\0'; 5505 5506 if (hbsize >= strlen(vptr)) 5507 { 5508 (void) strlcpy(name, vptr, hbsize); 5509 sm_free(vptr); 5510 *statp = EX_OK; 5511 return TRUE; 5512 } 5513 *statp = EX_UNAVAILABLE; 5514 sm_free(vptr); 5515 return FALSE; 5516 } 5517 5518 5519 /* 5520 ** NI_PROPVAL -- NetInfo property value lookup routine 5521 ** 5522 ** Parameters: 5523 ** keydir -- the NetInfo directory name in which to search 5524 ** for the key. 5525 ** keyprop -- the name of the property in which to find the 5526 ** property we are interested. Defaults to "name". 5527 ** keyval -- the value for which we are really searching. 5528 ** valprop -- the property name for the value in which we 5529 ** are interested. 5530 ** sepchar -- if non-nil, this can be multiple-valued, and 5531 ** we should return a string separated by this 5532 ** character. 5533 ** 5534 ** Returns: 5535 ** NULL -- if: 5536 ** 1. the directory is not found 5537 ** 2. the property name is not found 5538 ** 3. the property contains multiple values 5539 ** 4. some error occurred 5540 ** else -- the value of the lookup. 5541 ** 5542 ** Example: 5543 ** To search for an alias value, use: 5544 ** ni_propval("/aliases", "name", aliasname, "members", ',') 5545 ** 5546 ** Notes: 5547 ** Caller should free the return value of ni_proval 5548 */ 5549 5550 # include <netinfo/ni.h> 5551 5552 # define LOCAL_NETINFO_DOMAIN "." 5553 # define PARENT_NETINFO_DOMAIN ".." 5554 # define MAX_NI_LEVELS 256 5555 5556 char * 5557 ni_propval(keydir, keyprop, keyval, valprop, sepchar) 5558 char *keydir; 5559 char *keyprop; 5560 char *keyval; 5561 char *valprop; 5562 int sepchar; 5563 { 5564 char *propval = NULL; 5565 int i; 5566 int j, alen, l; 5567 void *ni = NULL; 5568 void *lastni = NULL; 5569 ni_status nis; 5570 ni_id nid; 5571 ni_namelist ninl; 5572 register char *p; 5573 char keybuf[1024]; 5574 5575 /* 5576 ** Create the full key from the two parts. 5577 ** 5578 ** Note that directory can end with, e.g., "name=" to specify 5579 ** an alternate search property. 5580 */ 5581 5582 i = strlen(keydir) + strlen(keyval) + 2; 5583 if (keyprop != NULL) 5584 i += strlen(keyprop) + 1; 5585 if (i >= sizeof keybuf) 5586 return NULL; 5587 (void) strlcpy(keybuf, keydir, sizeof keybuf); 5588 (void) strlcat(keybuf, "/", sizeof keybuf); 5589 if (keyprop != NULL) 5590 { 5591 (void) strlcat(keybuf, keyprop, sizeof keybuf); 5592 (void) strlcat(keybuf, "=", sizeof keybuf); 5593 } 5594 (void) strlcat(keybuf, keyval, sizeof keybuf); 5595 5596 if (tTd(38, 21)) 5597 dprintf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n", 5598 keydir, keyprop, keyval, valprop, sepchar, keybuf); 5599 /* 5600 ** If the passed directory and property name are found 5601 ** in one of netinfo domains we need to search (starting 5602 ** from the local domain moving all the way back to the 5603 ** root domain) set propval to the property's value 5604 ** and return it. 5605 */ 5606 5607 for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++) 5608 { 5609 if (i == 0) 5610 { 5611 nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni); 5612 if (tTd(38, 20)) 5613 dprintf("ni_open(LOCAL) = %d\n", nis); 5614 } 5615 else 5616 { 5617 if (lastni != NULL) 5618 ni_free(lastni); 5619 lastni = ni; 5620 nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni); 5621 if (tTd(38, 20)) 5622 dprintf("ni_open(PARENT) = %d\n", nis); 5623 } 5624 5625 /* 5626 ** Don't bother if we didn't get a handle on a 5627 ** proper domain. This is not necessarily an error. 5628 ** We would get a positive ni_status if, for instance 5629 ** we never found the directory or property and tried 5630 ** to open the parent of the root domain! 5631 */ 5632 5633 if (nis != 0) 5634 break; 5635 5636 /* 5637 ** Find the path to the server information. 5638 */ 5639 5640 if (ni_pathsearch(ni, &nid, keybuf) != 0) 5641 continue; 5642 5643 /* 5644 ** Find associated value information. 5645 */ 5646 5647 if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0) 5648 continue; 5649 5650 if (tTd(38, 20)) 5651 dprintf("ni_lookupprop: len=%d\n", 5652 ninl.ni_namelist_len); 5653 5654 /* 5655 ** See if we have an acceptable number of values. 5656 */ 5657 5658 if (ninl.ni_namelist_len <= 0) 5659 continue; 5660 5661 if (sepchar == '\0' && ninl.ni_namelist_len > 1) 5662 { 5663 ni_namelist_free(&ninl); 5664 continue; 5665 } 5666 5667 /* 5668 ** Calculate number of bytes needed and build result 5669 */ 5670 5671 alen = 1; 5672 for (j = 0; j < ninl.ni_namelist_len; j++) 5673 alen += strlen(ninl.ni_namelist_val[j]) + 1; 5674 propval = p = xalloc(alen); 5675 for (j = 0; j < ninl.ni_namelist_len; j++) 5676 { 5677 (void) strlcpy(p, ninl.ni_namelist_val[j], alen); 5678 l = strlen(p); 5679 p += l; 5680 *p++ = sepchar; 5681 alen -= l + 1; 5682 } 5683 *--p = '\0'; 5684 5685 ni_namelist_free(&ninl); 5686 } 5687 5688 /* 5689 ** Clean up. 5690 */ 5691 5692 if (ni != NULL) 5693 ni_free(ni); 5694 if (lastni != NULL && ni != lastni) 5695 ni_free(lastni); 5696 if (tTd(38, 20)) 5697 dprintf("ni_propval returns: '%s'\n", propval); 5698 5699 return propval; 5700 } 5701 5702 #endif /* NETINFO */ 5703 /* 5704 ** TEXT (unindexed text file) Modules 5705 ** 5706 ** This code donated by Sun Microsystems. 5707 */ 5708 5709 #define map_sff map_lockfd /* overload field */ 5710 5711 5712 /* 5713 ** TEXT_MAP_OPEN -- open text table 5714 */ 5715 5716 bool 5717 text_map_open(map, mode) 5718 MAP *map; 5719 int mode; 5720 { 5721 long sff; 5722 int i; 5723 5724 if (tTd(38, 2)) 5725 dprintf("text_map_open(%s, %s, %d)\n", 5726 map->map_mname, map->map_file, mode); 5727 5728 mode &= O_ACCMODE; 5729 if (mode != O_RDONLY) 5730 { 5731 errno = EPERM; 5732 return FALSE; 5733 } 5734 5735 if (*map->map_file == '\0') 5736 { 5737 syserr("text map \"%s\": file name required", 5738 map->map_mname); 5739 return FALSE; 5740 } 5741 5742 if (map->map_file[0] != '/') 5743 { 5744 syserr("text map \"%s\": file name must be fully qualified", 5745 map->map_mname); 5746 return FALSE; 5747 } 5748 5749 sff = SFF_ROOTOK|SFF_REGONLY; 5750 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 5751 sff |= SFF_NOWLINK; 5752 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 5753 sff |= SFF_SAFEDIRPATH; 5754 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName, 5755 sff, S_IRUSR, NULL)) != 0) 5756 { 5757 int save_errno = errno; 5758 5759 /* cannot open this map */ 5760 if (tTd(38, 2)) 5761 dprintf("\tunsafe map file: %d\n", i); 5762 errno = save_errno; 5763 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5764 syserr("text map \"%s\": unsafe map file %s", 5765 map->map_mname, map->map_file); 5766 return FALSE; 5767 } 5768 5769 if (map->map_keycolnm == NULL) 5770 map->map_keycolno = 0; 5771 else 5772 { 5773 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm))) 5774 { 5775 syserr("text map \"%s\", file %s: -k should specify a number, not %s", 5776 map->map_mname, map->map_file, 5777 map->map_keycolnm); 5778 return FALSE; 5779 } 5780 map->map_keycolno = atoi(map->map_keycolnm); 5781 } 5782 5783 if (map->map_valcolnm == NULL) 5784 map->map_valcolno = 0; 5785 else 5786 { 5787 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm))) 5788 { 5789 syserr("text map \"%s\", file %s: -v should specify a number, not %s", 5790 map->map_mname, map->map_file, 5791 map->map_valcolnm); 5792 return FALSE; 5793 } 5794 map->map_valcolno = atoi(map->map_valcolnm); 5795 } 5796 5797 if (tTd(38, 2)) 5798 { 5799 dprintf("text_map_open(%s, %s): delimiter = ", 5800 map->map_mname, map->map_file); 5801 if (map->map_coldelim == '\0') 5802 dprintf("(white space)\n"); 5803 else 5804 dprintf("%c\n", map->map_coldelim); 5805 } 5806 5807 map->map_sff = sff; 5808 return TRUE; 5809 } 5810 5811 5812 /* 5813 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table 5814 */ 5815 5816 char * 5817 text_map_lookup(map, name, av, statp) 5818 MAP *map; 5819 char *name; 5820 char **av; 5821 int *statp; 5822 { 5823 char *vp; 5824 auto int vsize; 5825 int buflen; 5826 FILE *f; 5827 char delim; 5828 int key_idx; 5829 bool found_it; 5830 long sff = map->map_sff; 5831 char search_key[MAXNAME + 1]; 5832 char linebuf[MAXLINE]; 5833 char buf[MAXNAME + 1]; 5834 5835 found_it = FALSE; 5836 if (tTd(38, 20)) 5837 dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name); 5838 5839 buflen = strlen(name); 5840 if (buflen > sizeof search_key - 1) 5841 buflen = sizeof search_key - 1; 5842 memmove(search_key, name, buflen); 5843 search_key[buflen] = '\0'; 5844 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 5845 makelower(search_key); 5846 5847 f = safefopen(map->map_file, O_RDONLY, FileMode, sff); 5848 if (f == NULL) 5849 { 5850 map->map_mflags &= ~(MF_VALID|MF_OPEN); 5851 *statp = EX_UNAVAILABLE; 5852 return NULL; 5853 } 5854 key_idx = map->map_keycolno; 5855 delim = map->map_coldelim; 5856 while (fgets(linebuf, MAXLINE, f) != NULL) 5857 { 5858 char *p; 5859 5860 /* skip comment line */ 5861 if (linebuf[0] == '#') 5862 continue; 5863 p = strchr(linebuf, '\n'); 5864 if (p != NULL) 5865 *p = '\0'; 5866 p = get_column(linebuf, key_idx, delim, buf, sizeof buf); 5867 if (p != NULL && strcasecmp(search_key, p) == 0) 5868 { 5869 found_it = TRUE; 5870 break; 5871 } 5872 } 5873 (void) fclose(f); 5874 if (!found_it) 5875 { 5876 *statp = EX_NOTFOUND; 5877 return NULL; 5878 } 5879 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf); 5880 if (vp == NULL) 5881 { 5882 *statp = EX_NOTFOUND; 5883 return NULL; 5884 } 5885 vsize = strlen(vp); 5886 *statp = EX_OK; 5887 if (bitset(MF_MATCHONLY, map->map_mflags)) 5888 return map_rewrite(map, name, strlen(name), NULL); 5889 else 5890 return map_rewrite(map, vp, vsize, av); 5891 } 5892 5893 /* 5894 ** TEXT_GETCANONNAME -- look up canonical name in hosts file 5895 */ 5896 5897 static bool 5898 text_getcanonname(name, hbsize, statp) 5899 char *name; 5900 int hbsize; 5901 int *statp; 5902 { 5903 bool found; 5904 char *dot; 5905 FILE *f; 5906 char linebuf[MAXLINE]; 5907 char cbuf[MAXNAME + 1]; 5908 char nbuf[MAXNAME + 1]; 5909 5910 if (tTd(38, 20)) 5911 dprintf("text_getcanonname(%s)\n", name); 5912 5913 if (strlen(name) >= (SIZE_T) sizeof nbuf) 5914 { 5915 *statp = EX_UNAVAILABLE; 5916 return FALSE; 5917 } 5918 (void) strlcpy(nbuf, name, sizeof nbuf); 5919 dot = shorten_hostname(nbuf); 5920 5921 f = fopen(HostsFile, "r"); 5922 if (f == NULL) 5923 { 5924 *statp = EX_UNAVAILABLE; 5925 return FALSE; 5926 } 5927 found = FALSE; 5928 while (!found && fgets(linebuf, MAXLINE, f) != NULL) 5929 { 5930 char *p = strpbrk(linebuf, "#\n"); 5931 5932 if (p != NULL) 5933 *p = '\0'; 5934 if (linebuf[0] != '\0') 5935 found = extract_canonname(nbuf, dot, linebuf, 5936 cbuf, sizeof cbuf); 5937 } 5938 (void) fclose(f); 5939 if (!found) 5940 { 5941 *statp = EX_NOHOST; 5942 return FALSE; 5943 } 5944 5945 if ((SIZE_T) hbsize >= strlen(cbuf)) 5946 { 5947 (void) strlcpy(name, cbuf, hbsize); 5948 *statp = EX_OK; 5949 return TRUE; 5950 } 5951 *statp = EX_UNAVAILABLE; 5952 return FALSE; 5953 } 5954 /* 5955 ** STAB (Symbol Table) Modules 5956 */ 5957 5958 5959 /* 5960 ** STAB_MAP_LOOKUP -- look up alias in symbol table 5961 */ 5962 5963 /* ARGSUSED2 */ 5964 char * 5965 stab_map_lookup(map, name, av, pstat) 5966 register MAP *map; 5967 char *name; 5968 char **av; 5969 int *pstat; 5970 { 5971 register STAB *s; 5972 5973 if (tTd(38, 20)) 5974 dprintf("stab_lookup(%s, %s)\n", 5975 map->map_mname, name); 5976 5977 s = stab(name, ST_ALIAS, ST_FIND); 5978 if (s != NULL) 5979 return s->s_alias; 5980 return NULL; 5981 } 5982 5983 5984 /* 5985 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild) 5986 */ 5987 5988 void 5989 stab_map_store(map, lhs, rhs) 5990 register MAP *map; 5991 char *lhs; 5992 char *rhs; 5993 { 5994 register STAB *s; 5995 5996 s = stab(lhs, ST_ALIAS, ST_ENTER); 5997 s->s_alias = newstr(rhs); 5998 } 5999 6000 6001 /* 6002 ** STAB_MAP_OPEN -- initialize (reads data file) 6003 ** 6004 ** This is a wierd case -- it is only intended as a fallback for 6005 ** aliases. For this reason, opens for write (only during a 6006 ** "newaliases") always fails, and opens for read open the 6007 ** actual underlying text file instead of the database. 6008 */ 6009 6010 bool 6011 stab_map_open(map, mode) 6012 register MAP *map; 6013 int mode; 6014 { 6015 FILE *af; 6016 long sff; 6017 struct stat st; 6018 6019 if (tTd(38, 2)) 6020 dprintf("stab_map_open(%s, %s, %d)\n", 6021 map->map_mname, map->map_file, mode); 6022 6023 mode &= O_ACCMODE; 6024 if (mode != O_RDONLY) 6025 { 6026 errno = EPERM; 6027 return FALSE; 6028 } 6029 6030 sff = SFF_ROOTOK|SFF_REGONLY; 6031 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 6032 sff |= SFF_NOWLINK; 6033 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 6034 sff |= SFF_SAFEDIRPATH; 6035 af = safefopen(map->map_file, O_RDONLY, 0444, sff); 6036 if (af == NULL) 6037 return FALSE; 6038 readaliases(map, af, FALSE, FALSE); 6039 6040 if (fstat(fileno(af), &st) >= 0) 6041 map->map_mtime = st.st_mtime; 6042 (void) fclose(af); 6043 6044 return TRUE; 6045 } 6046 /* 6047 ** Implicit Modules 6048 ** 6049 ** Tries several types. For back compatibility of aliases. 6050 */ 6051 6052 6053 /* 6054 ** IMPL_MAP_LOOKUP -- lookup in best open database 6055 */ 6056 6057 char * 6058 impl_map_lookup(map, name, av, pstat) 6059 MAP *map; 6060 char *name; 6061 char **av; 6062 int *pstat; 6063 { 6064 if (tTd(38, 20)) 6065 dprintf("impl_map_lookup(%s, %s)\n", 6066 map->map_mname, name); 6067 6068 #ifdef NEWDB 6069 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6070 return db_map_lookup(map, name, av, pstat); 6071 #endif /* NEWDB */ 6072 #ifdef NDBM 6073 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6074 return ndbm_map_lookup(map, name, av, pstat); 6075 #endif /* NDBM */ 6076 return stab_map_lookup(map, name, av, pstat); 6077 } 6078 6079 /* 6080 ** IMPL_MAP_STORE -- store in open databases 6081 */ 6082 6083 void 6084 impl_map_store(map, lhs, rhs) 6085 MAP *map; 6086 char *lhs; 6087 char *rhs; 6088 { 6089 if (tTd(38, 12)) 6090 dprintf("impl_map_store(%s, %s, %s)\n", 6091 map->map_mname, lhs, rhs); 6092 #ifdef NEWDB 6093 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6094 db_map_store(map, lhs, rhs); 6095 #endif /* NEWDB */ 6096 #ifdef NDBM 6097 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6098 ndbm_map_store(map, lhs, rhs); 6099 #endif /* NDBM */ 6100 stab_map_store(map, lhs, rhs); 6101 } 6102 6103 /* 6104 ** IMPL_MAP_OPEN -- implicit database open 6105 */ 6106 6107 bool 6108 impl_map_open(map, mode) 6109 MAP *map; 6110 int mode; 6111 { 6112 if (tTd(38, 2)) 6113 dprintf("impl_map_open(%s, %s, %d)\n", 6114 map->map_mname, map->map_file, mode); 6115 6116 mode &= O_ACCMODE; 6117 #ifdef NEWDB 6118 map->map_mflags |= MF_IMPL_HASH; 6119 if (hash_map_open(map, mode)) 6120 { 6121 # ifdef NDBM_YP_COMPAT 6122 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL) 6123 # endif /* NDBM_YP_COMPAT */ 6124 return TRUE; 6125 } 6126 else 6127 map->map_mflags &= ~MF_IMPL_HASH; 6128 #endif /* NEWDB */ 6129 #ifdef NDBM 6130 map->map_mflags |= MF_IMPL_NDBM; 6131 if (ndbm_map_open(map, mode)) 6132 { 6133 return TRUE; 6134 } 6135 else 6136 map->map_mflags &= ~MF_IMPL_NDBM; 6137 #endif /* NDBM */ 6138 6139 #if defined(NEWDB) || defined(NDBM) 6140 if (Verbose) 6141 message("WARNING: cannot open alias database %s%s", 6142 map->map_file, 6143 mode == O_RDONLY ? "; reading text version" : ""); 6144 #else /* defined(NEWDB) || defined(NDBM) */ 6145 if (mode != O_RDONLY) 6146 usrerr("Cannot rebuild aliases: no database format defined"); 6147 #endif /* defined(NEWDB) || defined(NDBM) */ 6148 6149 if (mode == O_RDONLY) 6150 return stab_map_open(map, mode); 6151 else 6152 return FALSE; 6153 } 6154 6155 6156 /* 6157 ** IMPL_MAP_CLOSE -- close any open database(s) 6158 */ 6159 6160 void 6161 impl_map_close(map) 6162 MAP *map; 6163 { 6164 if (tTd(38, 9)) 6165 dprintf("impl_map_close(%s, %s, %lx)\n", 6166 map->map_mname, map->map_file, map->map_mflags); 6167 #ifdef NEWDB 6168 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6169 { 6170 db_map_close(map); 6171 map->map_mflags &= ~MF_IMPL_HASH; 6172 } 6173 #endif /* NEWDB */ 6174 6175 #ifdef NDBM 6176 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6177 { 6178 ndbm_map_close(map); 6179 map->map_mflags &= ~MF_IMPL_NDBM; 6180 } 6181 #endif /* NDBM */ 6182 } 6183 /* 6184 ** User map class. 6185 ** 6186 ** Provides access to the system password file. 6187 */ 6188 6189 /* 6190 ** USER_MAP_OPEN -- open user map 6191 ** 6192 ** Really just binds field names to field numbers. 6193 */ 6194 6195 bool 6196 user_map_open(map, mode) 6197 MAP *map; 6198 int mode; 6199 { 6200 if (tTd(38, 2)) 6201 dprintf("user_map_open(%s, %d)\n", 6202 map->map_mname, mode); 6203 6204 mode &= O_ACCMODE; 6205 if (mode != O_RDONLY) 6206 { 6207 /* issue a pseudo-error message */ 6208 #ifdef ENOSYS 6209 errno = ENOSYS; 6210 #else /* ENOSYS */ 6211 # ifdef EFTYPE 6212 errno = EFTYPE; 6213 # else /* EFTYPE */ 6214 errno = ENXIO; 6215 # endif /* EFTYPE */ 6216 #endif /* ENOSYS */ 6217 return FALSE; 6218 } 6219 if (map->map_valcolnm == NULL) 6220 /* EMPTY */ 6221 /* nothing */ ; 6222 else if (strcasecmp(map->map_valcolnm, "name") == 0) 6223 map->map_valcolno = 1; 6224 else if (strcasecmp(map->map_valcolnm, "passwd") == 0) 6225 map->map_valcolno = 2; 6226 else if (strcasecmp(map->map_valcolnm, "uid") == 0) 6227 map->map_valcolno = 3; 6228 else if (strcasecmp(map->map_valcolnm, "gid") == 0) 6229 map->map_valcolno = 4; 6230 else if (strcasecmp(map->map_valcolnm, "gecos") == 0) 6231 map->map_valcolno = 5; 6232 else if (strcasecmp(map->map_valcolnm, "dir") == 0) 6233 map->map_valcolno = 6; 6234 else if (strcasecmp(map->map_valcolnm, "shell") == 0) 6235 map->map_valcolno = 7; 6236 else 6237 { 6238 syserr("User map %s: unknown column name %s", 6239 map->map_mname, map->map_valcolnm); 6240 return FALSE; 6241 } 6242 return TRUE; 6243 } 6244 6245 6246 /* 6247 ** USER_MAP_LOOKUP -- look up a user in the passwd file. 6248 */ 6249 6250 /* ARGSUSED3 */ 6251 char * 6252 user_map_lookup(map, key, av, statp) 6253 MAP *map; 6254 char *key; 6255 char **av; 6256 int *statp; 6257 { 6258 struct passwd *pw; 6259 auto bool fuzzy; 6260 6261 if (tTd(38, 20)) 6262 dprintf("user_map_lookup(%s, %s)\n", 6263 map->map_mname, key); 6264 6265 pw = finduser(key, &fuzzy); 6266 if (pw == NULL) 6267 return NULL; 6268 if (bitset(MF_MATCHONLY, map->map_mflags)) 6269 return map_rewrite(map, key, strlen(key), NULL); 6270 else 6271 { 6272 char *rwval = NULL; 6273 char buf[30]; 6274 6275 switch (map->map_valcolno) 6276 { 6277 case 0: 6278 case 1: 6279 rwval = pw->pw_name; 6280 break; 6281 6282 case 2: 6283 rwval = pw->pw_passwd; 6284 break; 6285 6286 case 3: 6287 snprintf(buf, sizeof buf, "%d", (int) pw->pw_uid); 6288 rwval = buf; 6289 break; 6290 6291 case 4: 6292 snprintf(buf, sizeof buf, "%d", (int) pw->pw_gid); 6293 rwval = buf; 6294 break; 6295 6296 case 5: 6297 rwval = pw->pw_gecos; 6298 break; 6299 6300 case 6: 6301 rwval = pw->pw_dir; 6302 break; 6303 6304 case 7: 6305 rwval = pw->pw_shell; 6306 break; 6307 } 6308 return map_rewrite(map, rwval, strlen(rwval), av); 6309 } 6310 } 6311 /* 6312 ** Program map type. 6313 ** 6314 ** This provides access to arbitrary programs. It should be used 6315 ** only very sparingly, since there is no way to bound the cost 6316 ** of invoking an arbitrary program. 6317 */ 6318 6319 char * 6320 prog_map_lookup(map, name, av, statp) 6321 MAP *map; 6322 char *name; 6323 char **av; 6324 int *statp; 6325 { 6326 int i; 6327 int save_errno; 6328 int fd; 6329 int status; 6330 auto pid_t pid; 6331 register char *p; 6332 char *rval; 6333 char *argv[MAXPV + 1]; 6334 char buf[MAXLINE]; 6335 6336 if (tTd(38, 20)) 6337 dprintf("prog_map_lookup(%s, %s) %s\n", 6338 map->map_mname, name, map->map_file); 6339 6340 i = 0; 6341 argv[i++] = map->map_file; 6342 if (map->map_rebuild != NULL) 6343 { 6344 snprintf(buf, sizeof buf, "%s", map->map_rebuild); 6345 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) 6346 { 6347 if (i >= MAXPV - 1) 6348 break; 6349 argv[i++] = p; 6350 } 6351 } 6352 argv[i++] = name; 6353 argv[i] = NULL; 6354 if (tTd(38, 21)) 6355 { 6356 dprintf("prog_open:"); 6357 for (i = 0; argv[i] != NULL; i++) 6358 dprintf(" %s", argv[i]); 6359 dprintf("\n"); 6360 } 6361 (void) blocksignal(SIGCHLD); 6362 pid = prog_open(argv, &fd, CurEnv); 6363 if (pid < 0) 6364 { 6365 if (!bitset(MF_OPTIONAL, map->map_mflags)) 6366 syserr("prog_map_lookup(%s) failed (%s) -- closing", 6367 map->map_mname, errstring(errno)); 6368 else if (tTd(38, 9)) 6369 dprintf("prog_map_lookup(%s) failed (%s) -- closing", 6370 map->map_mname, errstring(errno)); 6371 map->map_mflags &= ~(MF_VALID|MF_OPEN); 6372 *statp = EX_OSFILE; 6373 return NULL; 6374 } 6375 i = read(fd, buf, sizeof buf - 1); 6376 if (i < 0) 6377 { 6378 syserr("prog_map_lookup(%s): read error %s\n", 6379 map->map_mname, errstring(errno)); 6380 rval = NULL; 6381 } 6382 else if (i == 0) 6383 { 6384 if (tTd(38, 20)) 6385 dprintf("prog_map_lookup(%s): empty answer\n", 6386 map->map_mname); 6387 rval = NULL; 6388 } 6389 else 6390 { 6391 buf[i] = '\0'; 6392 p = strchr(buf, '\n'); 6393 if (p != NULL) 6394 *p = '\0'; 6395 6396 /* collect the return value */ 6397 if (bitset(MF_MATCHONLY, map->map_mflags)) 6398 rval = map_rewrite(map, name, strlen(name), NULL); 6399 else 6400 rval = map_rewrite(map, buf, strlen(buf), av); 6401 6402 /* now flush any additional output */ 6403 while ((i = read(fd, buf, sizeof buf)) > 0) 6404 continue; 6405 } 6406 6407 /* wait for the process to terminate */ 6408 (void) close(fd); 6409 status = waitfor(pid); 6410 save_errno = errno; 6411 (void) releasesignal(SIGCHLD); 6412 errno = save_errno; 6413 6414 if (status == -1) 6415 { 6416 syserr("prog_map_lookup(%s): wait error %s\n", 6417 map->map_mname, errstring(errno)); 6418 *statp = EX_SOFTWARE; 6419 rval = NULL; 6420 } 6421 else if (WIFEXITED(status)) 6422 { 6423 if ((*statp = WEXITSTATUS(status)) != EX_OK) 6424 rval = NULL; 6425 } 6426 else 6427 { 6428 syserr("prog_map_lookup(%s): child died on signal %d", 6429 map->map_mname, status); 6430 *statp = EX_UNAVAILABLE; 6431 rval = NULL; 6432 } 6433 return rval; 6434 } 6435 /* 6436 ** Sequenced map type. 6437 ** 6438 ** Tries each map in order until something matches, much like 6439 ** implicit. Stores go to the first map in the list that can 6440 ** support storing. 6441 ** 6442 ** This is slightly unusual in that there are two interfaces. 6443 ** The "sequence" interface lets you stack maps arbitrarily. 6444 ** The "switch" interface builds a sequence map by looking 6445 ** at a system-dependent configuration file such as 6446 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. 6447 ** 6448 ** We don't need an explicit open, since all maps are 6449 ** opened during startup, including underlying maps. 6450 */ 6451 6452 /* 6453 ** SEQ_MAP_PARSE -- Sequenced map parsing 6454 */ 6455 6456 bool 6457 seq_map_parse(map, ap) 6458 MAP *map; 6459 char *ap; 6460 { 6461 int maxmap; 6462 6463 if (tTd(38, 2)) 6464 dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap); 6465 maxmap = 0; 6466 while (*ap != '\0') 6467 { 6468 register char *p; 6469 STAB *s; 6470 6471 /* find beginning of map name */ 6472 while (isascii(*ap) && isspace(*ap)) 6473 ap++; 6474 for (p = ap; 6475 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.'; 6476 p++) 6477 continue; 6478 if (*p != '\0') 6479 *p++ = '\0'; 6480 while (*p != '\0' && (!isascii(*p) || !isalnum(*p))) 6481 p++; 6482 if (*ap == '\0') 6483 { 6484 ap = p; 6485 continue; 6486 } 6487 s = stab(ap, ST_MAP, ST_FIND); 6488 if (s == NULL) 6489 { 6490 syserr("Sequence map %s: unknown member map %s", 6491 map->map_mname, ap); 6492 } 6493 else if (maxmap == MAXMAPSTACK) 6494 { 6495 syserr("Sequence map %s: too many member maps (%d max)", 6496 map->map_mname, MAXMAPSTACK); 6497 maxmap++; 6498 } 6499 else if (maxmap < MAXMAPSTACK) 6500 { 6501 map->map_stack[maxmap++] = &s->s_map; 6502 } 6503 ap = p; 6504 } 6505 return TRUE; 6506 } 6507 6508 6509 /* 6510 ** SWITCH_MAP_OPEN -- open a switched map 6511 ** 6512 ** This looks at the system-dependent configuration and builds 6513 ** a sequence map that does the same thing. 6514 ** 6515 ** Every system must define a switch_map_find routine in conf.c 6516 ** that will return the list of service types associated with a 6517 ** given service class. 6518 */ 6519 6520 bool 6521 switch_map_open(map, mode) 6522 MAP *map; 6523 int mode; 6524 { 6525 int mapno; 6526 int nmaps; 6527 char *maptype[MAXMAPSTACK]; 6528 6529 if (tTd(38, 2)) 6530 dprintf("switch_map_open(%s, %s, %d)\n", 6531 map->map_mname, map->map_file, mode); 6532 6533 mode &= O_ACCMODE; 6534 nmaps = switch_map_find(map->map_file, maptype, map->map_return); 6535 if (tTd(38, 19)) 6536 { 6537 dprintf("\tswitch_map_find => %d\n", nmaps); 6538 for (mapno = 0; mapno < nmaps; mapno++) 6539 dprintf("\t\t%s\n", maptype[mapno]); 6540 } 6541 if (nmaps <= 0 || nmaps > MAXMAPSTACK) 6542 return FALSE; 6543 6544 for (mapno = 0; mapno < nmaps; mapno++) 6545 { 6546 register STAB *s; 6547 char nbuf[MAXNAME + 1]; 6548 6549 if (maptype[mapno] == NULL) 6550 continue; 6551 (void) snprintf(nbuf, sizeof nbuf, "%s.%s", 6552 map->map_mname, maptype[mapno]); 6553 s = stab(nbuf, ST_MAP, ST_FIND); 6554 if (s == NULL) 6555 { 6556 syserr("Switch map %s: unknown member map %s", 6557 map->map_mname, nbuf); 6558 } 6559 else 6560 { 6561 map->map_stack[mapno] = &s->s_map; 6562 if (tTd(38, 4)) 6563 dprintf("\tmap_stack[%d] = %s:%s\n", 6564 mapno, s->s_map.map_class->map_cname, 6565 nbuf); 6566 } 6567 } 6568 return TRUE; 6569 } 6570 6571 6572 /* 6573 ** SEQ_MAP_CLOSE -- close all underlying maps 6574 */ 6575 6576 void 6577 seq_map_close(map) 6578 MAP *map; 6579 { 6580 int mapno; 6581 6582 if (tTd(38, 9)) 6583 dprintf("seq_map_close(%s)\n", map->map_mname); 6584 6585 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6586 { 6587 MAP *mm = map->map_stack[mapno]; 6588 6589 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags)) 6590 continue; 6591 mm->map_mflags |= MF_CLOSING; 6592 mm->map_class->map_close(mm); 6593 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING); 6594 } 6595 } 6596 6597 6598 /* 6599 ** SEQ_MAP_LOOKUP -- sequenced map lookup 6600 */ 6601 6602 char * 6603 seq_map_lookup(map, key, args, pstat) 6604 MAP *map; 6605 char *key; 6606 char **args; 6607 int *pstat; 6608 { 6609 int mapno; 6610 int mapbit = 0x01; 6611 bool tempfail = FALSE; 6612 6613 if (tTd(38, 20)) 6614 dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key); 6615 6616 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) 6617 { 6618 MAP *mm = map->map_stack[mapno]; 6619 char *rv; 6620 6621 if (mm == NULL) 6622 continue; 6623 if (!bitset(MF_OPEN, mm->map_mflags) && 6624 !openmap(mm)) 6625 { 6626 if (bitset(mapbit, map->map_return[MA_UNAVAIL])) 6627 { 6628 *pstat = EX_UNAVAILABLE; 6629 return NULL; 6630 } 6631 continue; 6632 } 6633 *pstat = EX_OK; 6634 rv = mm->map_class->map_lookup(mm, key, args, pstat); 6635 if (rv != NULL) 6636 return rv; 6637 if (*pstat == EX_TEMPFAIL) 6638 { 6639 if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) 6640 return NULL; 6641 tempfail = TRUE; 6642 } 6643 else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) 6644 break; 6645 } 6646 if (tempfail) 6647 *pstat = EX_TEMPFAIL; 6648 else if (*pstat == EX_OK) 6649 *pstat = EX_NOTFOUND; 6650 return NULL; 6651 } 6652 6653 6654 /* 6655 ** SEQ_MAP_STORE -- sequenced map store 6656 */ 6657 6658 void 6659 seq_map_store(map, key, val) 6660 MAP *map; 6661 char *key; 6662 char *val; 6663 { 6664 int mapno; 6665 6666 if (tTd(38, 12)) 6667 dprintf("seq_map_store(%s, %s, %s)\n", 6668 map->map_mname, key, val); 6669 6670 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6671 { 6672 MAP *mm = map->map_stack[mapno]; 6673 6674 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags)) 6675 continue; 6676 6677 mm->map_class->map_store(mm, key, val); 6678 return; 6679 } 6680 syserr("seq_map_store(%s, %s, %s): no writable map", 6681 map->map_mname, key, val); 6682 } 6683 /* 6684 ** NULL stubs 6685 */ 6686 6687 /* ARGSUSED */ 6688 bool 6689 null_map_open(map, mode) 6690 MAP *map; 6691 int mode; 6692 { 6693 return TRUE; 6694 } 6695 6696 /* ARGSUSED */ 6697 void 6698 null_map_close(map) 6699 MAP *map; 6700 { 6701 return; 6702 } 6703 6704 char * 6705 null_map_lookup(map, key, args, pstat) 6706 MAP *map; 6707 char *key; 6708 char **args; 6709 int *pstat; 6710 { 6711 *pstat = EX_NOTFOUND; 6712 return NULL; 6713 } 6714 6715 /* ARGSUSED */ 6716 void 6717 null_map_store(map, key, val) 6718 MAP *map; 6719 char *key; 6720 char *val; 6721 { 6722 return; 6723 } 6724 6725 6726 /* 6727 ** BOGUS stubs 6728 */ 6729 6730 char * 6731 bogus_map_lookup(map, key, args, pstat) 6732 MAP *map; 6733 char *key; 6734 char **args; 6735 int *pstat; 6736 { 6737 *pstat = EX_TEMPFAIL; 6738 return NULL; 6739 } 6740 6741 MAPCLASS BogusMapClass = 6742 { 6743 "bogus-map", NULL, 0, 6744 NULL, bogus_map_lookup, null_map_store, 6745 null_map_open, null_map_close, 6746 }; 6747 /* 6748 ** MACRO modules 6749 */ 6750 6751 char * 6752 macro_map_lookup(map, name, av, statp) 6753 MAP *map; 6754 char *name; 6755 char **av; 6756 int *statp; 6757 { 6758 int mid; 6759 6760 if (tTd(38, 20)) 6761 dprintf("macro_map_lookup(%s, %s)\n", map->map_mname, 6762 name == NULL ? "NULL" : name); 6763 6764 if (name == NULL || 6765 *name == '\0' || 6766 (mid = macid(name, NULL)) == '\0') 6767 { 6768 *statp = EX_CONFIG; 6769 return NULL; 6770 } 6771 6772 if (av[1] == NULL) 6773 define(mid, NULL, CurEnv); 6774 else 6775 define(mid, newstr(av[1]), CurEnv); 6776 6777 *statp = EX_OK; 6778 return ""; 6779 } 6780 /* 6781 ** REGEX modules 6782 */ 6783 6784 #ifdef MAP_REGEX 6785 6786 # include <regex.h> 6787 6788 # define DEFAULT_DELIM CONDELSE 6789 6790 # define END_OF_FIELDS -1 6791 6792 # define ERRBUF_SIZE 80 6793 # define MAX_MATCH 32 6794 6795 # define xnalloc(s) memset(xalloc(s), '\0', s); 6796 6797 struct regex_map 6798 { 6799 regex_t *regex_pattern_buf; /* xalloc it */ 6800 int *regex_subfields; /* move to type MAP */ 6801 char *regex_delim; /* move to type MAP */ 6802 }; 6803 6804 static int 6805 parse_fields(s, ibuf, blen, nr_substrings) 6806 char *s; 6807 int *ibuf; /* array */ 6808 int blen; /* number of elements in ibuf */ 6809 int nr_substrings; /* number of substrings in the pattern */ 6810 { 6811 register char *cp; 6812 int i = 0; 6813 bool lastone = FALSE; 6814 6815 blen--; /* for terminating END_OF_FIELDS */ 6816 cp = s; 6817 do 6818 { 6819 for (;; cp++) 6820 { 6821 if (*cp == ',') 6822 { 6823 *cp = '\0'; 6824 break; 6825 } 6826 if (*cp == '\0') 6827 { 6828 lastone = TRUE; 6829 break; 6830 } 6831 } 6832 if (i < blen) 6833 { 6834 int val = atoi(s); 6835 6836 if (val < 0 || val >= nr_substrings) 6837 { 6838 syserr("field (%d) out of range, only %d substrings in pattern", 6839 val, nr_substrings); 6840 return -1; 6841 } 6842 ibuf[i++] = val; 6843 } 6844 else 6845 { 6846 syserr("too many fields, %d max\n", blen); 6847 return -1; 6848 } 6849 s = ++cp; 6850 } while (!lastone); 6851 ibuf[i] = END_OF_FIELDS; 6852 return i; 6853 } 6854 6855 bool 6856 regex_map_init(map, ap) 6857 MAP *map; 6858 char *ap; 6859 { 6860 int regerr; 6861 struct regex_map *map_p; 6862 register char *p; 6863 char *sub_param = NULL; 6864 int pflags; 6865 static char defdstr[] = { (char)DEFAULT_DELIM, '\0' }; 6866 6867 if (tTd(38, 2)) 6868 dprintf("regex_map_init: mapname '%s', args '%s'\n", 6869 map->map_mname, ap); 6870 6871 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; 6872 6873 p = ap; 6874 6875 map_p = (struct regex_map *) xnalloc(sizeof *map_p); 6876 map_p->regex_pattern_buf = (regex_t *)xnalloc(sizeof(regex_t)); 6877 6878 for (;;) 6879 { 6880 while (isascii(*p) && isspace(*p)) 6881 p++; 6882 if (*p != '-') 6883 break; 6884 switch (*++p) 6885 { 6886 case 'n': /* not */ 6887 map->map_mflags |= MF_REGEX_NOT; 6888 break; 6889 6890 case 'f': /* case sensitive */ 6891 map->map_mflags |= MF_NOFOLDCASE; 6892 pflags &= ~REG_ICASE; 6893 break; 6894 6895 case 'b': /* basic regular expressions */ 6896 pflags &= ~REG_EXTENDED; 6897 break; 6898 6899 case 's': /* substring match () syntax */ 6900 sub_param = ++p; 6901 pflags &= ~REG_NOSUB; 6902 break; 6903 6904 case 'd': /* delimiter */ 6905 map_p->regex_delim = ++p; 6906 break; 6907 6908 case 'a': /* map append */ 6909 map->map_app = ++p; 6910 break; 6911 6912 case 'm': /* matchonly */ 6913 map->map_mflags |= MF_MATCHONLY; 6914 break; 6915 6916 case 'S': 6917 map->map_spacesub = *++p; 6918 break; 6919 6920 case 'D': 6921 map->map_mflags |= MF_DEFER; 6922 break; 6923 6924 } 6925 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 6926 p++; 6927 if (*p != '\0') 6928 *p++ = '\0'; 6929 } 6930 if (tTd(38, 3)) 6931 dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags); 6932 6933 if ((regerr = regcomp(map_p->regex_pattern_buf, p, pflags)) != 0) 6934 { 6935 /* Errorhandling */ 6936 char errbuf[ERRBUF_SIZE]; 6937 6938 (void) regerror(regerr, map_p->regex_pattern_buf, 6939 errbuf, ERRBUF_SIZE); 6940 syserr("pattern-compile-error: %s\n", errbuf); 6941 sm_free(map_p->regex_pattern_buf); 6942 sm_free(map_p); 6943 return FALSE; 6944 } 6945 6946 if (map->map_app != NULL) 6947 map->map_app = newstr(map->map_app); 6948 if (map_p->regex_delim != NULL) 6949 map_p->regex_delim = newstr(map_p->regex_delim); 6950 else 6951 map_p->regex_delim = defdstr; 6952 6953 if (!bitset(REG_NOSUB, pflags)) 6954 { 6955 /* substring matching */ 6956 int substrings; 6957 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1)); 6958 6959 substrings = map_p->regex_pattern_buf->re_nsub + 1; 6960 6961 if (tTd(38, 3)) 6962 dprintf("regex_map_init: nr of substrings %d\n", 6963 substrings); 6964 6965 if (substrings >= MAX_MATCH) 6966 { 6967 syserr("too many substrings, %d max\n", MAX_MATCH); 6968 sm_free(map_p->regex_pattern_buf); 6969 sm_free(map_p); 6970 return FALSE; 6971 } 6972 if (sub_param != NULL && sub_param[0] != '\0') 6973 { 6974 /* optional parameter -sfields */ 6975 if (parse_fields(sub_param, fields, 6976 MAX_MATCH + 1, substrings) == -1) 6977 return FALSE; 6978 } 6979 else 6980 { 6981 /* set default fields */ 6982 int i; 6983 6984 for (i = 0; i < substrings; i++) 6985 fields[i] = i; 6986 fields[i] = END_OF_FIELDS; 6987 } 6988 map_p->regex_subfields = fields; 6989 if (tTd(38, 3)) 6990 { 6991 int *ip; 6992 6993 dprintf("regex_map_init: subfields"); 6994 for (ip = fields; *ip != END_OF_FIELDS; ip++) 6995 dprintf(" %d", *ip); 6996 dprintf("\n"); 6997 } 6998 } 6999 map->map_db1 = (ARBPTR_T)map_p; /* dirty hack */ 7000 7001 return TRUE; 7002 } 7003 7004 static char * 7005 regex_map_rewrite(map, s, slen, av) 7006 MAP *map; 7007 const char *s; 7008 size_t slen; 7009 char **av; 7010 { 7011 if (bitset(MF_MATCHONLY, map->map_mflags)) 7012 return map_rewrite(map, av[0], strlen(av[0]), NULL); 7013 else 7014 return map_rewrite(map, s, slen, av); 7015 } 7016 7017 char * 7018 regex_map_lookup(map, name, av, statp) 7019 MAP *map; 7020 char *name; 7021 char **av; 7022 int *statp; 7023 { 7024 int reg_res; 7025 struct regex_map *map_p; 7026 regmatch_t pmatch[MAX_MATCH]; 7027 7028 if (tTd(38, 20)) 7029 { 7030 char **cpp; 7031 7032 dprintf("regex_map_lookup: key '%s'\n", name); 7033 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7034 dprintf("regex_map_lookup: arg '%s'\n", *cpp); 7035 } 7036 7037 map_p = (struct regex_map *)(map->map_db1); 7038 reg_res = regexec(map_p->regex_pattern_buf, 7039 name, MAX_MATCH, pmatch, 0); 7040 7041 if (bitset(MF_REGEX_NOT, map->map_mflags)) 7042 { 7043 /* option -n */ 7044 if (reg_res == REG_NOMATCH) 7045 return regex_map_rewrite(map, "", (size_t)0, av); 7046 else 7047 return NULL; 7048 } 7049 if (reg_res == REG_NOMATCH) 7050 return NULL; 7051 7052 if (map_p->regex_subfields != NULL) 7053 { 7054 /* option -s */ 7055 static char retbuf[MAXNAME]; 7056 int fields[MAX_MATCH + 1]; 7057 bool first = TRUE; 7058 int anglecnt = 0, cmntcnt = 0, spacecnt = 0; 7059 bool quotemode = FALSE, bslashmode = FALSE; 7060 register char *dp, *sp; 7061 char *endp, *ldp; 7062 int *ip; 7063 7064 dp = retbuf; 7065 ldp = retbuf + sizeof(retbuf) - 1; 7066 7067 if (av[1] != NULL) 7068 { 7069 if (parse_fields(av[1], fields, MAX_MATCH + 1, 7070 (int) map_p->regex_pattern_buf->re_nsub + 1) == -1) 7071 { 7072 *statp = EX_CONFIG; 7073 return NULL; 7074 } 7075 ip = fields; 7076 } 7077 else 7078 ip = map_p->regex_subfields; 7079 7080 for ( ; *ip != END_OF_FIELDS; ip++) 7081 { 7082 if (!first) 7083 { 7084 for (sp = map_p->regex_delim; *sp; sp++) 7085 { 7086 if (dp < ldp) 7087 *dp++ = *sp; 7088 } 7089 } 7090 else 7091 first = FALSE; 7092 7093 7094 if (*ip >= MAX_MATCH || 7095 pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0) 7096 continue; 7097 7098 sp = name + pmatch[*ip].rm_so; 7099 endp = name + pmatch[*ip].rm_eo; 7100 for (; endp > sp; sp++) 7101 { 7102 if (dp < ldp) 7103 { 7104 if (bslashmode) 7105 { 7106 *dp++ = *sp; 7107 bslashmode = FALSE; 7108 } 7109 else if (quotemode && *sp != '"' && 7110 *sp != '\\') 7111 { 7112 *dp++ = *sp; 7113 } 7114 else switch(*dp++ = *sp) 7115 { 7116 case '\\': 7117 bslashmode = TRUE; 7118 break; 7119 7120 case '(': 7121 cmntcnt++; 7122 break; 7123 7124 case ')': 7125 cmntcnt--; 7126 break; 7127 7128 case '<': 7129 anglecnt++; 7130 break; 7131 7132 case '>': 7133 anglecnt--; 7134 break; 7135 7136 case ' ': 7137 spacecnt++; 7138 break; 7139 7140 case '"': 7141 quotemode = !quotemode; 7142 break; 7143 } 7144 } 7145 } 7146 } 7147 if (anglecnt != 0 || cmntcnt != 0 || quotemode || 7148 bslashmode || spacecnt != 0) 7149 { 7150 sm_syslog(LOG_WARNING, NOQID, 7151 "Warning: regex may cause prescan() failure map=%s lookup=%s", 7152 map->map_mname, name); 7153 return NULL; 7154 } 7155 7156 *dp = '\0'; 7157 7158 return regex_map_rewrite(map, retbuf, strlen(retbuf), av); 7159 } 7160 return regex_map_rewrite(map, "", (size_t)0, av); 7161 } 7162 #endif /* MAP_REGEX */ 7163 /* 7164 ** NSD modules 7165 */ 7166 #ifdef MAP_NSD 7167 7168 # include <ndbm.h> 7169 # define _DATUM_DEFINED 7170 # include <ns_api.h> 7171 7172 typedef struct ns_map_list 7173 { 7174 ns_map_t *map; 7175 char *mapname; 7176 struct ns_map_list *next; 7177 } ns_map_list_t; 7178 7179 static ns_map_t * 7180 ns_map_t_find(mapname) 7181 char *mapname; 7182 { 7183 static ns_map_list_t *ns_maps = NULL; 7184 ns_map_list_t *ns_map; 7185 7186 /* walk the list of maps looking for the correctly named map */ 7187 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next) 7188 { 7189 if (strcmp(ns_map->mapname, mapname) == 0) 7190 break; 7191 } 7192 7193 /* if we are looking at a NULL ns_map_list_t, then create a new one */ 7194 if (ns_map == NULL) 7195 { 7196 ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map); 7197 ns_map->mapname = newstr(mapname); 7198 ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map); 7199 ns_map->next = ns_maps; 7200 ns_maps = ns_map; 7201 } 7202 return ns_map->map; 7203 } 7204 7205 char * 7206 nsd_map_lookup(map, name, av, statp) 7207 MAP *map; 7208 char *name; 7209 char **av; 7210 int *statp; 7211 { 7212 int buflen, r; 7213 char *p; 7214 ns_map_t *ns_map; 7215 char keybuf[MAXNAME + 1]; 7216 char buf[MAXLINE]; 7217 7218 if (tTd(38, 20)) 7219 dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name); 7220 7221 buflen = strlen(name); 7222 if (buflen > sizeof keybuf - 1) 7223 buflen = sizeof keybuf - 1; 7224 memmove(keybuf, name, buflen); 7225 keybuf[buflen] = '\0'; 7226 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 7227 makelower(keybuf); 7228 7229 ns_map = ns_map_t_find(map->map_file); 7230 if (ns_map == NULL) 7231 { 7232 if (tTd(38, 20)) 7233 dprintf("nsd_map_t_find failed\n"); 7234 *statp = EX_UNAVAILABLE; 7235 return NULL; 7236 } 7237 r = ns_lookup(ns_map, NULL, map->map_file, keybuf, NULL, buf, MAXLINE); 7238 if (r == NS_UNAVAIL || r == NS_TRYAGAIN) 7239 { 7240 *statp = EX_TEMPFAIL; 7241 return NULL; 7242 } 7243 if (r == NS_BADREQ 7244 # ifdef NS_NOPERM 7245 || r == NS_NOPERM 7246 # endif /* NS_NOPERM */ 7247 ) 7248 { 7249 *statp = EX_CONFIG; 7250 return NULL; 7251 } 7252 if (r != NS_SUCCESS) 7253 { 7254 *statp = EX_NOTFOUND; 7255 return NULL; 7256 } 7257 7258 *statp = EX_OK; 7259 7260 /* Null out trailing \n */ 7261 if ((p = strchr(buf, '\n')) != NULL) 7262 *p = '\0'; 7263 7264 return map_rewrite(map, buf, strlen(buf), av); 7265 } 7266 #endif /* MAP_NSD */ 7267 7268 char * 7269 arith_map_lookup(map, name, av, statp) 7270 MAP *map; 7271 char *name; 7272 char **av; 7273 int *statp; 7274 { 7275 long r; 7276 long v[2]; 7277 bool res = FALSE; 7278 bool boolres; 7279 static char result[16]; 7280 char **cpp; 7281 7282 if (tTd(38, 2)) 7283 { 7284 dprintf("arith_map_lookup: key '%s'\n", name); 7285 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7286 dprintf("arith_map_lookup: arg '%s'\n", *cpp); 7287 } 7288 r = 0; 7289 boolres = FALSE; 7290 cpp = av; 7291 *statp = EX_OK; 7292 7293 /* 7294 ** read arguments for arith map 7295 ** - no check is made whether they are really numbers 7296 ** - just ignores args after the second 7297 */ 7298 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++) 7299 v[r++] = strtol(*cpp, NULL, 0); 7300 7301 /* operator and (at least) two operands given? */ 7302 if (name != NULL && r == 2) 7303 { 7304 switch(*name) 7305 { 7306 #if _FFR_ARITH 7307 case '|': 7308 r = v[0] | v[1]; 7309 break; 7310 7311 case '&': 7312 r = v[0] & v[1]; 7313 break; 7314 7315 case '%': 7316 if (v[1] == 0) 7317 return NULL; 7318 r = v[0] % v[1]; 7319 break; 7320 #endif /* _FFR_ARITH */ 7321 7322 case '+': 7323 r = v[0] + v[1]; 7324 break; 7325 7326 case '-': 7327 r = v[0] - v[1]; 7328 break; 7329 7330 case '*': 7331 r = v[0] * v[1]; 7332 break; 7333 7334 case '/': 7335 if (v[1] == 0) 7336 return NULL; 7337 r = v[0] / v[1]; 7338 break; 7339 7340 case 'l': 7341 res = v[0] < v[1]; 7342 boolres = TRUE; 7343 break; 7344 7345 case '=': 7346 res = v[0] == v[1]; 7347 boolres = TRUE; 7348 break; 7349 7350 default: 7351 /* XXX */ 7352 *statp = EX_CONFIG; 7353 if (LogLevel > 10) 7354 sm_syslog(LOG_WARNING, NOQID, 7355 "arith_map: unknown operator %c", 7356 isprint(*name) ? *name : '?'); 7357 return NULL; 7358 } 7359 if (boolres) 7360 snprintf(result, sizeof result, res ? "TRUE" : "FALSE"); 7361 else 7362 snprintf(result, sizeof result, "%ld", r); 7363 return result; 7364 } 7365 *statp = EX_CONFIG; 7366 return NULL; 7367 } 7368