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