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