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