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.13 2000/07/14 16:48:21 ca 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 } 572 else 573 { 574 /* don't try again */ 575 map->map_mflags &= ~MF_VALID; 576 } 577 } 578 579 if (restore) 580 { 581 Errors = saveerrors; 582 HoldErrs = savehold; 583 QuickAbort = savequick; 584 } 585 586 return bitset(MF_OPEN, map->map_mflags); 587 } 588 /* 589 ** CLOSEMAPS -- close all open maps opened by the current pid. 590 ** 591 ** Parameters: 592 ** none 593 ** 594 ** Returns: 595 ** none. 596 */ 597 598 void 599 closemaps() 600 { 601 stabapply(map_close, 0); 602 } 603 /* 604 ** MAP_CLOSE -- close a map opened by the current pid. 605 ** 606 ** Parameters: 607 ** s -- STAB entry: if map: try to open 608 ** second parameter is unused (required by stabapply()) 609 ** 610 ** Returns: 611 ** none. 612 */ 613 614 /* ARGSUSED1 */ 615 static void 616 map_close(s, unused) 617 register STAB *s; 618 int unused; 619 { 620 MAP *map; 621 622 if (s->s_type != ST_MAP) 623 return; 624 625 map = &s->s_map; 626 627 if (!bitset(MF_VALID, map->map_mflags) || 628 !bitset(MF_OPEN, map->map_mflags) || 629 bitset(MF_SHARED, 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)\n", 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 map->map_mflags |= MF_SHARED; 2845 return TRUE; 2846 } 2847 2848 /* No connection yet, connect */ 2849 if (!ldapmap_start(map)) 2850 return FALSE; 2851 2852 /* Save connection for reuse */ 2853 s->s_ldap = lmap->ldap_ld; 2854 return TRUE; 2855 } 2856 2857 /* 2858 ** LDAPMAP_START -- actually connect to an LDAP server 2859 ** 2860 ** Parameters: 2861 ** map -- the map being opened. 2862 ** 2863 ** Returns: 2864 ** TRUE if connection is successful, FALSE otherwise. 2865 ** 2866 ** Side Effects: 2867 ** Populates lmap->ldap_ld. 2868 */ 2869 2870 static jmp_buf LDAPTimeout; 2871 2872 static bool 2873 ldapmap_start(map) 2874 MAP *map; 2875 { 2876 register int bind_result; 2877 int save_errno; 2878 register EVENT *ev = NULL; 2879 LDAPMAP_STRUCT *lmap; 2880 LDAP *ld; 2881 2882 if (tTd(38, 2)) 2883 dprintf("ldapmap_start(%s)\n", map->map_mname); 2884 2885 lmap = (LDAPMAP_STRUCT *) map->map_db1; 2886 2887 if (tTd(38,9)) 2888 dprintf("ldapmap_start(%s, %d)\n", 2889 lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host, 2890 lmap->ldap_port); 2891 2892 # if USE_LDAP_INIT 2893 ld = ldap_init(lmap->ldap_host, lmap->ldap_port); 2894 # else /* USE_LDAP_INIT */ 2895 /* 2896 ** If using ldap_open(), the actual connection to the server 2897 ** happens now so we need the timeout here. For ldap_init(), 2898 ** the connection happens at bind time. 2899 */ 2900 2901 /* set the timeout */ 2902 if (lmap->ldap_timeout.tv_sec != 0) 2903 { 2904 if (setjmp(LDAPTimeout) != 0) 2905 { 2906 if (LogLevel > 1) 2907 sm_syslog(LOG_NOTICE, CurEnv->e_id, 2908 "timeout conning to LDAP server %.100s", 2909 lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host); 2910 return FALSE; 2911 } 2912 ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0); 2913 } 2914 2915 ld = ldap_open(lmap->ldap_host, lmap->ldap_port); 2916 save_errno = errno; 2917 2918 /* clear the event if it has not sprung */ 2919 if (ev != NULL) 2920 clrevent(ev); 2921 # endif /* USE_LDAP_INIT */ 2922 2923 errno = save_errno; 2924 if (ld == NULL) 2925 { 2926 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2927 { 2928 if (bitset(MF_NODEFER, map->map_mflags)) 2929 syserr("%s failed to %s in map %s", 2930 # if USE_LDAP_INIT 2931 "ldap_init", 2932 # else /* USE_LDAP_INIT */ 2933 "ldap_open", 2934 # endif /* USE_LDAP_INIT */ 2935 lmap->ldap_host == NULL ? "localhost" 2936 : lmap->ldap_host, 2937 map->map_mname); 2938 else 2939 syserr("421 4.0.0 %s failed to %s in map %s", 2940 # if USE_LDAP_INIT 2941 "ldap_init", 2942 # else /* USE_LDAP_INIT */ 2943 "ldap_open", 2944 # endif /* USE_LDAP_INIT */ 2945 lmap->ldap_host == NULL ? "localhost" 2946 : lmap->ldap_host, 2947 map->map_mname); 2948 } 2949 return FALSE; 2950 } 2951 2952 ldapmap_setopts(ld, lmap); 2953 2954 # if USE_LDAP_INIT 2955 /* 2956 ** If using ldap_init(), the actual connection to the server 2957 ** happens at ldap_bind_s() so we need the timeout here. 2958 */ 2959 2960 /* set the timeout */ 2961 if (lmap->ldap_timeout.tv_sec != 0) 2962 { 2963 if (setjmp(LDAPTimeout) != 0) 2964 { 2965 if (LogLevel > 1) 2966 sm_syslog(LOG_NOTICE, CurEnv->e_id, 2967 "timeout conning to LDAP server %.100s", 2968 lmap->ldap_host == NULL ? "localhost" 2969 : lmap->ldap_host); 2970 return FALSE; 2971 } 2972 ev = setevent(lmap->ldap_timeout.tv_sec, ldaptimeout, 0); 2973 } 2974 # endif /* USE_LDAP_INIT */ 2975 2976 # ifdef LDAP_AUTH_KRBV4 2977 if (lmap->ldap_method == LDAP_AUTH_KRBV4 && 2978 lmap->ldap_secret != NULL) 2979 { 2980 /* 2981 ** Need to put ticket in environment here instead of 2982 ** during parseargs as there may be different tickets 2983 ** for different LDAP connections. 2984 */ 2985 2986 (void) putenv(lmap->ldap_secret); 2987 } 2988 # endif /* LDAP_AUTH_KRBV4 */ 2989 2990 bind_result = ldap_bind_s(ld, lmap->ldap_binddn, 2991 lmap->ldap_secret, lmap->ldap_method); 2992 2993 # if USE_LDAP_INIT 2994 /* clear the event if it has not sprung */ 2995 if (ev != NULL) 2996 clrevent(ev); 2997 # endif /* USE_LDAP_INIT */ 2998 2999 if (bind_result != LDAP_SUCCESS) 3000 { 3001 errno = bind_result + E_LDAPBASE; 3002 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3003 { 3004 syserr("421 4.0.0 Cannot bind to map %s in ldap server %s", 3005 map->map_mname, 3006 lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host); 3007 } 3008 return FALSE; 3009 } 3010 3011 /* We need to cast ld into the map structure */ 3012 lmap->ldap_ld = ld; 3013 return TRUE; 3014 } 3015 3016 /* ARGSUSED */ 3017 static void 3018 ldaptimeout(sig_no) 3019 int sig_no; 3020 { 3021 longjmp(LDAPTimeout, 1); 3022 } 3023 3024 /* 3025 ** LDAPMAP_CLOSE -- close ldap map 3026 */ 3027 3028 void 3029 ldapmap_close(map) 3030 MAP *map; 3031 { 3032 LDAPMAP_STRUCT *lmap; 3033 STAB *s; 3034 3035 if (tTd(38, 2)) 3036 dprintf("ldapmap_close(%s)\n", map->map_mname); 3037 3038 lmap = (LDAPMAP_STRUCT *) map->map_db1; 3039 3040 /* Check if already closed */ 3041 if (lmap->ldap_ld == NULL) 3042 return; 3043 3044 s = ldapmap_findconn(lmap); 3045 3046 /* Check if already closed */ 3047 if (s->s_ldap == NULL) 3048 return; 3049 3050 /* If same as saved connection, stored connection is going away */ 3051 if (s->s_ldap == lmap->ldap_ld) 3052 s->s_ldap = NULL; 3053 3054 if (lmap->ldap_ld != NULL) 3055 { 3056 ldap_unbind(lmap->ldap_ld); 3057 lmap->ldap_ld = NULL; 3058 } 3059 } 3060 3061 # ifdef SUNET_ID 3062 /* 3063 ** SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form 3064 ** This only makes sense at Stanford University. 3065 */ 3066 3067 char * 3068 sunet_id_hash(str) 3069 char *str; 3070 { 3071 char *p, *p_last; 3072 3073 p = str; 3074 p_last = p; 3075 while (*p != '\0') 3076 { 3077 if (islower(*p) || isdigit(*p)) 3078 { 3079 *p_last = *p; 3080 p_last++; 3081 } 3082 else if (isupper(*p)) 3083 { 3084 *p_last = tolower(*p); 3085 p_last++; 3086 } 3087 ++p; 3088 } 3089 if (*p_last != '\0') 3090 *p_last = '\0'; 3091 return str; 3092 } 3093 # endif /* SUNET_ID */ 3094 3095 /* 3096 ** LDAPMAP_LOOKUP -- look up a datum in a LDAP map 3097 */ 3098 3099 char * 3100 ldapmap_lookup(map, name, av, statp) 3101 MAP *map; 3102 char *name; 3103 char **av; 3104 int *statp; 3105 { 3106 int i; 3107 int entries = 0; 3108 int msgid; 3109 int ret; 3110 int vsize; 3111 char *fp, *vp; 3112 char *p, *q; 3113 char *result = NULL; 3114 LDAPMAP_STRUCT *lmap = NULL; 3115 char keybuf[MAXNAME + 1]; 3116 char filter[LDAPMAP_MAX_FILTER + 1]; 3117 3118 if (tTd(38, 20)) 3119 dprintf("ldapmap_lookup(%s, %s)\n", map->map_mname, name); 3120 3121 /* Get ldap struct pointer from map */ 3122 lmap = (LDAPMAP_STRUCT *) map->map_db1; 3123 ldapmap_setopts(lmap->ldap_ld, lmap); 3124 3125 (void) strlcpy(keybuf, name, sizeof keybuf); 3126 3127 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3128 { 3129 # ifdef SUNET_ID 3130 sunet_id_hash(keybuf); 3131 # else /* SUNET_ID */ 3132 makelower(keybuf); 3133 # endif /* SUNET_ID */ 3134 } 3135 3136 /* substitute keybuf into filter, perhaps multiple times */ 3137 memset(filter, '\0', sizeof filter); 3138 fp = filter; 3139 p = lmap->ldap_filter; 3140 while ((q = strchr(p, '%')) != NULL) 3141 { 3142 if (q[1] == 's') 3143 { 3144 snprintf(fp, SPACELEFT(filter, fp), "%.*s%s", 3145 q - p, p, keybuf); 3146 fp += strlen(fp); 3147 p = q + 2; 3148 } 3149 else if (q[1] == '0') 3150 { 3151 char *k = keybuf; 3152 3153 snprintf(fp, SPACELEFT(filter, fp), "%.*s", 3154 q - p, p); 3155 fp += strlen(fp); 3156 p = q + 2; 3157 3158 /* Properly escape LDAP special characters */ 3159 while (SPACELEFT(filter, fp) > 0 && 3160 *k != '\0') 3161 { 3162 if (*k == '*' || *k == '(' || 3163 *k == ')' || *k == '\\') 3164 { 3165 (void) strlcat(fp, 3166 (*k == '*' ? "\\2A" : 3167 (*k == '(' ? "\\28" : 3168 (*k == ')' ? "\\29" : 3169 (*k == '\\' ? "\\5C" : 3170 "\00")))), 3171 SPACELEFT(filter, fp)); 3172 fp += strlen(fp); 3173 k++; 3174 } 3175 else 3176 *fp++ = *k++; 3177 } 3178 } 3179 else 3180 { 3181 snprintf(fp, SPACELEFT(filter, fp), "%.*s", 3182 q - p + 1, p); 3183 p = q + (q[1] == '%' ? 2 : 1); 3184 fp += strlen(fp); 3185 } 3186 } 3187 snprintf(fp, SPACELEFT(filter, fp), "%s", p); 3188 if (tTd(38, 20)) 3189 dprintf("ldap search filter=%s\n", filter); 3190 3191 lmap->ldap_res = NULL; 3192 msgid = ldap_search(lmap->ldap_ld, lmap->ldap_base, lmap->ldap_scope, 3193 filter, 3194 (lmap->ldap_attr[0] == NULL ? NULL : 3195 lmap->ldap_attr), 3196 lmap->ldap_attrsonly); 3197 if (msgid == -1) 3198 { 3199 errno = ldapmap_geterrno(lmap->ldap_ld) + E_LDAPBASE; 3200 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3201 { 3202 if (bitset(MF_NODEFER, map->map_mflags)) 3203 syserr("Error in ldap_search_st using %s in map %s", 3204 filter, map->map_mname); 3205 else 3206 syserr("421 4.0.0 Error in ldap_search_st using %s in map %s", 3207 filter, map->map_mname); 3208 } 3209 *statp = EX_TEMPFAIL; 3210 return NULL; 3211 } 3212 3213 *statp = EX_NOTFOUND; 3214 vp = NULL; 3215 3216 /* Get results (all if MF_NOREWRITE, otherwise one by one) */ 3217 while ((ret = ldap_result(lmap->ldap_ld, msgid, 3218 bitset(MF_NOREWRITE, map->map_mflags), 3219 (lmap->ldap_timeout.tv_sec == 0 ? NULL : 3220 &(lmap->ldap_timeout)), 3221 &(lmap->ldap_res))) == LDAP_RES_SEARCH_ENTRY) 3222 { 3223 LDAPMessage *entry; 3224 3225 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 3226 { 3227 entries += ldap_count_entries(lmap->ldap_ld, 3228 lmap->ldap_res); 3229 if (entries > 1) 3230 { 3231 *statp = EX_NOTFOUND; 3232 if (lmap->ldap_res != NULL) 3233 { 3234 ldap_msgfree(lmap->ldap_res); 3235 lmap->ldap_res = NULL; 3236 } 3237 (void) ldap_abandon(lmap->ldap_ld, msgid); 3238 if (vp != NULL) 3239 free(vp); 3240 if (tTd(38, 25)) 3241 dprintf("ldap search found multiple on a single match query\n"); 3242 return NULL; 3243 } 3244 } 3245 3246 /* If we don't want multiple values and we have one, break */ 3247 if (map->map_coldelim == '\0' && vp != NULL) 3248 break; 3249 3250 /* Cycle through all entries */ 3251 for (entry = ldap_first_entry(lmap->ldap_ld, lmap->ldap_res); 3252 entry != NULL; 3253 entry = ldap_next_entry(lmap->ldap_ld, lmap->ldap_res)) 3254 { 3255 BerElement *ber; 3256 char *attr; 3257 char **vals = NULL; 3258 3259 /* 3260 ** If matching only and found an entry, 3261 ** no need to spin through attributes 3262 */ 3263 3264 if (*statp == EX_OK && 3265 bitset(MF_MATCHONLY, map->map_mflags)) 3266 continue; 3267 3268 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 3269 /* 3270 ** Reset value to prevent lingering 3271 ** LDAP_DECODING_ERROR due to 3272 ** OpenLDAP 1.X's hack (see below) 3273 */ 3274 3275 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 3276 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 3277 3278 for (attr = ldap_first_attribute(lmap->ldap_ld, entry, 3279 &ber); 3280 attr != NULL; 3281 attr = ldap_next_attribute(lmap->ldap_ld, entry, 3282 ber)) 3283 { 3284 char *tmp, *vp_tmp; 3285 3286 if (lmap->ldap_attrsonly == LDAPMAP_FALSE) 3287 { 3288 vals = ldap_get_values(lmap->ldap_ld, 3289 entry, 3290 attr); 3291 if (vals == NULL) 3292 { 3293 errno = ldapmap_geterrno(lmap->ldap_ld); 3294 if (errno == LDAP_SUCCESS) 3295 continue; 3296 3297 /* Must be an error */ 3298 errno += E_LDAPBASE; 3299 if (!bitset(MF_OPTIONAL, 3300 map->map_mflags)) 3301 { 3302 if (bitset(MF_NODEFER, 3303 map->map_mflags)) 3304 syserr("Error getting LDAP values in map %s", 3305 map->map_mname); 3306 else 3307 syserr("421 4.0.0 Error getting LDAP values in map %s", 3308 map->map_mname); 3309 } 3310 *statp = EX_TEMPFAIL; 3311 # if USING_NETSCAPE_LDAP 3312 ldap_mem_free(attr); 3313 # endif /* USING_NETSCAPE_LDAP */ 3314 if (lmap->ldap_res != NULL) 3315 { 3316 ldap_msgfree(lmap->ldap_res); 3317 lmap->ldap_res = NULL; 3318 } 3319 (void) ldap_abandon(lmap->ldap_ld, 3320 msgid); 3321 if (vp != NULL) 3322 free(vp); 3323 return NULL; 3324 } 3325 } 3326 3327 *statp = EX_OK; 3328 3329 # if !defined(LDAP_VERSION_MAX) && !defined(LDAP_OPT_SIZELIMIT) 3330 /* 3331 ** Reset value to prevent lingering 3332 ** LDAP_DECODING_ERROR due to 3333 ** OpenLDAP 1.X's hack (see below) 3334 */ 3335 3336 lmap->ldap_ld->ld_errno = LDAP_SUCCESS; 3337 # endif /* !defined(LDAP_VERSION_MAX) !defined(LDAP_OPT_SIZELIMIT) */ 3338 3339 /* 3340 ** If matching only, 3341 ** no need to spin through entries 3342 */ 3343 3344 if (bitset(MF_MATCHONLY, map->map_mflags)) 3345 continue; 3346 3347 /* 3348 ** If we don't want multiple values, 3349 ** return first found. 3350 */ 3351 3352 if (map->map_coldelim == '\0') 3353 { 3354 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 3355 { 3356 vp = newstr(attr); 3357 # if USING_NETSCAPE_LDAP 3358 ldap_mem_free(attr); 3359 # endif /* USING_NETSCAPE_LDAP */ 3360 break; 3361 } 3362 3363 if (vals[0] == NULL) 3364 { 3365 ldap_value_free(vals); 3366 # if USING_NETSCAPE_LDAP 3367 ldap_mem_free(attr); 3368 # endif /* USING_NETSCAPE_LDAP */ 3369 continue; 3370 } 3371 3372 vp = newstr(vals[0]); 3373 ldap_value_free(vals); 3374 # if USING_NETSCAPE_LDAP 3375 ldap_mem_free(attr); 3376 # endif /* USING_NETSCAPE_LDAP */ 3377 break; 3378 } 3379 3380 /* attributes only */ 3381 if (lmap->ldap_attrsonly == LDAPMAP_TRUE) 3382 { 3383 if (vp == NULL) 3384 vp = newstr(attr); 3385 else 3386 { 3387 vsize = strlen(vp) + 3388 strlen(attr) + 2; 3389 tmp = xalloc(vsize); 3390 snprintf(tmp, vsize, "%s%c%s", 3391 vp, map->map_coldelim, 3392 attr); 3393 free(vp); 3394 vp = tmp; 3395 } 3396 # if USING_NETSCAPE_LDAP 3397 ldap_mem_free(attr); 3398 # endif /* USING_NETSCAPE_LDAP */ 3399 continue; 3400 } 3401 3402 /* 3403 ** If there is more than one, 3404 ** munge then into a map_coldelim 3405 ** separated string 3406 */ 3407 3408 vsize = 0; 3409 for (i = 0; vals[i] != NULL; i++) 3410 vsize += strlen(vals[i]) + 1; 3411 vp_tmp = xalloc(vsize); 3412 *vp_tmp = '\0'; 3413 3414 p = vp_tmp; 3415 for (i = 0; vals[i] != NULL; i++) 3416 { 3417 p += strlcpy(p, vals[i], 3418 vsize - (p - vp_tmp)); 3419 if (p >= vp_tmp + vsize) 3420 syserr("ldapmap_lookup: Internal error: buffer too small for LDAP values"); 3421 if (vals[i + 1] != NULL) 3422 *p++ = map->map_coldelim; 3423 } 3424 3425 ldap_value_free(vals); 3426 # if USING_NETSCAPE_LDAP 3427 ldap_mem_free(attr); 3428 # endif /* USING_NETSCAPE_LDAP */ 3429 if (vp == NULL) 3430 { 3431 vp = vp_tmp; 3432 continue; 3433 } 3434 vsize = strlen(vp) + strlen(vp_tmp) + 2; 3435 tmp = xalloc(vsize); 3436 snprintf(tmp, vsize, "%s%c%s", 3437 vp, map->map_coldelim, vp_tmp); 3438 3439 free(vp); 3440 free(vp_tmp); 3441 vp = tmp; 3442 } 3443 errno = ldapmap_geterrno(lmap->ldap_ld); 3444 3445 /* 3446 ** We check errno != LDAP_DECODING_ERROR since 3447 ** OpenLDAP 1.X has a very ugly *undocumented* 3448 ** hack of returning this error code from 3449 ** ldap_next_attribute() if the library freed the 3450 ** ber attribute. See: 3451 ** http://www.openldap.org/lists/openldap-devel/9901/msg00064.html 3452 */ 3453 3454 if (errno != LDAP_SUCCESS && 3455 errno != LDAP_DECODING_ERROR) 3456 { 3457 /* Must be an error */ 3458 errno += E_LDAPBASE; 3459 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3460 { 3461 if (bitset(MF_NODEFER, map->map_mflags)) 3462 syserr("Error getting LDAP attributes in map %s", 3463 map->map_mname); 3464 else 3465 syserr("421 4.0.0 Error getting LDAP attributes in map %s", 3466 map->map_mname); 3467 } 3468 *statp = EX_TEMPFAIL; 3469 if (lmap->ldap_res != NULL) 3470 { 3471 ldap_msgfree(lmap->ldap_res); 3472 lmap->ldap_res = NULL; 3473 } 3474 (void) ldap_abandon(lmap->ldap_ld, msgid); 3475 if (vp != NULL) 3476 free(vp); 3477 return NULL; 3478 } 3479 3480 /* We don't want multiple values and we have one */ 3481 if (map->map_coldelim == '\0' && vp != NULL) 3482 break; 3483 } 3484 errno = ldapmap_geterrno(lmap->ldap_ld); 3485 if (errno != LDAP_SUCCESS && errno != LDAP_DECODING_ERROR) 3486 { 3487 /* Must be an error */ 3488 errno += E_LDAPBASE; 3489 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3490 { 3491 if (bitset(MF_NODEFER, map->map_mflags)) 3492 syserr("Error getting LDAP entries in map %s", 3493 map->map_mname); 3494 else 3495 syserr("421 4.0.0 Error getting LDAP entries in map %s", 3496 map->map_mname); 3497 } 3498 *statp = EX_TEMPFAIL; 3499 if (lmap->ldap_res != NULL) 3500 { 3501 ldap_msgfree(lmap->ldap_res); 3502 lmap->ldap_res = NULL; 3503 } 3504 (void) ldap_abandon(lmap->ldap_ld, msgid); 3505 if (vp != NULL) 3506 free(vp); 3507 return NULL; 3508 } 3509 ldap_msgfree(lmap->ldap_res); 3510 lmap->ldap_res = NULL; 3511 } 3512 3513 /* 3514 ** If grabbing all results at once for MF_NOREWRITE and 3515 ** only want a single match, make sure that's all we have 3516 */ 3517 3518 if (ret == LDAP_RES_SEARCH_RESULT && 3519 bitset(MF_NOREWRITE|MF_SINGLEMATCH, map->map_mflags)) 3520 { 3521 entries += ldap_count_entries(lmap->ldap_ld, lmap->ldap_res); 3522 if (entries > 1) 3523 { 3524 *statp = EX_NOTFOUND; 3525 if (lmap->ldap_res != NULL) 3526 { 3527 ldap_msgfree(lmap->ldap_res); 3528 lmap->ldap_res = NULL; 3529 } 3530 if (vp != NULL) 3531 free(vp); 3532 return NULL; 3533 } 3534 *statp = EX_OK; 3535 } 3536 3537 if (ret == 0) 3538 errno = ETIMEDOUT; 3539 else 3540 errno = ldapmap_geterrno(lmap->ldap_ld); 3541 if (errno != LDAP_SUCCESS) 3542 { 3543 /* Must be an error */ 3544 if (ret != 0) 3545 errno += E_LDAPBASE; 3546 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3547 { 3548 if (bitset(MF_NODEFER, map->map_mflags)) 3549 syserr("Error getting LDAP results in map %s", 3550 map->map_mname); 3551 else 3552 syserr("421 4.0.0 Error getting LDAP results in map %s", 3553 map->map_mname); 3554 } 3555 *statp = EX_TEMPFAIL; 3556 if (vp != NULL) 3557 free(vp); 3558 return NULL; 3559 } 3560 3561 /* Did we match anything? */ 3562 if (vp == NULL) 3563 return NULL; 3564 3565 /* 3566 ** If MF_NOREWRITE, we are special map which doesn't 3567 ** actually return a map value. Instead, we don't free 3568 ** ldap_res and let the calling function process the LDAP 3569 ** results. The caller should ldap_msgfree(lmap->ldap_res). 3570 */ 3571 3572 if (bitset(MF_NOREWRITE, map->map_mflags)) 3573 { 3574 /* vp != NULL due to test above */ 3575 free(vp); 3576 return ""; 3577 } 3578 3579 if (*statp == EX_OK) 3580 { 3581 /* vp != NULL due to test above */ 3582 if (LogLevel > 9) 3583 sm_syslog(LOG_INFO, CurEnv->e_id, 3584 "ldap %.100s => %s", name, vp); 3585 if (bitset(MF_MATCHONLY, map->map_mflags)) 3586 result = map_rewrite(map, name, strlen(name), NULL); 3587 else 3588 result = map_rewrite(map, vp, strlen(vp), av); 3589 free(vp); 3590 } 3591 return result; 3592 } 3593 3594 /* 3595 ** LDAPMAP_FINDCONN -- find an LDAP connection to the server 3596 ** 3597 ** Cache LDAP connections based on the host, port, bind DN, 3598 ** and secret so we don't have multiple connections open to 3599 ** the same server for different maps. 3600 ** 3601 ** Parameters: 3602 ** lmap -- LDAP map information 3603 ** 3604 ** Returns: 3605 ** Symbol table entry for the LDAP connection. 3606 ** 3607 */ 3608 3609 static STAB * 3610 ldapmap_findconn(lmap) 3611 LDAPMAP_STRUCT *lmap; 3612 { 3613 int len; 3614 char *nbuf; 3615 STAB *s; 3616 3617 len = (lmap->ldap_host == NULL ? strlen("localhost") : 3618 strlen(lmap->ldap_host)) + 1 + 8 + 1 + 3619 (lmap->ldap_binddn == NULL ? 0 : strlen(lmap->ldap_binddn)) + 3620 1 + 3621 (lmap->ldap_secret == NULL ? 0 : strlen(lmap->ldap_secret)) + 3622 1; 3623 nbuf = xalloc(len); 3624 snprintf(nbuf, len, "%s%c%d%c%s%c%s", 3625 (lmap->ldap_host == NULL ? "localhost" : lmap->ldap_host), 3626 CONDELSE, 3627 lmap->ldap_port, 3628 CONDELSE, 3629 (lmap->ldap_binddn == NULL ? "" : lmap->ldap_binddn), 3630 CONDELSE, 3631 (lmap->ldap_secret == NULL ? "" : lmap->ldap_secret)); 3632 s = stab(nbuf, ST_LDAP, ST_ENTER); 3633 free(nbuf); 3634 return s; 3635 } 3636 /* 3637 ** LDAPMAP_SETOPTS -- set LDAP options 3638 ** 3639 ** Parameters: 3640 ** ld -- LDAP session handle 3641 ** lmap -- LDAP map information 3642 ** 3643 ** Returns: 3644 ** None. 3645 ** 3646 */ 3647 3648 static void 3649 ldapmap_setopts(ld, lmap) 3650 LDAP *ld; 3651 LDAPMAP_STRUCT *lmap; 3652 { 3653 # if USE_LDAP_SET_OPTION 3654 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->ldap_deref); 3655 if (bitset(LDAP_OPT_REFERRALS, lmap->ldap_options)) 3656 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_ON); 3657 else 3658 ldap_set_option(ld, LDAP_OPT_REFERRALS, LDAP_OPT_OFF); 3659 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->ldap_sizelimit); 3660 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->ldap_timelimit); 3661 # else /* USE_LDAP_SET_OPTION */ 3662 /* From here on in we can use ldap internal timelimits */ 3663 ld->ld_deref = lmap->ldap_deref; 3664 ld->ld_options = lmap->ldap_options; 3665 ld->ld_sizelimit = lmap->ldap_sizelimit; 3666 ld->ld_timelimit = lmap->ldap_timelimit; 3667 # endif /* USE_LDAP_SET_OPTION */ 3668 } 3669 /* 3670 ** LDAPMAP_GETERRNO -- get ldap errno value 3671 ** 3672 ** Parameters: 3673 ** ld -- LDAP session handle 3674 ** 3675 ** Returns: 3676 ** LDAP errno. 3677 ** 3678 */ 3679 3680 static int 3681 ldapmap_geterrno(ld) 3682 LDAP *ld; 3683 { 3684 int err = LDAP_SUCCESS; 3685 3686 # if defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 3687 (void) ldap_get_option(ld, LDAP_OPT_ERROR_NUMBER, &err); 3688 # else /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 3689 # ifdef LDAP_OPT_SIZELIMIT 3690 err = ldap_get_lderrno(ld, NULL, NULL); 3691 # else /* LDAP_OPT_SIZELIMIT */ 3692 err = ld->ld_errno; 3693 3694 /* 3695 ** Reset value to prevent lingering LDAP_DECODING_ERROR due to 3696 ** OpenLDAP 1.X's hack (see above) 3697 */ 3698 3699 ld->ld_errno = LDAP_SUCCESS; 3700 # endif /* LDAP_OPT_SIZELIMIT */ 3701 # endif /* defined(LDAP_VERSION_MAX) && LDAP_VERSION_MAX >= 3 */ 3702 return err; 3703 } 3704 3705 /* 3706 ** LDAPX_MAP_PARSEARGS -- print warning about use of ldapx map. 3707 */ 3708 3709 bool 3710 ldapx_map_parseargs(map, args) 3711 MAP *map; 3712 char *args; 3713 { 3714 printf("Warning: The \"ldapx\" map class is deprecated and will be removed in a future\n"); 3715 printf(" version. Use the \"ldap\" map class instead for map \"%s\".\n", 3716 map->map_mname); 3717 return ldapmap_parseargs(map, args); 3718 } 3719 3720 /* 3721 ** LDAPMAP_PARSEARGS -- parse ldap map definition args. 3722 */ 3723 3724 struct lamvalues LDAPAuthMethods[] = 3725 { 3726 { "none", LDAP_AUTH_NONE }, 3727 { "simple", LDAP_AUTH_SIMPLE }, 3728 # ifdef LDAP_AUTH_KRBV4 3729 { "krbv4", LDAP_AUTH_KRBV4 }, 3730 # endif /* LDAP_AUTH_KRBV4 */ 3731 { NULL, 0 } 3732 }; 3733 3734 struct ladvalues LDAPAliasDereference[] = 3735 { 3736 { "never", LDAP_DEREF_NEVER }, 3737 { "always", LDAP_DEREF_ALWAYS }, 3738 { "search", LDAP_DEREF_SEARCHING }, 3739 { "find", LDAP_DEREF_FINDING }, 3740 { NULL, 0 } 3741 }; 3742 3743 struct lssvalues LDAPSearchScope[] = 3744 { 3745 { "base", LDAP_SCOPE_BASE }, 3746 { "one", LDAP_SCOPE_ONELEVEL }, 3747 { "sub", LDAP_SCOPE_SUBTREE }, 3748 { NULL, 0 } 3749 }; 3750 3751 bool 3752 ldapmap_parseargs(map, args) 3753 MAP *map; 3754 char *args; 3755 { 3756 bool secretread = TRUE; 3757 int i; 3758 register char *p = args; 3759 LDAPMAP_STRUCT *lmap; 3760 struct lamvalues *lam; 3761 struct ladvalues *lad; 3762 struct lssvalues *lss; 3763 char m_tmp[MAXPATHLEN + LDAPMAP_MAX_PASSWD]; 3764 3765 /* Get ldap struct pointer from map */ 3766 lmap = (LDAPMAP_STRUCT *) map->map_db1; 3767 3768 /* Check if setting the initial LDAP defaults */ 3769 if (lmap == NULL || lmap != LDAPDefaults) 3770 { 3771 /* We need to alloc an LDAPMAP_STRUCT struct */ 3772 lmap = (LDAPMAP_STRUCT *) xalloc(sizeof *lmap); 3773 if (LDAPDefaults == NULL) 3774 ldapmap_clear(lmap); 3775 else 3776 STRUCTCOPY(*LDAPDefaults, *lmap); 3777 } 3778 3779 /* there is no check whether there is really an argument */ 3780 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 3781 map->map_spacesub = SpaceSub; /* default value */ 3782 for (;;) 3783 { 3784 while (isascii(*p) && isspace(*p)) 3785 p++; 3786 if (*p != '-') 3787 break; 3788 switch (*++p) 3789 { 3790 case 'N': 3791 map->map_mflags |= MF_INCLNULL; 3792 map->map_mflags &= ~MF_TRY0NULL; 3793 break; 3794 3795 case 'O': 3796 map->map_mflags &= ~MF_TRY1NULL; 3797 break; 3798 3799 case 'o': 3800 map->map_mflags |= MF_OPTIONAL; 3801 break; 3802 3803 case 'f': 3804 map->map_mflags |= MF_NOFOLDCASE; 3805 break; 3806 3807 case 'm': 3808 map->map_mflags |= MF_MATCHONLY; 3809 break; 3810 3811 case 'A': 3812 map->map_mflags |= MF_APPEND; 3813 break; 3814 3815 case 'q': 3816 map->map_mflags |= MF_KEEPQUOTES; 3817 break; 3818 3819 case 'a': 3820 map->map_app = ++p; 3821 break; 3822 3823 case 'T': 3824 map->map_tapp = ++p; 3825 break; 3826 3827 case 't': 3828 map->map_mflags |= MF_NODEFER; 3829 break; 3830 3831 case 'S': 3832 map->map_spacesub = *++p; 3833 break; 3834 3835 case 'D': 3836 map->map_mflags |= MF_DEFER; 3837 break; 3838 3839 case 'z': 3840 if (*++p != '\\') 3841 map->map_coldelim = *p; 3842 else 3843 { 3844 switch (*++p) 3845 { 3846 case 'n': 3847 map->map_coldelim = '\n'; 3848 break; 3849 3850 case 't': 3851 map->map_coldelim = '\t'; 3852 break; 3853 3854 default: 3855 map->map_coldelim = '\\'; 3856 } 3857 } 3858 break; 3859 3860 /* Start of ldapmap specific args */ 3861 case 'k': /* search field */ 3862 while (isascii(*++p) && isspace(*p)) 3863 continue; 3864 lmap->ldap_filter = p; 3865 break; 3866 3867 case 'v': /* attr to return */ 3868 while (isascii(*++p) && isspace(*p)) 3869 continue; 3870 lmap->ldap_attr[0] = p; 3871 lmap->ldap_attr[1] = NULL; 3872 break; 3873 3874 case '1': 3875 map->map_mflags |= MF_SINGLEMATCH; 3876 break; 3877 3878 /* args stolen from ldapsearch.c */ 3879 case 'R': /* don't auto chase referrals */ 3880 # ifdef LDAP_REFERRALS 3881 lmap->ldap_options &= ~LDAP_OPT_REFERRALS; 3882 # else /* LDAP_REFERRALS */ 3883 syserr("compile with -DLDAP_REFERRALS for referral support\n"); 3884 # endif /* LDAP_REFERRALS */ 3885 break; 3886 3887 case 'n': /* retrieve attribute names only */ 3888 lmap->ldap_attrsonly = LDAPMAP_TRUE; 3889 break; 3890 3891 case 'r': /* alias dereferencing */ 3892 while (isascii(*++p) && isspace(*p)) 3893 continue; 3894 3895 if (strncasecmp(p, "LDAP_DEREF_", 11) == 0) 3896 p += 11; 3897 3898 for (lad = LDAPAliasDereference; 3899 lad != NULL && lad->lad_name != NULL; lad++) 3900 { 3901 if (strncasecmp(p, lad->lad_name, 3902 strlen(lad->lad_name)) == 0) 3903 break; 3904 } 3905 if (lad->lad_name != NULL) 3906 lmap->ldap_deref = lad->lad_code; 3907 else 3908 { 3909 /* bad config line */ 3910 if (!bitset(MCF_OPTFILE, 3911 map->map_class->map_cflags)) 3912 { 3913 char *ptr; 3914 3915 if ((ptr = strchr(p, ' ')) != NULL) 3916 *ptr = '\0'; 3917 syserr("Deref must be [never|always|search|find] not %s in map %s", 3918 p, map->map_mname); 3919 if (ptr != NULL) 3920 *ptr = ' '; 3921 return FALSE; 3922 } 3923 } 3924 break; 3925 3926 case 's': /* search scope */ 3927 while (isascii(*++p) && isspace(*p)) 3928 continue; 3929 3930 if (strncasecmp(p, "LDAP_SCOPE_", 11) == 0) 3931 p += 11; 3932 3933 for (lss = LDAPSearchScope; 3934 lss != NULL && lss->lss_name != NULL; lss++) 3935 { 3936 if (strncasecmp(p, lss->lss_name, 3937 strlen(lss->lss_name)) == 0) 3938 break; 3939 } 3940 if (lss->lss_name != NULL) 3941 lmap->ldap_scope = lss->lss_code; 3942 else 3943 { 3944 /* bad config line */ 3945 if (!bitset(MCF_OPTFILE, 3946 map->map_class->map_cflags)) 3947 { 3948 char *ptr; 3949 3950 if ((ptr = strchr(p, ' ')) != NULL) 3951 *ptr = '\0'; 3952 syserr("Scope must be [base|one|sub] not %s in map %s", 3953 p, map->map_mname); 3954 if (ptr != NULL) 3955 *ptr = ' '; 3956 return FALSE; 3957 } 3958 } 3959 break; 3960 3961 case 'h': /* ldap host */ 3962 while (isascii(*++p) && isspace(*p)) 3963 continue; 3964 lmap->ldap_host = p; 3965 break; 3966 3967 case 'b': /* search base */ 3968 while (isascii(*++p) && isspace(*p)) 3969 continue; 3970 lmap->ldap_base = p; 3971 break; 3972 3973 case 'p': /* ldap port */ 3974 while (isascii(*++p) && isspace(*p)) 3975 continue; 3976 lmap->ldap_port = atoi(p); 3977 break; 3978 3979 case 'l': /* time limit */ 3980 while (isascii(*++p) && isspace(*p)) 3981 continue; 3982 lmap->ldap_timelimit = atoi(p); 3983 lmap->ldap_timeout.tv_sec = lmap->ldap_timelimit; 3984 break; 3985 3986 case 'Z': 3987 while (isascii(*++p) && isspace(*p)) 3988 continue; 3989 lmap->ldap_sizelimit = atoi(p); 3990 break; 3991 3992 case 'd': /* Dn to bind to server as */ 3993 while (isascii(*++p) && isspace(*p)) 3994 continue; 3995 lmap->ldap_binddn = p; 3996 break; 3997 3998 case 'M': /* Method for binding */ 3999 while (isascii(*++p) && isspace(*p)) 4000 continue; 4001 4002 if (strncasecmp(p, "LDAP_AUTH_", 10) == 0) 4003 p += 10; 4004 4005 for (lam = LDAPAuthMethods; 4006 lam != NULL && lam->lam_name != NULL; lam++) 4007 { 4008 if (strncasecmp(p, lam->lam_name, 4009 strlen(lam->lam_name)) == 0) 4010 break; 4011 } 4012 if (lam->lam_name != NULL) 4013 lmap->ldap_method = lam->lam_code; 4014 else 4015 { 4016 /* bad config line */ 4017 if (!bitset(MCF_OPTFILE, 4018 map->map_class->map_cflags)) 4019 { 4020 char *ptr; 4021 4022 if ((ptr = strchr(p, ' ')) != NULL) 4023 *ptr = '\0'; 4024 syserr("Method for binding must be [none|simple|krbv4] not %s in map %s", 4025 p, map->map_mname); 4026 if (ptr != NULL) 4027 *ptr = ' '; 4028 return FALSE; 4029 } 4030 } 4031 4032 break; 4033 4034 /* 4035 ** This is a string that is dependent on the 4036 ** method used defined above. 4037 */ 4038 4039 case 'P': /* Secret password for binding */ 4040 while (isascii(*++p) && isspace(*p)) 4041 continue; 4042 lmap->ldap_secret = p; 4043 secretread = FALSE; 4044 break; 4045 4046 default: 4047 syserr("Illegal option %c map %s", *p, map->map_mname); 4048 break; 4049 } 4050 4051 /* need to account for quoted strings here */ 4052 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 4053 { 4054 if (*p == '"') 4055 { 4056 while (*++p != '"' && *p != '\0') 4057 continue; 4058 if (*p != '\0') 4059 p++; 4060 } 4061 else 4062 p++; 4063 } 4064 4065 if (*p != '\0') 4066 *p++ = '\0'; 4067 } 4068 4069 if (map->map_app != NULL) 4070 map->map_app = newstr(ldapmap_dequote(map->map_app)); 4071 if (map->map_tapp != NULL) 4072 map->map_tapp = newstr(ldapmap_dequote(map->map_tapp)); 4073 4074 /* 4075 ** We need to swallow up all the stuff into a struct 4076 ** and dump it into map->map_dbptr1 4077 */ 4078 4079 if (lmap->ldap_host != NULL && 4080 (LDAPDefaults == NULL || 4081 LDAPDefaults == lmap || 4082 LDAPDefaults->ldap_host != lmap->ldap_host)) 4083 lmap->ldap_host = newstr(ldapmap_dequote(lmap->ldap_host)); 4084 map->map_domain = lmap->ldap_host; 4085 4086 if (lmap->ldap_binddn != NULL && 4087 (LDAPDefaults == NULL || 4088 LDAPDefaults == lmap || 4089 LDAPDefaults->ldap_binddn != lmap->ldap_binddn)) 4090 lmap->ldap_binddn = newstr(ldapmap_dequote(lmap->ldap_binddn)); 4091 4092 if (lmap->ldap_secret != NULL && 4093 (LDAPDefaults == NULL || 4094 LDAPDefaults == lmap || 4095 LDAPDefaults->ldap_secret != lmap->ldap_secret)) 4096 { 4097 FILE *sfd; 4098 long sff = SFF_OPENASROOT|SFF_ROOTOK|SFF_NOWLINK|SFF_NOWWFILES|SFF_NOGWFILES; 4099 4100 if (DontLockReadFiles) 4101 sff |= SFF_NOLOCK; 4102 4103 /* need to use method to map secret to passwd string */ 4104 switch (lmap->ldap_method) 4105 { 4106 case LDAP_AUTH_NONE: 4107 /* Do nothing */ 4108 break; 4109 4110 case LDAP_AUTH_SIMPLE: 4111 4112 /* 4113 ** Secret is the name of a file with 4114 ** the first line as the password. 4115 */ 4116 4117 /* Already read in the secret? */ 4118 if (secretread) 4119 break; 4120 4121 sfd = safefopen(ldapmap_dequote(lmap->ldap_secret), 4122 O_RDONLY, 0, sff); 4123 if (sfd == NULL) 4124 { 4125 syserr("LDAP map: cannot open secret %s", 4126 ldapmap_dequote(lmap->ldap_secret)); 4127 return FALSE; 4128 } 4129 lmap->ldap_secret = sfgets(m_tmp, LDAPMAP_MAX_PASSWD, 4130 sfd, 0, "ldapmap_parseargs"); 4131 (void) fclose(sfd); 4132 if (lmap->ldap_secret != NULL && 4133 strlen(m_tmp) > 0) 4134 { 4135 /* chomp newline */ 4136 if (m_tmp[strlen(m_tmp) - 1] == '\n') 4137 m_tmp[strlen(m_tmp) - 1] = '\0'; 4138 4139 lmap->ldap_secret = m_tmp; 4140 } 4141 break; 4142 4143 # ifdef LDAP_AUTH_KRBV4 4144 case LDAP_AUTH_KRBV4: 4145 4146 /* 4147 ** Secret is where the ticket file is 4148 ** stashed 4149 */ 4150 4151 snprintf(m_tmp, MAXPATHLEN + LDAPMAP_MAX_PASSWD, 4152 "KRBTKFILE=%s", 4153 ldapmap_dequote(lmap->ldap_secret)); 4154 lmap->ldap_secret = m_tmp; 4155 break; 4156 # endif /* LDAP_AUTH_KRBV4 */ 4157 4158 default: /* Should NEVER get here */ 4159 syserr("LDAP map: Illegal value in lmap method"); 4160 return FALSE; 4161 break; 4162 } 4163 } 4164 4165 if (lmap->ldap_secret != NULL && 4166 (LDAPDefaults == NULL || 4167 LDAPDefaults == lmap || 4168 LDAPDefaults->ldap_secret != lmap->ldap_secret)) 4169 lmap->ldap_secret = newstr(ldapmap_dequote(lmap->ldap_secret)); 4170 4171 if (lmap->ldap_base != NULL && 4172 (LDAPDefaults == NULL || 4173 LDAPDefaults == lmap || 4174 LDAPDefaults->ldap_base != lmap->ldap_base)) 4175 lmap->ldap_base = newstr(ldapmap_dequote(lmap->ldap_base)); 4176 4177 /* 4178 ** Save the server from extra work. If request is for a single 4179 ** match, tell the server to only return enough records to 4180 ** determine if there is a single match or not. This can not 4181 ** be one since the server would only return one and we wouldn't 4182 ** know if there were others available. 4183 */ 4184 4185 if (bitset(MF_SINGLEMATCH, map->map_mflags)) 4186 lmap->ldap_sizelimit = 2; 4187 4188 /* If setting defaults, don't process ldap_filter and ldap_attr */ 4189 if (lmap == LDAPDefaults) 4190 return TRUE; 4191 4192 if (lmap->ldap_filter != NULL) 4193 lmap->ldap_filter = newstr(ldapmap_dequote(lmap->ldap_filter)); 4194 else 4195 { 4196 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 4197 { 4198 syserr("No filter given in map %s", map->map_mname); 4199 return FALSE; 4200 } 4201 } 4202 4203 if (lmap->ldap_attr[0] != NULL) 4204 { 4205 i = 0; 4206 p = ldapmap_dequote(lmap->ldap_attr[0]); 4207 lmap->ldap_attr[0] = NULL; 4208 4209 while (p != NULL) 4210 { 4211 char *v; 4212 4213 while (isascii(*p) && isspace(*p)) 4214 p++; 4215 if (*p == '\0') 4216 break; 4217 v = p; 4218 p = strchr(v, ','); 4219 if (p != NULL) 4220 *p++ = '\0'; 4221 4222 if (i == LDAPMAP_MAX_ATTR) 4223 { 4224 syserr("Too many return attributes in %s (max %d)", 4225 map->map_mname, LDAPMAP_MAX_ATTR); 4226 return FALSE; 4227 } 4228 if (*v != '\0') 4229 lmap->ldap_attr[i++] = newstr(v); 4230 } 4231 lmap->ldap_attr[i] = NULL; 4232 } 4233 4234 map->map_db1 = (ARBPTR_T) lmap; 4235 return TRUE; 4236 } 4237 4238 /* 4239 ** LDAPMAP_CLEAR -- set default values for LDAPMAP_STRUCT 4240 ** 4241 ** Parameters: 4242 ** lmap -- pointer to LDAPMAP_STRUCT to clear 4243 ** 4244 ** Returns: 4245 ** None. 4246 ** 4247 */ 4248 4249 static void 4250 ldapmap_clear(lmap) 4251 LDAPMAP_STRUCT *lmap; 4252 { 4253 lmap->ldap_host = NULL; 4254 lmap->ldap_port = LDAP_PORT; 4255 lmap->ldap_deref = LDAP_DEREF_NEVER; 4256 lmap->ldap_timelimit = LDAP_NO_LIMIT; 4257 lmap->ldap_sizelimit = LDAP_NO_LIMIT; 4258 # ifdef LDAP_REFERRALS 4259 lmap->ldap_options = LDAP_OPT_REFERRALS; 4260 # else /* LDAP_REFERRALS */ 4261 lmap->ldap_options = 0; 4262 # endif /* LDAP_REFERRALS */ 4263 lmap->ldap_binddn = NULL; 4264 lmap->ldap_secret = NULL; 4265 lmap->ldap_method = LDAP_AUTH_SIMPLE; 4266 lmap->ldap_base = NULL; 4267 lmap->ldap_scope = LDAP_SCOPE_SUBTREE; 4268 lmap->ldap_attrsonly = LDAPMAP_FALSE; 4269 lmap->ldap_timeout.tv_sec = 0; 4270 lmap->ldap_timeout.tv_usec = 0; 4271 lmap->ldap_ld = NULL; 4272 lmap->ldap_filter = NULL; 4273 lmap->ldap_attr[0] = NULL; 4274 lmap->ldap_res = NULL; 4275 } 4276 /* 4277 ** LDAPMAP_SET_DEFAULTS -- Read default map spec from LDAPDefaults in .cf 4278 ** 4279 ** Parameters: 4280 ** spec -- map argument string from LDAPDefaults option 4281 ** 4282 ** Returns: 4283 ** None. 4284 ** 4285 */ 4286 4287 void 4288 ldapmap_set_defaults(spec) 4289 char *spec; 4290 { 4291 MAP map; 4292 4293 /* Allocate and set the default values */ 4294 if (LDAPDefaults == NULL) 4295 LDAPDefaults = (LDAPMAP_STRUCT *) xalloc(sizeof *LDAPDefaults); 4296 ldapmap_clear(LDAPDefaults); 4297 4298 memset(&map, '\0', sizeof map); 4299 map.map_db1 = (ARBPTR_T) LDAPDefaults; 4300 4301 (void) ldapmap_parseargs(&map, spec); 4302 4303 /* These should never be set in LDAPDefaults */ 4304 if (map.map_mflags != (MF_TRY0NULL|MF_TRY1NULL) || 4305 map.map_spacesub != SpaceSub || 4306 map.map_app != NULL || 4307 map.map_tapp != NULL) 4308 { 4309 syserr("readcf: option LDAPDefaultSpec: Do not set non-LDAP specific flags"); 4310 if (map.map_app != NULL) 4311 { 4312 free(map.map_app); 4313 map.map_app = NULL; 4314 } 4315 if (map.map_tapp != NULL) 4316 { 4317 free(map.map_tapp); 4318 map.map_tapp = NULL; 4319 } 4320 } 4321 4322 if (LDAPDefaults->ldap_filter != NULL) 4323 { 4324 syserr("readcf: option LDAPDefaultSpec: Do not set the LDAP search filter"); 4325 /* don't free, it isn't malloc'ed in parseargs */ 4326 LDAPDefaults->ldap_filter = NULL; 4327 } 4328 4329 if (LDAPDefaults->ldap_attr[0] != NULL) 4330 { 4331 syserr("readcf: option LDAPDefaultSpec: Do not set the requested LDAP attributes"); 4332 /* don't free, they aren't malloc'ed in parseargs */ 4333 LDAPDefaults->ldap_attr[0] = NULL; 4334 } 4335 } 4336 #endif /* LDAPMAP */ 4337 /* 4338 ** PH map 4339 */ 4340 4341 #ifdef PH_MAP 4342 4343 /* 4344 ** Support for the CCSO Nameserver (ph/qi). 4345 ** This code is intended to replace the so-called "ph mailer". 4346 ** Contributed by Mark D. Roth <roth@uiuc.edu>. Contact him for support. 4347 */ 4348 4349 # include <qiapi.h> 4350 # include <qicode.h> 4351 4352 /* 4353 ** PH_MAP_PARSEARGS -- parse ph map definition args. 4354 */ 4355 4356 bool 4357 ph_map_parseargs(map, args) 4358 MAP *map; 4359 char *args; 4360 { 4361 int i; 4362 register int done; 4363 PH_MAP_STRUCT *pmap = NULL; 4364 register char *p = args; 4365 4366 pmap = (PH_MAP_STRUCT *) xalloc(sizeof *pmap); 4367 4368 /* defaults */ 4369 pmap->ph_servers = NULL; 4370 pmap->ph_field_list = NULL; 4371 pmap->ph_to_server = NULL; 4372 pmap->ph_from_server = NULL; 4373 pmap->ph_sockfd = -1; 4374 pmap->ph_timeout = 0; 4375 4376 map->map_mflags |= MF_TRY0NULL|MF_TRY1NULL; 4377 for (;;) 4378 { 4379 while (isascii(*p) && isspace(*p)) 4380 p++; 4381 if (*p != '-') 4382 break; 4383 switch (*++p) 4384 { 4385 case 'N': 4386 map->map_mflags |= MF_INCLNULL; 4387 map->map_mflags &= ~MF_TRY0NULL; 4388 break; 4389 4390 case 'O': 4391 map->map_mflags &= ~MF_TRY1NULL; 4392 break; 4393 4394 case 'o': 4395 map->map_mflags |= MF_OPTIONAL; 4396 break; 4397 4398 case 'f': 4399 map->map_mflags |= MF_NOFOLDCASE; 4400 break; 4401 4402 case 'm': 4403 map->map_mflags |= MF_MATCHONLY; 4404 break; 4405 4406 case 'A': 4407 map->map_mflags |= MF_APPEND; 4408 break; 4409 4410 case 'q': 4411 map->map_mflags |= MF_KEEPQUOTES; 4412 break; 4413 4414 case 't': 4415 map->map_mflags |= MF_NODEFER; 4416 break; 4417 4418 case 'a': 4419 map->map_app = ++p; 4420 break; 4421 4422 case 'T': 4423 map->map_tapp = ++p; 4424 break; 4425 4426 #if _FFR_PHMAP_TIMEOUT 4427 case 'l': 4428 while (isascii(*++p) && isspace(*p)) 4429 continue; 4430 pmap->ph_timeout = atoi(p); 4431 break; 4432 #endif /* _FFR_PHMAP_TIMEOUT */ 4433 4434 case 'S': 4435 map->map_spacesub = *++p; 4436 break; 4437 4438 case 'D': 4439 map->map_mflags |= MF_DEFER; 4440 break; 4441 4442 case 'h': /* PH server list */ 4443 while (isascii(*++p) && isspace(*p)) 4444 continue; 4445 pmap->ph_servers = p; 4446 break; 4447 4448 case 'v': /* fields to search for */ 4449 while (isascii(*++p) && isspace(*p)) 4450 continue; 4451 pmap->ph_field_list = p; 4452 break; 4453 4454 default: 4455 syserr("ph_map_parseargs: unknown option -%c\n", *p); 4456 } 4457 4458 /* try to account for quoted strings */ 4459 done = isascii(*p) && isspace(*p); 4460 while (*p != '\0' && !done) 4461 { 4462 if (*p == '"') 4463 { 4464 while (*++p != '"' && *p != '\0') 4465 continue; 4466 if (*p != '\0') 4467 p++; 4468 } 4469 else 4470 p++; 4471 done = isascii(*p) && isspace(*p); 4472 } 4473 4474 if (*p != '\0') 4475 *p++ = '\0'; 4476 } 4477 4478 if (map->map_app != NULL) 4479 map->map_app = newstr(ph_map_dequote(map->map_app)); 4480 if (map->map_tapp != NULL) 4481 map->map_tapp = newstr(ph_map_dequote(map->map_tapp)); 4482 4483 if (pmap->ph_field_list != NULL) 4484 pmap->ph_field_list = newstr(ph_map_dequote(pmap->ph_field_list)); 4485 else 4486 pmap->ph_field_list = DEFAULT_PH_MAP_FIELDS; 4487 4488 if (pmap->ph_servers != NULL) 4489 pmap->ph_servers = newstr(ph_map_dequote(pmap->ph_servers)); 4490 else 4491 { 4492 syserr("ph_map_parseargs: -h flag is required"); 4493 return FALSE; 4494 } 4495 4496 map->map_db1 = (ARBPTR_T) pmap; 4497 return TRUE; 4498 } 4499 4500 #if _FFR_PHMAP_TIMEOUT 4501 /* 4502 ** PH_MAP_CLOSE -- close the connection to the ph server 4503 */ 4504 4505 static void 4506 ph_map_safeclose(map) 4507 MAP *map; 4508 { 4509 int save_errno = errno; 4510 PH_MAP_STRUCT *pmap; 4511 4512 pmap = (PH_MAP_STRUCT *)map->map_db1; 4513 4514 if (pmap->ph_sockfd != -1) 4515 { 4516 (void) close(pmap->ph_sockfd); 4517 pmap->ph_sockfd = -1; 4518 } 4519 if (pmap->ph_from_server != NULL) 4520 { 4521 (void) fclose(pmap->ph_from_server); 4522 pmap->ph_from_server = NULL; 4523 } 4524 if (pmap->ph_to_server != NULL) 4525 { 4526 (void) fclose(pmap->ph_to_server); 4527 pmap->ph_to_server = NULL; 4528 } 4529 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 4530 errno = save_errno; 4531 } 4532 4533 void 4534 ph_map_close(map) 4535 MAP *map; 4536 { 4537 PH_MAP_STRUCT *pmap; 4538 4539 pmap = (PH_MAP_STRUCT *)map->map_db1; 4540 (void) fprintf(pmap->ph_to_server, "quit\n"); 4541 (void) fflush(pmap->ph_to_server); 4542 ph_map_safeclose(map); 4543 } 4544 4545 static jmp_buf PHTimeout; 4546 4547 /* ARGSUSED */ 4548 static void 4549 ph_timeout_func(sig_no) 4550 int sig_no; 4551 { 4552 longjmp(PHTimeout, 1); 4553 } 4554 #else /* _FFR_PHMAP_TIMEOUT */ 4555 /* 4556 ** PH_MAP_CLOSE -- close the connection to the ph server 4557 */ 4558 4559 void 4560 ph_map_close(map) 4561 MAP *map; 4562 { 4563 PH_MAP_STRUCT *pmap; 4564 4565 pmap = (PH_MAP_STRUCT *)map->map_db1; 4566 CloseQi(pmap->ph_to_server, pmap->ph_from_server); 4567 pmap->ph_to_server = NULL; 4568 pmap->ph_from_server = NULL; 4569 } 4570 #endif /* _FFR_PHMAP_TIMEOUT */ 4571 4572 /* 4573 ** PH_MAP_OPEN -- sub for opening PH map 4574 */ 4575 bool 4576 ph_map_open(map, mode) 4577 MAP *map; 4578 int mode; 4579 { 4580 #if !_FFR_PHMAP_TIMEOUT 4581 int save_errno = 0; 4582 #endif /* !_FFR_PHMAP_TIMEOUT */ 4583 int j; 4584 char *hostlist, *tmp; 4585 QIR *server_data = NULL; 4586 PH_MAP_STRUCT *pmap; 4587 #if _FFR_PHMAP_TIMEOUT 4588 register EVENT *ev = NULL; 4589 #endif /* _FFR_PHMAP_TIMEOUT */ 4590 4591 if (tTd(38, 2)) 4592 dprintf("ph_map_open(%s)\n", map->map_mname); 4593 4594 mode &= O_ACCMODE; 4595 if (mode != O_RDONLY) 4596 { 4597 /* issue a pseudo-error message */ 4598 # ifdef ENOSYS 4599 errno = ENOSYS; 4600 # else /* ENOSYS */ 4601 # ifdef EFTYPE 4602 errno = EFTYPE; 4603 # else /* EFTYPE */ 4604 errno = ENXIO; 4605 # endif /* EFTYPE */ 4606 # endif /* ENOSYS */ 4607 return FALSE; 4608 } 4609 4610 pmap = (PH_MAP_STRUCT *)map->map_db1; 4611 4612 hostlist = newstr(pmap->ph_servers); 4613 tmp = strtok(hostlist, " "); 4614 do 4615 { 4616 #if _FFR_PHMAP_TIMEOUT 4617 if (pmap->ph_timeout != 0) 4618 { 4619 if (setjmp(PHTimeout) != 0) 4620 { 4621 ev = NULL; 4622 if (LogLevel > 1) 4623 sm_syslog(LOG_NOTICE, CurEnv->e_id, 4624 "timeout connecting to PH server %.100s", 4625 tmp); 4626 # ifdef ETIMEDOUT 4627 errno = ETIMEDOUT; 4628 # else /* ETIMEDOUT */ 4629 errno = 0; 4630 # endif /* ETIMEDOUT */ 4631 goto ph_map_open_abort; 4632 } 4633 ev = setevent(pmap->ph_timeout, ph_timeout_func, 0); 4634 } 4635 if (!OpenQiSock(tmp, &(pmap->ph_sockfd)) && 4636 !Sock2FILEs(pmap->ph_sockfd, &(pmap->ph_to_server), 4637 &(pmap->ph_from_server)) && 4638 fprintf(pmap->ph_to_server, "id sendmail+phmap\n") >= 0 && 4639 fflush(pmap->ph_to_server) == 0 && 4640 (server_data = ReadQi(pmap->ph_from_server, &j)) != NULL && 4641 server_data->code == 200) 4642 { 4643 if (ev != NULL) 4644 clrevent(ev); 4645 FreeQIR(server_data); 4646 #else /* _FFR_PHMAP_TIMEOUT */ 4647 if (OpenQi(tmp, &(pmap->ph_to_server), 4648 &(pmap->ph_from_server)) >= 0) 4649 { 4650 if (fprintf(pmap->ph_to_server, 4651 "id sendmail+phmap\n") < 0 || 4652 fflush(pmap->ph_to_server) < 0 || 4653 (server_data = ReadQi(pmap->ph_from_server, 4654 &j)) == NULL || 4655 server_data->code != 200) 4656 { 4657 save_errno = errno; 4658 CloseQi(pmap->ph_to_server, 4659 pmap->ph_from_server); 4660 continue; 4661 } 4662 if (server_data != NULL) 4663 FreeQIR(server_data); 4664 #endif /* _FFR_PHMAP_TIMEOUT */ 4665 free(hostlist); 4666 return TRUE; 4667 } 4668 #if _FFR_PHMAP_TIMEOUT 4669 ph_map_open_abort: 4670 if (ev != NULL) 4671 clrevent(ev); 4672 ph_map_safeclose(map); 4673 if (server_data != NULL) 4674 { 4675 FreeQIR(server_data); 4676 server_data = NULL; 4677 } 4678 #else /* _FFR_PHMAP_TIMEOUT */ 4679 save_errno = errno; 4680 #endif /* _FFR_PHMAP_TIMEOUT */ 4681 } while (tmp = strtok(NULL, " ")); 4682 4683 #if !_FFR_PHMAP_TIMEOUT 4684 errno = save_errno; 4685 #endif /* !_FFR_PHMAP_TIMEOUT */ 4686 if (!bitset(MF_OPTIONAL, map->map_mflags)) 4687 { 4688 if (errno == 0 && !bitset(MF_NODEFER,map->map_mflags)) 4689 errno = EAGAIN; 4690 syserr("ph_map_open: cannot connect to PH server"); 4691 } 4692 else if (LogLevel > 1) 4693 sm_syslog(LOG_NOTICE, CurEnv->e_id, 4694 "ph_map_open: cannot connect to PH server"); 4695 free(hostlist); 4696 return FALSE; 4697 } 4698 4699 /* 4700 ** PH_MAP_LOOKUP -- look up key from ph server 4701 */ 4702 4703 #if _FFR_PHMAP_TIMEOUT 4704 # define MAX_PH_FIELDS 20 4705 #endif /* _FFR_PHMAP_TIMEOUT */ 4706 4707 char * 4708 ph_map_lookup(map, key, args, pstat) 4709 MAP *map; 4710 char *key; 4711 char **args; 4712 int *pstat; 4713 { 4714 int j; 4715 size_t sz; 4716 char *tmp, *tmp2; 4717 char *message = NULL, *field = NULL, *fmtkey; 4718 QIR *server_data = NULL; 4719 QIR *qirp; 4720 char keybuf[MAXKEY + 1], fieldbuf[101]; 4721 #if _FFR_PHMAP_TIMEOUT 4722 QIR *hold_data[MAX_PH_FIELDS]; 4723 int hold_data_idx = 0; 4724 register EVENT *ev = NULL; 4725 #endif /* _FFR_PHMAP_TIMEOUT */ 4726 PH_MAP_STRUCT *pmap; 4727 4728 pmap = (PH_MAP_STRUCT *)map->map_db1; 4729 4730 *pstat = EX_OK; 4731 4732 #if _FFR_PHMAP_TIMEOUT 4733 if (pmap->ph_timeout != 0) 4734 { 4735 if (setjmp(PHTimeout) != 0) 4736 { 4737 ev = NULL; 4738 if (LogLevel > 1) 4739 sm_syslog(LOG_NOTICE, CurEnv->e_id, 4740 "timeout during PH lookup of %.100s", 4741 key); 4742 # ifdef ETIMEDOUT 4743 errno = ETIMEDOUT; 4744 # else /* ETIMEDOUT */ 4745 errno = 0; 4746 # endif /* ETIMEDOUT */ 4747 *pstat = EX_TEMPFAIL; 4748 goto ph_map_lookup_abort; 4749 } 4750 ev = setevent(pmap->ph_timeout, ph_timeout_func, 0); 4751 } 4752 4753 #endif /* _FFR_PHMAP_TIMEOUT */ 4754 /* check all relevant fields */ 4755 tmp = pmap->ph_field_list; 4756 do 4757 { 4758 #if _FFR_PHMAP_TIMEOUT 4759 server_data = NULL; 4760 #endif /* _FFR_PHMAP_TIMEOUT */ 4761 while (isascii(*tmp) && isspace(*tmp)) 4762 tmp++; 4763 if (*tmp == '\0') 4764 break; 4765 sz = strcspn(tmp, " ") + 1; 4766 if (sz > sizeof fieldbuf) 4767 sz = sizeof fieldbuf; 4768 (void) strlcpy(fieldbuf, tmp, sz); 4769 field = fieldbuf; 4770 tmp += sz; 4771 4772 (void) strlcpy(keybuf, key, sizeof keybuf); 4773 fmtkey = keybuf; 4774 if (strcmp(field, "alias") == 0) 4775 { 4776 /* 4777 ** for alias lookups, replace any punctuation 4778 ** characters with '-' 4779 */ 4780 4781 for (tmp2 = fmtkey; *tmp2 != '\0'; tmp2++) 4782 { 4783 if (isascii(*tmp2) && ispunct(*tmp2)) 4784 *tmp2 = '-'; 4785 } 4786 tmp2 = field; 4787 } 4788 else if (strcmp(field,"spacedname") == 0) 4789 { 4790 /* 4791 ** for "spaced" name lookups, replace any 4792 ** punctuation characters with a space 4793 */ 4794 4795 for (tmp2 = fmtkey; *tmp2 != '\0'; tmp2++) 4796 { 4797 if (isascii(*tmp2) && ispunct(*tmp2) && 4798 *tmp2 != '*') 4799 *tmp2 = ' '; 4800 } 4801 tmp2 = &(field[6]); 4802 } 4803 else 4804 tmp2 = field; 4805 4806 if (LogLevel > 9) 4807 sm_syslog(LOG_NOTICE, CurEnv->e_id, 4808 "ph_map_lookup: query %s=\"%s\" return email", 4809 tmp2, fmtkey); 4810 if (tTd(38, 20)) 4811 dprintf("ph_map_lookup: query %s=\"%s\" return email\n", 4812 tmp2, fmtkey); 4813 4814 j = 0; 4815 4816 if (fprintf(pmap->ph_to_server, "query %s=%s return email\n", 4817 tmp2, fmtkey) < 0) 4818 message = "qi query command failed"; 4819 else if (fflush(pmap->ph_to_server) < 0) 4820 message = "qi fflush failed"; 4821 else if ((server_data = ReadQi(pmap->ph_from_server, 4822 &j)) == NULL) 4823 message = "ReadQi() returned NULL"; 4824 4825 #if _FFR_PHMAP_TIMEOUT 4826 if ((hold_data[hold_data_idx] = server_data) != NULL) 4827 { 4828 /* save pointer for later free() */ 4829 hold_data_idx++; 4830 } 4831 #endif /* _FFR_PHMAP_TIMEOUT */ 4832 4833 if (server_data == NULL || 4834 (server_data->code >= 400 && 4835 server_data->code < 500)) 4836 { 4837 /* temporary failure */ 4838 *pstat = EX_TEMPFAIL; 4839 #if _FFR_PHMAP_TIMEOUT 4840 break; 4841 #else /* _FFR_PHMAP_TIMEOUT */ 4842 if (server_data != NULL) 4843 { 4844 FreeQIR(server_data); 4845 server_data = NULL; 4846 } 4847 return NULL; 4848 #endif /* _FFR_PHMAP_TIMEOUT */ 4849 } 4850 4851 /* 4852 ** if we found a single match, break out. 4853 ** otherwise, try the next field. 4854 */ 4855 4856 if (j == 1) 4857 break; 4858 4859 /* 4860 ** check for a single response which is an error: 4861 ** ReadQi() doesn't set j on error responses, 4862 ** but we should stop here instead of moving on if 4863 ** it happens (e.g., alias found but email field empty) 4864 */ 4865 4866 for (qirp = server_data; 4867 qirp != NULL && qirp->code < 0; 4868 qirp++) 4869 { 4870 if (tTd(38, 20)) 4871 dprintf("ph_map_lookup: QIR: %d:%d:%d:%s\n", 4872 qirp->code, qirp->subcode, qirp->field, 4873 (qirp->message ? qirp->message 4874 : "[NULL]")); 4875 if (qirp->code <= -500) 4876 { 4877 j = 0; 4878 goto ph_map_lookup_abort; 4879 } 4880 } 4881 4882 #if _FFR_PHMAP_TIMEOUT 4883 } while (*tmp != '\0' && hold_data_idx < MAX_PH_FIELDS); 4884 #else /* _FFR_PHMAP_TIMEOUT */ 4885 } while (*tmp != '\0'); 4886 #endif /* _FFR_PHMAP_TIMEOUT */ 4887 4888 ph_map_lookup_abort: 4889 #if _FFR_PHMAP_TIMEOUT 4890 if (ev != NULL) 4891 clrevent(ev); 4892 4893 /* 4894 ** Return EX_TEMPFAIL if the timer popped 4895 ** or we got a temporary PH error 4896 */ 4897 4898 if (*pstat == EX_TEMPFAIL) 4899 ph_map_safeclose(map); 4900 4901 /* if we didn't find a single match, bail out */ 4902 if (*pstat == EX_OK && j != 1) 4903 *pstat = EX_UNAVAILABLE; 4904 4905 if (*pstat == EX_OK) 4906 { 4907 /* 4908 ** skip leading whitespace and chop at first address 4909 */ 4910 4911 for (tmp = server_data->message; 4912 isascii(*tmp) && isspace(*tmp); 4913 tmp++) 4914 continue; 4915 4916 for (tmp2 = tmp; *tmp2 != '\0'; tmp2++) 4917 { 4918 if (isascii(*tmp2) && isspace(*tmp2)) 4919 { 4920 *tmp2 = '\0'; 4921 break; 4922 } 4923 } 4924 4925 if (tTd(38,20)) 4926 dprintf("ph_map_lookup: %s => %s\n", key, tmp); 4927 4928 if (bitset(MF_MATCHONLY, map->map_mflags)) 4929 message = map_rewrite(map, key, strlen(key), NULL); 4930 else 4931 message = map_rewrite(map, tmp, strlen(tmp), args); 4932 } 4933 4934 /* 4935 ** Deferred free() of returned server_data values 4936 ** the deferral is to avoid the risk of a free() being 4937 ** interrupted by the event timer. By now the timeout event 4938 ** has been cleared and none of the data is still in use. 4939 */ 4940 4941 while (--hold_data_idx >= 0) 4942 { 4943 if (hold_data[hold_data_idx] != NULL) 4944 FreeQIR(hold_data[hold_data_idx]); 4945 } 4946 4947 if (*pstat == EX_OK) 4948 return message; 4949 4950 return NULL; 4951 #else /* _FFR_PHMAP_TIMEOUT */ 4952 /* if we didn't find a single match, bail out */ 4953 if (j != 1) 4954 { 4955 *pstat = EX_UNAVAILABLE; 4956 if (server_data != NULL) 4957 { 4958 FreeQIR(server_data); 4959 server_data = NULL; 4960 } 4961 return NULL; 4962 } 4963 4964 /* 4965 ** skip leading whitespace and chop at first address 4966 */ 4967 4968 for (tmp = server_data->message; 4969 isascii(*tmp) && isspace(*tmp); 4970 tmp++) 4971 continue; 4972 4973 for (tmp2 = tmp; *tmp2 != '\0'; tmp2++) 4974 { 4975 if (isascii(*tmp2) && isspace(*tmp2)) 4976 { 4977 *tmp2 = '\0'; 4978 break; 4979 } 4980 } 4981 4982 if (tTd(38,20)) 4983 dprintf("ph_map_lookup: %s => %s\n", key, tmp); 4984 4985 if (bitset(MF_MATCHONLY, map->map_mflags)) 4986 message = map_rewrite(map, key, strlen(key), NULL); 4987 else 4988 message = map_rewrite(map, tmp, strlen(tmp), args); 4989 if (server_data != NULL) 4990 { 4991 FreeQIR(server_data); 4992 server_data = NULL; 4993 } 4994 return message; 4995 #endif /* _FFR_PHMAP_TIMEOUT */ 4996 } 4997 #endif /* PH_MAP */ 4998 /* 4999 ** syslog map 5000 */ 5001 5002 #define map_prio map_lockfd /* overload field */ 5003 5004 /* 5005 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages. 5006 */ 5007 5008 bool 5009 syslog_map_parseargs(map, args) 5010 MAP *map; 5011 char *args; 5012 { 5013 char *p = args; 5014 char *priority = NULL; 5015 5016 /* there is no check whether there is really an argument */ 5017 while (*p != '\0') 5018 { 5019 while (isascii(*p) && isspace(*p)) 5020 p++; 5021 if (*p != '-') 5022 break; 5023 ++p; 5024 if (*p == 'D') 5025 { 5026 map->map_mflags |= MF_DEFER; 5027 ++p; 5028 } 5029 else if (*p == 'S') 5030 { 5031 map->map_spacesub = *++p; 5032 if (*p != '\0') 5033 p++; 5034 } 5035 else if (*p == 'L') 5036 { 5037 while (*++p != '\0' && isascii(*p) && isspace(*p)) 5038 continue; 5039 if (*p == '\0') 5040 break; 5041 priority = p; 5042 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 5043 p++; 5044 if (*p != '\0') 5045 *p++ = '\0'; 5046 } 5047 else 5048 { 5049 syserr("Illegal option %c map syslog", *p); 5050 ++p; 5051 } 5052 } 5053 5054 if (priority == NULL) 5055 map->map_prio = LOG_INFO; 5056 else 5057 { 5058 if (strncasecmp("LOG_", priority, 4) == 0) 5059 priority += 4; 5060 5061 #ifdef LOG_EMERG 5062 if (strcasecmp("EMERG", priority) == 0) 5063 map->map_prio = LOG_EMERG; 5064 else 5065 #endif /* LOG_EMERG */ 5066 #ifdef LOG_ALERT 5067 if (strcasecmp("ALERT", priority) == 0) 5068 map->map_prio = LOG_ALERT; 5069 else 5070 #endif /* LOG_ALERT */ 5071 #ifdef LOG_CRIT 5072 if (strcasecmp("CRIT", priority) == 0) 5073 map->map_prio = LOG_CRIT; 5074 else 5075 #endif /* LOG_CRIT */ 5076 #ifdef LOG_ERR 5077 if (strcasecmp("ERR", priority) == 0) 5078 map->map_prio = LOG_ERR; 5079 else 5080 #endif /* LOG_ERR */ 5081 #ifdef LOG_WARNING 5082 if (strcasecmp("WARNING", priority) == 0) 5083 map->map_prio = LOG_WARNING; 5084 else 5085 #endif /* LOG_WARNING */ 5086 #ifdef LOG_NOTICE 5087 if (strcasecmp("NOTICE", priority) == 0) 5088 map->map_prio = LOG_NOTICE; 5089 else 5090 #endif /* LOG_NOTICE */ 5091 #ifdef LOG_INFO 5092 if (strcasecmp("INFO", priority) == 0) 5093 map->map_prio = LOG_INFO; 5094 else 5095 #endif /* LOG_INFO */ 5096 #ifdef LOG_DEBUG 5097 if (strcasecmp("DEBUG", priority) == 0) 5098 map->map_prio = LOG_DEBUG; 5099 else 5100 #endif /* LOG_DEBUG */ 5101 { 5102 syserr("syslog_map_parseargs: Unknown priority %s\n", 5103 priority); 5104 return FALSE; 5105 } 5106 } 5107 return TRUE; 5108 } 5109 5110 /* 5111 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string 5112 */ 5113 5114 char * 5115 syslog_map_lookup(map, string, args, statp) 5116 MAP *map; 5117 char *string; 5118 char **args; 5119 int *statp; 5120 { 5121 char *ptr = map_rewrite(map, string, strlen(string), args); 5122 5123 if (ptr != NULL) 5124 { 5125 if (tTd(38, 20)) 5126 dprintf("syslog_map_lookup(%s (priority %d): %s\n", 5127 map->map_mname, map->map_prio, ptr); 5128 5129 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr); 5130 } 5131 5132 *statp = EX_OK; 5133 return ""; 5134 } 5135 5136 /* 5137 ** HESIOD Modules 5138 */ 5139 5140 #ifdef HESIOD 5141 5142 bool 5143 hes_map_open(map, mode) 5144 MAP *map; 5145 int mode; 5146 { 5147 if (tTd(38, 2)) 5148 dprintf("hes_map_open(%s, %s, %d)\n", 5149 map->map_mname, map->map_file, mode); 5150 5151 if (mode != O_RDONLY) 5152 { 5153 /* issue a pseudo-error message */ 5154 # ifdef ENOSYS 5155 errno = ENOSYS; 5156 # else /* ENOSYS */ 5157 # ifdef EFTYPE 5158 errno = EFTYPE; 5159 # else /* EFTYPE */ 5160 errno = ENXIO; 5161 # endif /* EFTYPE */ 5162 # endif /* ENOSYS */ 5163 return FALSE; 5164 } 5165 5166 # ifdef HESIOD_INIT 5167 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0) 5168 return TRUE; 5169 5170 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5171 syserr("421 4.0.0 cannot initialize Hesiod map (%s)", 5172 errstring(errno)); 5173 return FALSE; 5174 # else /* HESIOD_INIT */ 5175 if (hes_error() == HES_ER_UNINIT) 5176 hes_init(); 5177 switch (hes_error()) 5178 { 5179 case HES_ER_OK: 5180 case HES_ER_NOTFOUND: 5181 return TRUE; 5182 } 5183 5184 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5185 syserr("421 4.0.0 cannot initialize Hesiod map (%d)", hes_error()); 5186 5187 return FALSE; 5188 # endif /* HESIOD_INIT */ 5189 } 5190 5191 char * 5192 hes_map_lookup(map, name, av, statp) 5193 MAP *map; 5194 char *name; 5195 char **av; 5196 int *statp; 5197 { 5198 char **hp; 5199 5200 if (tTd(38, 20)) 5201 dprintf("hes_map_lookup(%s, %s)\n", map->map_file, name); 5202 5203 if (name[0] == '\\') 5204 { 5205 char *np; 5206 int nl; 5207 char nbuf[MAXNAME]; 5208 5209 nl = strlen(name); 5210 if (nl < sizeof nbuf - 1) 5211 np = nbuf; 5212 else 5213 np = xalloc(strlen(name) + 2); 5214 np[0] = '\\'; 5215 (void) strlcpy(&np[1], name, (sizeof nbuf) - 1); 5216 # ifdef HESIOD_INIT 5217 hp = hesiod_resolve(HesiodContext, np, map->map_file); 5218 # else /* HESIOD_INIT */ 5219 hp = hes_resolve(np, map->map_file); 5220 # endif /* HESIOD_INIT */ 5221 if (np != nbuf) 5222 free(np); 5223 } 5224 else 5225 { 5226 # ifdef HESIOD_INIT 5227 hp = hesiod_resolve(HesiodContext, name, map->map_file); 5228 # else /* HESIOD_INIT */ 5229 hp = hes_resolve(name, map->map_file); 5230 # endif /* HESIOD_INIT */ 5231 } 5232 # ifdef HESIOD_INIT 5233 if (hp == NULL) 5234 return NULL; 5235 if (*hp == NULL) 5236 { 5237 hesiod_free_list(HesiodContext, hp); 5238 switch (errno) 5239 { 5240 case ENOENT: 5241 *statp = EX_NOTFOUND; 5242 break; 5243 case ECONNREFUSED: 5244 case EMSGSIZE: 5245 *statp = EX_TEMPFAIL; 5246 break; 5247 case ENOMEM: 5248 default: 5249 *statp = EX_UNAVAILABLE; 5250 break; 5251 } 5252 return NULL; 5253 } 5254 # else /* HESIOD_INIT */ 5255 if (hp == NULL || hp[0] == NULL) 5256 { 5257 switch (hes_error()) 5258 { 5259 case HES_ER_OK: 5260 *statp = EX_OK; 5261 break; 5262 5263 case HES_ER_NOTFOUND: 5264 *statp = EX_NOTFOUND; 5265 break; 5266 5267 case HES_ER_CONFIG: 5268 *statp = EX_UNAVAILABLE; 5269 break; 5270 5271 case HES_ER_NET: 5272 *statp = EX_TEMPFAIL; 5273 break; 5274 } 5275 return NULL; 5276 } 5277 # endif /* HESIOD_INIT */ 5278 5279 if (bitset(MF_MATCHONLY, map->map_mflags)) 5280 return map_rewrite(map, name, strlen(name), NULL); 5281 else 5282 return map_rewrite(map, hp[0], strlen(hp[0]), av); 5283 } 5284 5285 #endif /* HESIOD */ 5286 /* 5287 ** NeXT NETINFO Modules 5288 */ 5289 5290 #if NETINFO 5291 5292 # define NETINFO_DEFAULT_DIR "/aliases" 5293 # define NETINFO_DEFAULT_PROPERTY "members" 5294 5295 /* 5296 ** NI_MAP_OPEN -- open NetInfo Aliases 5297 */ 5298 5299 bool 5300 ni_map_open(map, mode) 5301 MAP *map; 5302 int mode; 5303 { 5304 if (tTd(38, 2)) 5305 dprintf("ni_map_open(%s, %s, %d)\n", 5306 map->map_mname, map->map_file, mode); 5307 mode &= O_ACCMODE; 5308 5309 if (*map->map_file == '\0') 5310 map->map_file = NETINFO_DEFAULT_DIR; 5311 5312 if (map->map_valcolnm == NULL) 5313 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY; 5314 5315 if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags)) 5316 map->map_coldelim = ','; 5317 5318 return TRUE; 5319 } 5320 5321 5322 /* 5323 ** NI_MAP_LOOKUP -- look up a datum in NetInfo 5324 */ 5325 5326 char * 5327 ni_map_lookup(map, name, av, statp) 5328 MAP *map; 5329 char *name; 5330 char **av; 5331 int *statp; 5332 { 5333 char *res; 5334 char *propval; 5335 5336 if (tTd(38, 20)) 5337 dprintf("ni_map_lookup(%s, %s)\n", map->map_mname, name); 5338 5339 propval = ni_propval(map->map_file, map->map_keycolnm, name, 5340 map->map_valcolnm, map->map_coldelim); 5341 5342 if (propval == NULL) 5343 return NULL; 5344 5345 if (bitset(MF_MATCHONLY, map->map_mflags)) 5346 res = map_rewrite(map, name, strlen(name), NULL); 5347 else 5348 res = map_rewrite(map, propval, strlen(propval), av); 5349 free(propval); 5350 return res; 5351 } 5352 5353 5354 static bool 5355 ni_getcanonname(name, hbsize, statp) 5356 char *name; 5357 int hbsize; 5358 int *statp; 5359 { 5360 char *vptr; 5361 char *ptr; 5362 char nbuf[MAXNAME + 1]; 5363 5364 if (tTd(38, 20)) 5365 dprintf("ni_getcanonname(%s)\n", name); 5366 5367 if (strlcpy(nbuf, name, sizeof nbuf) >= sizeof nbuf) 5368 { 5369 *statp = EX_UNAVAILABLE; 5370 return FALSE; 5371 } 5372 shorten_hostname(nbuf); 5373 5374 /* we only accept single token search key */ 5375 if (strchr(nbuf, '.')) 5376 { 5377 *statp = EX_NOHOST; 5378 return FALSE; 5379 } 5380 5381 /* Do the search */ 5382 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n'); 5383 5384 if (vptr == NULL) 5385 { 5386 *statp = EX_NOHOST; 5387 return FALSE; 5388 } 5389 5390 /* Only want the first machine name */ 5391 if ((ptr = strchr(vptr, '\n')) != NULL) 5392 *ptr = '\0'; 5393 5394 if (hbsize >= strlen(vptr)) 5395 { 5396 (void) strlcpy(name, vptr, hbsize); 5397 free(vptr); 5398 *statp = EX_OK; 5399 return TRUE; 5400 } 5401 *statp = EX_UNAVAILABLE; 5402 free(vptr); 5403 return FALSE; 5404 } 5405 5406 5407 /* 5408 ** NI_PROPVAL -- NetInfo property value lookup routine 5409 ** 5410 ** Parameters: 5411 ** keydir -- the NetInfo directory name in which to search 5412 ** for the key. 5413 ** keyprop -- the name of the property in which to find the 5414 ** property we are interested. Defaults to "name". 5415 ** keyval -- the value for which we are really searching. 5416 ** valprop -- the property name for the value in which we 5417 ** are interested. 5418 ** sepchar -- if non-nil, this can be multiple-valued, and 5419 ** we should return a string separated by this 5420 ** character. 5421 ** 5422 ** Returns: 5423 ** NULL -- if: 5424 ** 1. the directory is not found 5425 ** 2. the property name is not found 5426 ** 3. the property contains multiple values 5427 ** 4. some error occurred 5428 ** else -- the value of the lookup. 5429 ** 5430 ** Example: 5431 ** To search for an alias value, use: 5432 ** ni_propval("/aliases", "name", aliasname, "members", ',') 5433 ** 5434 ** Notes: 5435 ** Caller should free the return value of ni_proval 5436 */ 5437 5438 # include <netinfo/ni.h> 5439 5440 # define LOCAL_NETINFO_DOMAIN "." 5441 # define PARENT_NETINFO_DOMAIN ".." 5442 # define MAX_NI_LEVELS 256 5443 5444 char * 5445 ni_propval(keydir, keyprop, keyval, valprop, sepchar) 5446 char *keydir; 5447 char *keyprop; 5448 char *keyval; 5449 char *valprop; 5450 int sepchar; 5451 { 5452 char *propval = NULL; 5453 int i; 5454 int j, alen, l; 5455 void *ni = NULL; 5456 void *lastni = NULL; 5457 ni_status nis; 5458 ni_id nid; 5459 ni_namelist ninl; 5460 register char *p; 5461 char keybuf[1024]; 5462 5463 /* 5464 ** Create the full key from the two parts. 5465 ** 5466 ** Note that directory can end with, e.g., "name=" to specify 5467 ** an alternate search property. 5468 */ 5469 5470 i = strlen(keydir) + strlen(keyval) + 2; 5471 if (keyprop != NULL) 5472 i += strlen(keyprop) + 1; 5473 if (i >= sizeof keybuf) 5474 return NULL; 5475 (void) strlcpy(keybuf, keydir, sizeof keybuf); 5476 (void) strlcat(keybuf, "/", sizeof keybuf); 5477 if (keyprop != NULL) 5478 { 5479 (void) strlcat(keybuf, keyprop, sizeof keybuf); 5480 (void) strlcat(keybuf, "=", sizeof keybuf); 5481 } 5482 (void) strlcat(keybuf, keyval, sizeof keybuf); 5483 5484 if (tTd(38, 21)) 5485 dprintf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n", 5486 keydir, keyprop, keyval, valprop, sepchar, keybuf); 5487 /* 5488 ** If the passed directory and property name are found 5489 ** in one of netinfo domains we need to search (starting 5490 ** from the local domain moving all the way back to the 5491 ** root domain) set propval to the property's value 5492 ** and return it. 5493 */ 5494 5495 for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++) 5496 { 5497 if (i == 0) 5498 { 5499 nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni); 5500 if (tTd(38, 20)) 5501 dprintf("ni_open(LOCAL) = %d\n", nis); 5502 } 5503 else 5504 { 5505 if (lastni != NULL) 5506 ni_free(lastni); 5507 lastni = ni; 5508 nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni); 5509 if (tTd(38, 20)) 5510 dprintf("ni_open(PARENT) = %d\n", nis); 5511 } 5512 5513 /* 5514 ** Don't bother if we didn't get a handle on a 5515 ** proper domain. This is not necessarily an error. 5516 ** We would get a positive ni_status if, for instance 5517 ** we never found the directory or property and tried 5518 ** to open the parent of the root domain! 5519 */ 5520 5521 if (nis != 0) 5522 break; 5523 5524 /* 5525 ** Find the path to the server information. 5526 */ 5527 5528 if (ni_pathsearch(ni, &nid, keybuf) != 0) 5529 continue; 5530 5531 /* 5532 ** Find associated value information. 5533 */ 5534 5535 if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0) 5536 continue; 5537 5538 if (tTd(38, 20)) 5539 dprintf("ni_lookupprop: len=%d\n", 5540 ninl.ni_namelist_len); 5541 5542 /* 5543 ** See if we have an acceptable number of values. 5544 */ 5545 5546 if (ninl.ni_namelist_len <= 0) 5547 continue; 5548 5549 if (sepchar == '\0' && ninl.ni_namelist_len > 1) 5550 { 5551 ni_namelist_free(&ninl); 5552 continue; 5553 } 5554 5555 /* 5556 ** Calculate number of bytes needed and build result 5557 */ 5558 5559 alen = 1; 5560 for (j = 0; j < ninl.ni_namelist_len; j++) 5561 alen += strlen(ninl.ni_namelist_val[j]) + 1; 5562 propval = p = xalloc(alen); 5563 for (j = 0; j < ninl.ni_namelist_len; j++) 5564 { 5565 (void) strlcpy(p, ninl.ni_namelist_val[j], alen); 5566 l = strlen(p); 5567 p += l; 5568 *p++ = sepchar; 5569 alen -= l + 1; 5570 } 5571 *--p = '\0'; 5572 5573 ni_namelist_free(&ninl); 5574 } 5575 5576 /* 5577 ** Clean up. 5578 */ 5579 5580 if (ni != NULL) 5581 ni_free(ni); 5582 if (lastni != NULL && ni != lastni) 5583 ni_free(lastni); 5584 if (tTd(38, 20)) 5585 dprintf("ni_propval returns: '%s'\n", propval); 5586 5587 return propval; 5588 } 5589 5590 #endif /* NETINFO */ 5591 /* 5592 ** TEXT (unindexed text file) Modules 5593 ** 5594 ** This code donated by Sun Microsystems. 5595 */ 5596 5597 #define map_sff map_lockfd /* overload field */ 5598 5599 5600 /* 5601 ** TEXT_MAP_OPEN -- open text table 5602 */ 5603 5604 bool 5605 text_map_open(map, mode) 5606 MAP *map; 5607 int mode; 5608 { 5609 long sff; 5610 int i; 5611 5612 if (tTd(38, 2)) 5613 dprintf("text_map_open(%s, %s, %d)\n", 5614 map->map_mname, map->map_file, mode); 5615 5616 mode &= O_ACCMODE; 5617 if (mode != O_RDONLY) 5618 { 5619 errno = EPERM; 5620 return FALSE; 5621 } 5622 5623 if (*map->map_file == '\0') 5624 { 5625 syserr("text map \"%s\": file name required", 5626 map->map_mname); 5627 return FALSE; 5628 } 5629 5630 if (map->map_file[0] != '/') 5631 { 5632 syserr("text map \"%s\": file name must be fully qualified", 5633 map->map_mname); 5634 return FALSE; 5635 } 5636 5637 sff = SFF_ROOTOK|SFF_REGONLY; 5638 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 5639 sff |= SFF_NOWLINK; 5640 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 5641 sff |= SFF_SAFEDIRPATH; 5642 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName, 5643 sff, S_IRUSR, NULL)) != 0) 5644 { 5645 int save_errno = errno; 5646 5647 /* cannot open this map */ 5648 if (tTd(38, 2)) 5649 dprintf("\tunsafe map file: %d\n", i); 5650 errno = save_errno; 5651 if (!bitset(MF_OPTIONAL, map->map_mflags)) 5652 syserr("text map \"%s\": unsafe map file %s", 5653 map->map_mname, map->map_file); 5654 return FALSE; 5655 } 5656 5657 if (map->map_keycolnm == NULL) 5658 map->map_keycolno = 0; 5659 else 5660 { 5661 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm))) 5662 { 5663 syserr("text map \"%s\", file %s: -k should specify a number, not %s", 5664 map->map_mname, map->map_file, 5665 map->map_keycolnm); 5666 return FALSE; 5667 } 5668 map->map_keycolno = atoi(map->map_keycolnm); 5669 } 5670 5671 if (map->map_valcolnm == NULL) 5672 map->map_valcolno = 0; 5673 else 5674 { 5675 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm))) 5676 { 5677 syserr("text map \"%s\", file %s: -v should specify a number, not %s", 5678 map->map_mname, map->map_file, 5679 map->map_valcolnm); 5680 return FALSE; 5681 } 5682 map->map_valcolno = atoi(map->map_valcolnm); 5683 } 5684 5685 if (tTd(38, 2)) 5686 { 5687 dprintf("text_map_open(%s, %s): delimiter = ", 5688 map->map_mname, map->map_file); 5689 if (map->map_coldelim == '\0') 5690 dprintf("(white space)\n"); 5691 else 5692 dprintf("%c\n", map->map_coldelim); 5693 } 5694 5695 map->map_sff = sff; 5696 return TRUE; 5697 } 5698 5699 5700 /* 5701 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table 5702 */ 5703 5704 char * 5705 text_map_lookup(map, name, av, statp) 5706 MAP *map; 5707 char *name; 5708 char **av; 5709 int *statp; 5710 { 5711 char *vp; 5712 auto int vsize; 5713 int buflen; 5714 FILE *f; 5715 char delim; 5716 int key_idx; 5717 bool found_it; 5718 long sff = map->map_sff; 5719 char search_key[MAXNAME + 1]; 5720 char linebuf[MAXLINE]; 5721 char buf[MAXNAME + 1]; 5722 5723 found_it = FALSE; 5724 if (tTd(38, 20)) 5725 dprintf("text_map_lookup(%s, %s)\n", map->map_mname, name); 5726 5727 buflen = strlen(name); 5728 if (buflen > sizeof search_key - 1) 5729 buflen = sizeof search_key - 1; 5730 memmove(search_key, name, buflen); 5731 search_key[buflen] = '\0'; 5732 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 5733 makelower(search_key); 5734 5735 f = safefopen(map->map_file, O_RDONLY, FileMode, sff); 5736 if (f == NULL) 5737 { 5738 map->map_mflags &= ~(MF_VALID|MF_OPEN); 5739 *statp = EX_UNAVAILABLE; 5740 return NULL; 5741 } 5742 key_idx = map->map_keycolno; 5743 delim = map->map_coldelim; 5744 while (fgets(linebuf, MAXLINE, f) != NULL) 5745 { 5746 char *p; 5747 5748 /* skip comment line */ 5749 if (linebuf[0] == '#') 5750 continue; 5751 p = strchr(linebuf, '\n'); 5752 if (p != NULL) 5753 *p = '\0'; 5754 p = get_column(linebuf, key_idx, delim, buf, sizeof buf); 5755 if (p != NULL && strcasecmp(search_key, p) == 0) 5756 { 5757 found_it = TRUE; 5758 break; 5759 } 5760 } 5761 (void) fclose(f); 5762 if (!found_it) 5763 { 5764 *statp = EX_NOTFOUND; 5765 return NULL; 5766 } 5767 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf); 5768 if (vp == NULL) 5769 { 5770 *statp = EX_NOTFOUND; 5771 return NULL; 5772 } 5773 vsize = strlen(vp); 5774 *statp = EX_OK; 5775 if (bitset(MF_MATCHONLY, map->map_mflags)) 5776 return map_rewrite(map, name, strlen(name), NULL); 5777 else 5778 return map_rewrite(map, vp, vsize, av); 5779 } 5780 5781 /* 5782 ** TEXT_GETCANONNAME -- look up canonical name in hosts file 5783 */ 5784 5785 static bool 5786 text_getcanonname(name, hbsize, statp) 5787 char *name; 5788 int hbsize; 5789 int *statp; 5790 { 5791 bool found; 5792 FILE *f; 5793 char linebuf[MAXLINE]; 5794 char cbuf[MAXNAME + 1]; 5795 char nbuf[MAXNAME + 1]; 5796 5797 if (tTd(38, 20)) 5798 dprintf("text_getcanonname(%s)\n", name); 5799 5800 if (strlen(name) >= (SIZE_T) sizeof nbuf) 5801 { 5802 *statp = EX_UNAVAILABLE; 5803 return FALSE; 5804 } 5805 (void) strlcpy(nbuf, name, sizeof nbuf); 5806 shorten_hostname(nbuf); 5807 5808 f = fopen(HostsFile, "r"); 5809 if (f == NULL) 5810 { 5811 *statp = EX_UNAVAILABLE; 5812 return FALSE; 5813 } 5814 found = FALSE; 5815 while (!found && fgets(linebuf, MAXLINE, f) != NULL) 5816 { 5817 char *p = strpbrk(linebuf, "#\n"); 5818 5819 if (p != NULL) 5820 *p = '\0'; 5821 if (linebuf[0] != '\0') 5822 found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf); 5823 } 5824 (void) fclose(f); 5825 if (!found) 5826 { 5827 *statp = EX_NOHOST; 5828 return FALSE; 5829 } 5830 5831 if ((SIZE_T) hbsize >= strlen(cbuf)) 5832 { 5833 (void) strlcpy(name, cbuf, hbsize); 5834 *statp = EX_OK; 5835 return TRUE; 5836 } 5837 *statp = EX_UNAVAILABLE; 5838 return FALSE; 5839 } 5840 /* 5841 ** STAB (Symbol Table) Modules 5842 */ 5843 5844 5845 /* 5846 ** STAB_MAP_LOOKUP -- look up alias in symbol table 5847 */ 5848 5849 /* ARGSUSED2 */ 5850 char * 5851 stab_map_lookup(map, name, av, pstat) 5852 register MAP *map; 5853 char *name; 5854 char **av; 5855 int *pstat; 5856 { 5857 register STAB *s; 5858 5859 if (tTd(38, 20)) 5860 dprintf("stab_lookup(%s, %s)\n", 5861 map->map_mname, name); 5862 5863 s = stab(name, ST_ALIAS, ST_FIND); 5864 if (s != NULL) 5865 return s->s_alias; 5866 return NULL; 5867 } 5868 5869 5870 /* 5871 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild) 5872 */ 5873 5874 void 5875 stab_map_store(map, lhs, rhs) 5876 register MAP *map; 5877 char *lhs; 5878 char *rhs; 5879 { 5880 register STAB *s; 5881 5882 s = stab(lhs, ST_ALIAS, ST_ENTER); 5883 s->s_alias = newstr(rhs); 5884 } 5885 5886 5887 /* 5888 ** STAB_MAP_OPEN -- initialize (reads data file) 5889 ** 5890 ** This is a wierd case -- it is only intended as a fallback for 5891 ** aliases. For this reason, opens for write (only during a 5892 ** "newaliases") always fails, and opens for read open the 5893 ** actual underlying text file instead of the database. 5894 */ 5895 5896 bool 5897 stab_map_open(map, mode) 5898 register MAP *map; 5899 int mode; 5900 { 5901 FILE *af; 5902 long sff; 5903 struct stat st; 5904 5905 if (tTd(38, 2)) 5906 dprintf("stab_map_open(%s, %s, %d)\n", 5907 map->map_mname, map->map_file, mode); 5908 5909 mode &= O_ACCMODE; 5910 if (mode != O_RDONLY) 5911 { 5912 errno = EPERM; 5913 return FALSE; 5914 } 5915 5916 sff = SFF_ROOTOK|SFF_REGONLY; 5917 if (!bitnset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 5918 sff |= SFF_NOWLINK; 5919 if (!bitnset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 5920 sff |= SFF_SAFEDIRPATH; 5921 af = safefopen(map->map_file, O_RDONLY, 0444, sff); 5922 if (af == NULL) 5923 return FALSE; 5924 readaliases(map, af, FALSE, FALSE); 5925 5926 if (fstat(fileno(af), &st) >= 0) 5927 map->map_mtime = st.st_mtime; 5928 (void) fclose(af); 5929 5930 return TRUE; 5931 } 5932 /* 5933 ** Implicit Modules 5934 ** 5935 ** Tries several types. For back compatibility of aliases. 5936 */ 5937 5938 5939 /* 5940 ** IMPL_MAP_LOOKUP -- lookup in best open database 5941 */ 5942 5943 char * 5944 impl_map_lookup(map, name, av, pstat) 5945 MAP *map; 5946 char *name; 5947 char **av; 5948 int *pstat; 5949 { 5950 if (tTd(38, 20)) 5951 dprintf("impl_map_lookup(%s, %s)\n", 5952 map->map_mname, name); 5953 5954 #ifdef NEWDB 5955 if (bitset(MF_IMPL_HASH, map->map_mflags)) 5956 return db_map_lookup(map, name, av, pstat); 5957 #endif /* NEWDB */ 5958 #ifdef NDBM 5959 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 5960 return ndbm_map_lookup(map, name, av, pstat); 5961 #endif /* NDBM */ 5962 return stab_map_lookup(map, name, av, pstat); 5963 } 5964 5965 /* 5966 ** IMPL_MAP_STORE -- store in open databases 5967 */ 5968 5969 void 5970 impl_map_store(map, lhs, rhs) 5971 MAP *map; 5972 char *lhs; 5973 char *rhs; 5974 { 5975 if (tTd(38, 12)) 5976 dprintf("impl_map_store(%s, %s, %s)\n", 5977 map->map_mname, lhs, rhs); 5978 #ifdef NEWDB 5979 if (bitset(MF_IMPL_HASH, map->map_mflags)) 5980 db_map_store(map, lhs, rhs); 5981 #endif /* NEWDB */ 5982 #ifdef NDBM 5983 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 5984 ndbm_map_store(map, lhs, rhs); 5985 #endif /* NDBM */ 5986 stab_map_store(map, lhs, rhs); 5987 } 5988 5989 /* 5990 ** IMPL_MAP_OPEN -- implicit database open 5991 */ 5992 5993 bool 5994 impl_map_open(map, mode) 5995 MAP *map; 5996 int mode; 5997 { 5998 if (tTd(38, 2)) 5999 dprintf("impl_map_open(%s, %s, %d)\n", 6000 map->map_mname, map->map_file, mode); 6001 6002 mode &= O_ACCMODE; 6003 #ifdef NEWDB 6004 map->map_mflags |= MF_IMPL_HASH; 6005 if (hash_map_open(map, mode)) 6006 { 6007 # ifdef NDBM_YP_COMPAT 6008 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL) 6009 # endif /* NDBM_YP_COMPAT */ 6010 return TRUE; 6011 } 6012 else 6013 map->map_mflags &= ~MF_IMPL_HASH; 6014 #endif /* NEWDB */ 6015 #ifdef NDBM 6016 map->map_mflags |= MF_IMPL_NDBM; 6017 if (ndbm_map_open(map, mode)) 6018 { 6019 return TRUE; 6020 } 6021 else 6022 map->map_mflags &= ~MF_IMPL_NDBM; 6023 #endif /* NDBM */ 6024 6025 #if defined(NEWDB) || defined(NDBM) 6026 if (Verbose) 6027 message("WARNING: cannot open alias database %s%s", 6028 map->map_file, 6029 mode == O_RDONLY ? "; reading text version" : ""); 6030 #else /* defined(NEWDB) || defined(NDBM) */ 6031 if (mode != O_RDONLY) 6032 usrerr("Cannot rebuild aliases: no database format defined"); 6033 #endif /* defined(NEWDB) || defined(NDBM) */ 6034 6035 if (mode == O_RDONLY) 6036 return stab_map_open(map, mode); 6037 else 6038 return FALSE; 6039 } 6040 6041 6042 /* 6043 ** IMPL_MAP_CLOSE -- close any open database(s) 6044 */ 6045 6046 void 6047 impl_map_close(map) 6048 MAP *map; 6049 { 6050 if (tTd(38, 9)) 6051 dprintf("impl_map_close(%s, %s, %lx)\n", 6052 map->map_mname, map->map_file, map->map_mflags); 6053 #ifdef NEWDB 6054 if (bitset(MF_IMPL_HASH, map->map_mflags)) 6055 { 6056 db_map_close(map); 6057 map->map_mflags &= ~MF_IMPL_HASH; 6058 } 6059 #endif /* NEWDB */ 6060 6061 #ifdef NDBM 6062 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 6063 { 6064 ndbm_map_close(map); 6065 map->map_mflags &= ~MF_IMPL_NDBM; 6066 } 6067 #endif /* NDBM */ 6068 } 6069 /* 6070 ** User map class. 6071 ** 6072 ** Provides access to the system password file. 6073 */ 6074 6075 /* 6076 ** USER_MAP_OPEN -- open user map 6077 ** 6078 ** Really just binds field names to field numbers. 6079 */ 6080 6081 bool 6082 user_map_open(map, mode) 6083 MAP *map; 6084 int mode; 6085 { 6086 if (tTd(38, 2)) 6087 dprintf("user_map_open(%s, %d)\n", 6088 map->map_mname, mode); 6089 6090 mode &= O_ACCMODE; 6091 if (mode != O_RDONLY) 6092 { 6093 /* issue a pseudo-error message */ 6094 #ifdef ENOSYS 6095 errno = ENOSYS; 6096 #else /* ENOSYS */ 6097 # ifdef EFTYPE 6098 errno = EFTYPE; 6099 # else /* EFTYPE */ 6100 errno = ENXIO; 6101 # endif /* EFTYPE */ 6102 #endif /* ENOSYS */ 6103 return FALSE; 6104 } 6105 if (map->map_valcolnm == NULL) 6106 /* EMPTY */ 6107 /* nothing */ ; 6108 else if (strcasecmp(map->map_valcolnm, "name") == 0) 6109 map->map_valcolno = 1; 6110 else if (strcasecmp(map->map_valcolnm, "passwd") == 0) 6111 map->map_valcolno = 2; 6112 else if (strcasecmp(map->map_valcolnm, "uid") == 0) 6113 map->map_valcolno = 3; 6114 else if (strcasecmp(map->map_valcolnm, "gid") == 0) 6115 map->map_valcolno = 4; 6116 else if (strcasecmp(map->map_valcolnm, "gecos") == 0) 6117 map->map_valcolno = 5; 6118 else if (strcasecmp(map->map_valcolnm, "dir") == 0) 6119 map->map_valcolno = 6; 6120 else if (strcasecmp(map->map_valcolnm, "shell") == 0) 6121 map->map_valcolno = 7; 6122 else 6123 { 6124 syserr("User map %s: unknown column name %s", 6125 map->map_mname, map->map_valcolnm); 6126 return FALSE; 6127 } 6128 return TRUE; 6129 } 6130 6131 6132 /* 6133 ** USER_MAP_LOOKUP -- look up a user in the passwd file. 6134 */ 6135 6136 /* ARGSUSED3 */ 6137 char * 6138 user_map_lookup(map, key, av, statp) 6139 MAP *map; 6140 char *key; 6141 char **av; 6142 int *statp; 6143 { 6144 struct passwd *pw; 6145 auto bool fuzzy; 6146 6147 if (tTd(38, 20)) 6148 dprintf("user_map_lookup(%s, %s)\n", 6149 map->map_mname, key); 6150 6151 pw = finduser(key, &fuzzy); 6152 if (pw == NULL) 6153 return NULL; 6154 if (bitset(MF_MATCHONLY, map->map_mflags)) 6155 return map_rewrite(map, key, strlen(key), NULL); 6156 else 6157 { 6158 char *rwval = NULL; 6159 char buf[30]; 6160 6161 switch (map->map_valcolno) 6162 { 6163 case 0: 6164 case 1: 6165 rwval = pw->pw_name; 6166 break; 6167 6168 case 2: 6169 rwval = pw->pw_passwd; 6170 break; 6171 6172 case 3: 6173 snprintf(buf, sizeof buf, "%d", (int) pw->pw_uid); 6174 rwval = buf; 6175 break; 6176 6177 case 4: 6178 snprintf(buf, sizeof buf, "%d", (int) pw->pw_gid); 6179 rwval = buf; 6180 break; 6181 6182 case 5: 6183 rwval = pw->pw_gecos; 6184 break; 6185 6186 case 6: 6187 rwval = pw->pw_dir; 6188 break; 6189 6190 case 7: 6191 rwval = pw->pw_shell; 6192 break; 6193 } 6194 return map_rewrite(map, rwval, strlen(rwval), av); 6195 } 6196 } 6197 /* 6198 ** Program map type. 6199 ** 6200 ** This provides access to arbitrary programs. It should be used 6201 ** only very sparingly, since there is no way to bound the cost 6202 ** of invoking an arbitrary program. 6203 */ 6204 6205 char * 6206 prog_map_lookup(map, name, av, statp) 6207 MAP *map; 6208 char *name; 6209 char **av; 6210 int *statp; 6211 { 6212 int i; 6213 int save_errno; 6214 int fd; 6215 int status; 6216 auto pid_t pid; 6217 register char *p; 6218 char *rval; 6219 char *argv[MAXPV + 1]; 6220 char buf[MAXLINE]; 6221 6222 if (tTd(38, 20)) 6223 dprintf("prog_map_lookup(%s, %s) %s\n", 6224 map->map_mname, name, map->map_file); 6225 6226 i = 0; 6227 argv[i++] = map->map_file; 6228 if (map->map_rebuild != NULL) 6229 { 6230 snprintf(buf, sizeof buf, "%s", map->map_rebuild); 6231 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) 6232 { 6233 if (i >= MAXPV - 1) 6234 break; 6235 argv[i++] = p; 6236 } 6237 } 6238 argv[i++] = name; 6239 argv[i] = NULL; 6240 if (tTd(38, 21)) 6241 { 6242 dprintf("prog_open:"); 6243 for (i = 0; argv[i] != NULL; i++) 6244 dprintf(" %s", argv[i]); 6245 dprintf("\n"); 6246 } 6247 (void) blocksignal(SIGCHLD); 6248 pid = prog_open(argv, &fd, CurEnv); 6249 if (pid < 0) 6250 { 6251 if (!bitset(MF_OPTIONAL, map->map_mflags)) 6252 syserr("prog_map_lookup(%s) failed (%s) -- closing", 6253 map->map_mname, errstring(errno)); 6254 else if (tTd(38, 9)) 6255 dprintf("prog_map_lookup(%s) failed (%s) -- closing", 6256 map->map_mname, errstring(errno)); 6257 map->map_mflags &= ~(MF_VALID|MF_OPEN); 6258 *statp = EX_OSFILE; 6259 return NULL; 6260 } 6261 i = read(fd, buf, sizeof buf - 1); 6262 if (i < 0) 6263 { 6264 syserr("prog_map_lookup(%s): read error %s\n", 6265 map->map_mname, errstring(errno)); 6266 rval = NULL; 6267 } 6268 else if (i == 0) 6269 { 6270 if (tTd(38, 20)) 6271 dprintf("prog_map_lookup(%s): empty answer\n", 6272 map->map_mname); 6273 rval = NULL; 6274 } 6275 else 6276 { 6277 buf[i] = '\0'; 6278 p = strchr(buf, '\n'); 6279 if (p != NULL) 6280 *p = '\0'; 6281 6282 /* collect the return value */ 6283 if (bitset(MF_MATCHONLY, map->map_mflags)) 6284 rval = map_rewrite(map, name, strlen(name), NULL); 6285 else 6286 rval = map_rewrite(map, buf, strlen(buf), NULL); 6287 6288 /* now flush any additional output */ 6289 while ((i = read(fd, buf, sizeof buf)) > 0) 6290 continue; 6291 } 6292 6293 /* wait for the process to terminate */ 6294 (void) close(fd); 6295 status = waitfor(pid); 6296 save_errno = errno; 6297 (void) releasesignal(SIGCHLD); 6298 errno = save_errno; 6299 6300 if (status == -1) 6301 { 6302 syserr("prog_map_lookup(%s): wait error %s\n", 6303 map->map_mname, errstring(errno)); 6304 *statp = EX_SOFTWARE; 6305 rval = NULL; 6306 } 6307 else if (WIFEXITED(status)) 6308 { 6309 if ((*statp = WEXITSTATUS(status)) != EX_OK) 6310 rval = NULL; 6311 } 6312 else 6313 { 6314 syserr("prog_map_lookup(%s): child died on signal %d", 6315 map->map_mname, status); 6316 *statp = EX_UNAVAILABLE; 6317 rval = NULL; 6318 } 6319 return rval; 6320 } 6321 /* 6322 ** Sequenced map type. 6323 ** 6324 ** Tries each map in order until something matches, much like 6325 ** implicit. Stores go to the first map in the list that can 6326 ** support storing. 6327 ** 6328 ** This is slightly unusual in that there are two interfaces. 6329 ** The "sequence" interface lets you stack maps arbitrarily. 6330 ** The "switch" interface builds a sequence map by looking 6331 ** at a system-dependent configuration file such as 6332 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. 6333 ** 6334 ** We don't need an explicit open, since all maps are 6335 ** opened during startup, including underlying maps. 6336 */ 6337 6338 /* 6339 ** SEQ_MAP_PARSE -- Sequenced map parsing 6340 */ 6341 6342 bool 6343 seq_map_parse(map, ap) 6344 MAP *map; 6345 char *ap; 6346 { 6347 int maxmap; 6348 6349 if (tTd(38, 2)) 6350 dprintf("seq_map_parse(%s, %s)\n", map->map_mname, ap); 6351 maxmap = 0; 6352 while (*ap != '\0') 6353 { 6354 register char *p; 6355 STAB *s; 6356 6357 /* find beginning of map name */ 6358 while (isascii(*ap) && isspace(*ap)) 6359 ap++; 6360 for (p = ap; 6361 (isascii(*p) && isalnum(*p)) || *p == '_' || *p == '.'; 6362 p++) 6363 continue; 6364 if (*p != '\0') 6365 *p++ = '\0'; 6366 while (*p != '\0' && (!isascii(*p) || !isalnum(*p))) 6367 p++; 6368 if (*ap == '\0') 6369 { 6370 ap = p; 6371 continue; 6372 } 6373 s = stab(ap, ST_MAP, ST_FIND); 6374 if (s == NULL) 6375 { 6376 syserr("Sequence map %s: unknown member map %s", 6377 map->map_mname, ap); 6378 } 6379 else if (maxmap == MAXMAPSTACK) 6380 { 6381 syserr("Sequence map %s: too many member maps (%d max)", 6382 map->map_mname, MAXMAPSTACK); 6383 maxmap++; 6384 } 6385 else if (maxmap < MAXMAPSTACK) 6386 { 6387 map->map_stack[maxmap++] = &s->s_map; 6388 } 6389 ap = p; 6390 } 6391 return TRUE; 6392 } 6393 6394 6395 /* 6396 ** SWITCH_MAP_OPEN -- open a switched map 6397 ** 6398 ** This looks at the system-dependent configuration and builds 6399 ** a sequence map that does the same thing. 6400 ** 6401 ** Every system must define a switch_map_find routine in conf.c 6402 ** that will return the list of service types associated with a 6403 ** given service class. 6404 */ 6405 6406 bool 6407 switch_map_open(map, mode) 6408 MAP *map; 6409 int mode; 6410 { 6411 int mapno; 6412 int nmaps; 6413 char *maptype[MAXMAPSTACK]; 6414 6415 if (tTd(38, 2)) 6416 dprintf("switch_map_open(%s, %s, %d)\n", 6417 map->map_mname, map->map_file, mode); 6418 6419 mode &= O_ACCMODE; 6420 nmaps = switch_map_find(map->map_file, maptype, map->map_return); 6421 if (tTd(38, 19)) 6422 { 6423 dprintf("\tswitch_map_find => %d\n", nmaps); 6424 for (mapno = 0; mapno < nmaps; mapno++) 6425 dprintf("\t\t%s\n", maptype[mapno]); 6426 } 6427 if (nmaps <= 0 || nmaps > MAXMAPSTACK) 6428 return FALSE; 6429 6430 for (mapno = 0; mapno < nmaps; mapno++) 6431 { 6432 register STAB *s; 6433 char nbuf[MAXNAME + 1]; 6434 6435 if (maptype[mapno] == NULL) 6436 continue; 6437 (void) snprintf(nbuf, sizeof nbuf, "%s.%s", 6438 map->map_mname, maptype[mapno]); 6439 s = stab(nbuf, ST_MAP, ST_FIND); 6440 if (s == NULL) 6441 { 6442 syserr("Switch map %s: unknown member map %s", 6443 map->map_mname, nbuf); 6444 } 6445 else 6446 { 6447 map->map_stack[mapno] = &s->s_map; 6448 if (tTd(38, 4)) 6449 dprintf("\tmap_stack[%d] = %s:%s\n", 6450 mapno, s->s_map.map_class->map_cname, 6451 nbuf); 6452 } 6453 } 6454 return TRUE; 6455 } 6456 6457 6458 /* 6459 ** SEQ_MAP_CLOSE -- close all underlying maps 6460 */ 6461 6462 void 6463 seq_map_close(map) 6464 MAP *map; 6465 { 6466 int mapno; 6467 6468 if (tTd(38, 9)) 6469 dprintf("seq_map_close(%s)\n", map->map_mname); 6470 6471 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6472 { 6473 MAP *mm = map->map_stack[mapno]; 6474 6475 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags)) 6476 continue; 6477 mm->map_class->map_close(mm); 6478 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 6479 } 6480 } 6481 6482 6483 /* 6484 ** SEQ_MAP_LOOKUP -- sequenced map lookup 6485 */ 6486 6487 char * 6488 seq_map_lookup(map, key, args, pstat) 6489 MAP *map; 6490 char *key; 6491 char **args; 6492 int *pstat; 6493 { 6494 int mapno; 6495 int mapbit = 0x01; 6496 bool tempfail = FALSE; 6497 6498 if (tTd(38, 20)) 6499 dprintf("seq_map_lookup(%s, %s)\n", map->map_mname, key); 6500 6501 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) 6502 { 6503 MAP *mm = map->map_stack[mapno]; 6504 char *rv; 6505 6506 if (mm == NULL) 6507 continue; 6508 if (!bitset(MF_OPEN, mm->map_mflags) && 6509 !openmap(mm)) 6510 { 6511 if (bitset(mapbit, map->map_return[MA_UNAVAIL])) 6512 { 6513 *pstat = EX_UNAVAILABLE; 6514 return NULL; 6515 } 6516 continue; 6517 } 6518 *pstat = EX_OK; 6519 rv = mm->map_class->map_lookup(mm, key, args, pstat); 6520 if (rv != NULL) 6521 return rv; 6522 if (*pstat == EX_TEMPFAIL) 6523 { 6524 if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) 6525 return NULL; 6526 tempfail = TRUE; 6527 } 6528 else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) 6529 break; 6530 } 6531 if (tempfail) 6532 *pstat = EX_TEMPFAIL; 6533 else if (*pstat == EX_OK) 6534 *pstat = EX_NOTFOUND; 6535 return NULL; 6536 } 6537 6538 6539 /* 6540 ** SEQ_MAP_STORE -- sequenced map store 6541 */ 6542 6543 void 6544 seq_map_store(map, key, val) 6545 MAP *map; 6546 char *key; 6547 char *val; 6548 { 6549 int mapno; 6550 6551 if (tTd(38, 12)) 6552 dprintf("seq_map_store(%s, %s, %s)\n", 6553 map->map_mname, key, val); 6554 6555 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 6556 { 6557 MAP *mm = map->map_stack[mapno]; 6558 6559 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags)) 6560 continue; 6561 6562 mm->map_class->map_store(mm, key, val); 6563 return; 6564 } 6565 syserr("seq_map_store(%s, %s, %s): no writable map", 6566 map->map_mname, key, val); 6567 } 6568 /* 6569 ** NULL stubs 6570 */ 6571 6572 /* ARGSUSED */ 6573 bool 6574 null_map_open(map, mode) 6575 MAP *map; 6576 int mode; 6577 { 6578 return TRUE; 6579 } 6580 6581 /* ARGSUSED */ 6582 void 6583 null_map_close(map) 6584 MAP *map; 6585 { 6586 return; 6587 } 6588 6589 char * 6590 null_map_lookup(map, key, args, pstat) 6591 MAP *map; 6592 char *key; 6593 char **args; 6594 int *pstat; 6595 { 6596 *pstat = EX_NOTFOUND; 6597 return NULL; 6598 } 6599 6600 /* ARGSUSED */ 6601 void 6602 null_map_store(map, key, val) 6603 MAP *map; 6604 char *key; 6605 char *val; 6606 { 6607 return; 6608 } 6609 6610 6611 /* 6612 ** BOGUS stubs 6613 */ 6614 6615 char * 6616 bogus_map_lookup(map, key, args, pstat) 6617 MAP *map; 6618 char *key; 6619 char **args; 6620 int *pstat; 6621 { 6622 *pstat = EX_TEMPFAIL; 6623 return NULL; 6624 } 6625 6626 MAPCLASS BogusMapClass = 6627 { 6628 "bogus-map", NULL, 0, 6629 NULL, bogus_map_lookup, null_map_store, 6630 null_map_open, null_map_close, 6631 }; 6632 /* 6633 ** MACRO modules 6634 */ 6635 6636 char * 6637 macro_map_lookup(map, name, av, statp) 6638 MAP *map; 6639 char *name; 6640 char **av; 6641 int *statp; 6642 { 6643 int mid; 6644 6645 if (tTd(38, 20)) 6646 dprintf("macro_map_lookup(%s, %s)\n", map->map_mname, 6647 name == NULL ? "NULL" : name); 6648 6649 if (name == NULL || 6650 *name == '\0' || 6651 (mid = macid(name, NULL)) == '\0') 6652 { 6653 *statp = EX_CONFIG; 6654 return NULL; 6655 } 6656 6657 if (av[1] == NULL) 6658 define(mid, NULL, CurEnv); 6659 else 6660 define(mid, newstr(av[1]), CurEnv); 6661 6662 *statp = EX_OK; 6663 return ""; 6664 } 6665 /* 6666 ** REGEX modules 6667 */ 6668 6669 #ifdef MAP_REGEX 6670 6671 # include <regex.h> 6672 6673 # define DEFAULT_DELIM CONDELSE 6674 6675 # define END_OF_FIELDS -1 6676 6677 # define ERRBUF_SIZE 80 6678 # define MAX_MATCH 32 6679 6680 # define xnalloc(s) memset(xalloc(s), '\0', s); 6681 6682 struct regex_map 6683 { 6684 regex_t regex_pattern_buf; /* xalloc it */ 6685 int *regex_subfields; /* move to type MAP */ 6686 char *regex_delim; /* move to type MAP */ 6687 }; 6688 6689 static int 6690 parse_fields(s, ibuf, blen, nr_substrings) 6691 char *s; 6692 int *ibuf; /* array */ 6693 int blen; /* number of elements in ibuf */ 6694 int nr_substrings; /* number of substrings in the pattern */ 6695 { 6696 register char *cp; 6697 int i = 0; 6698 bool lastone = FALSE; 6699 6700 blen--; /* for terminating END_OF_FIELDS */ 6701 cp = s; 6702 do 6703 { 6704 for (;; cp++) 6705 { 6706 if (*cp == ',') 6707 { 6708 *cp = '\0'; 6709 break; 6710 } 6711 if (*cp == '\0') 6712 { 6713 lastone = TRUE; 6714 break; 6715 } 6716 } 6717 if (i < blen) 6718 { 6719 int val = atoi(s); 6720 6721 if (val < 0 || val >= nr_substrings) 6722 { 6723 syserr("field (%d) out of range, only %d substrings in pattern", 6724 val, nr_substrings); 6725 return -1; 6726 } 6727 ibuf[i++] = val; 6728 } 6729 else 6730 { 6731 syserr("too many fields, %d max\n", blen); 6732 return -1; 6733 } 6734 s = ++cp; 6735 } while (!lastone); 6736 ibuf[i] = END_OF_FIELDS; 6737 return i; 6738 } 6739 6740 bool 6741 regex_map_init(map, ap) 6742 MAP *map; 6743 char *ap; 6744 { 6745 int regerr; 6746 struct regex_map *map_p; 6747 register char *p; 6748 char *sub_param = NULL; 6749 int pflags; 6750 static char defdstr[] = { (char)DEFAULT_DELIM, '\0' }; 6751 6752 if (tTd(38, 2)) 6753 dprintf("regex_map_init: mapname '%s', args '%s'\n", 6754 map->map_mname, ap); 6755 6756 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; 6757 6758 p = ap; 6759 6760 map_p = (struct regex_map *) xnalloc(sizeof *map_p); 6761 6762 for (;;) 6763 { 6764 while (isascii(*p) && isspace(*p)) 6765 p++; 6766 if (*p != '-') 6767 break; 6768 switch (*++p) 6769 { 6770 case 'n': /* not */ 6771 map->map_mflags |= MF_REGEX_NOT; 6772 break; 6773 6774 case 'f': /* case sensitive */ 6775 map->map_mflags |= MF_NOFOLDCASE; 6776 pflags &= ~REG_ICASE; 6777 break; 6778 6779 case 'b': /* basic regular expressions */ 6780 pflags &= ~REG_EXTENDED; 6781 break; 6782 6783 case 's': /* substring match () syntax */ 6784 sub_param = ++p; 6785 pflags &= ~REG_NOSUB; 6786 break; 6787 6788 case 'd': /* delimiter */ 6789 map_p->regex_delim = ++p; 6790 break; 6791 6792 case 'a': /* map append */ 6793 map->map_app = ++p; 6794 break; 6795 6796 case 'm': /* matchonly */ 6797 map->map_mflags |= MF_MATCHONLY; 6798 break; 6799 6800 case 'S': 6801 map->map_spacesub = *++p; 6802 break; 6803 6804 case 'D': 6805 map->map_mflags |= MF_DEFER; 6806 break; 6807 6808 } 6809 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 6810 p++; 6811 if (*p != '\0') 6812 *p++ = '\0'; 6813 } 6814 if (tTd(38, 3)) 6815 dprintf("regex_map_init: compile '%s' 0x%x\n", p, pflags); 6816 6817 if ((regerr = regcomp(&(map_p->regex_pattern_buf), p, pflags)) != 0) 6818 { 6819 /* Errorhandling */ 6820 char errbuf[ERRBUF_SIZE]; 6821 6822 (void) regerror(regerr, &(map_p->regex_pattern_buf), 6823 errbuf, ERRBUF_SIZE); 6824 syserr("pattern-compile-error: %s\n", errbuf); 6825 free(map_p); 6826 return FALSE; 6827 } 6828 6829 if (map->map_app != NULL) 6830 map->map_app = newstr(map->map_app); 6831 if (map_p->regex_delim != NULL) 6832 map_p->regex_delim = newstr(map_p->regex_delim); 6833 else 6834 map_p->regex_delim = defdstr; 6835 6836 if (!bitset(REG_NOSUB, pflags)) 6837 { 6838 /* substring matching */ 6839 int substrings; 6840 int *fields = (int *) xalloc(sizeof(int) * (MAX_MATCH + 1)); 6841 6842 substrings = map_p->regex_pattern_buf.re_nsub + 1; 6843 6844 if (tTd(38, 3)) 6845 dprintf("regex_map_init: nr of substrings %d\n", 6846 substrings); 6847 6848 if (substrings >= MAX_MATCH) 6849 { 6850 syserr("too many substrings, %d max\n", MAX_MATCH); 6851 free(map_p); 6852 return FALSE; 6853 } 6854 if (sub_param != NULL && sub_param[0] != '\0') 6855 { 6856 /* optional parameter -sfields */ 6857 if (parse_fields(sub_param, fields, 6858 MAX_MATCH + 1, substrings) == -1) 6859 return FALSE; 6860 } 6861 else 6862 { 6863 /* set default fields */ 6864 int i; 6865 6866 for (i = 0; i < substrings; i++) 6867 fields[i] = i; 6868 fields[i] = END_OF_FIELDS; 6869 } 6870 map_p->regex_subfields = fields; 6871 if (tTd(38, 3)) 6872 { 6873 int *ip; 6874 6875 dprintf("regex_map_init: subfields"); 6876 for (ip = fields; *ip != END_OF_FIELDS; ip++) 6877 dprintf(" %d", *ip); 6878 dprintf("\n"); 6879 } 6880 } 6881 map->map_db1 = (ARBPTR_T)map_p; /* dirty hack */ 6882 6883 return TRUE; 6884 } 6885 6886 static char * 6887 regex_map_rewrite(map, s, slen, av) 6888 MAP *map; 6889 const char *s; 6890 size_t slen; 6891 char **av; 6892 { 6893 if (bitset(MF_MATCHONLY, map->map_mflags)) 6894 return map_rewrite(map, av[0], strlen(av[0]), NULL); 6895 else 6896 return map_rewrite(map, s, slen, NULL); 6897 } 6898 6899 char * 6900 regex_map_lookup(map, name, av, statp) 6901 MAP *map; 6902 char *name; 6903 char **av; 6904 int *statp; 6905 { 6906 int reg_res; 6907 struct regex_map *map_p; 6908 regmatch_t pmatch[MAX_MATCH]; 6909 6910 if (tTd(38, 20)) 6911 { 6912 char **cpp; 6913 6914 dprintf("regex_map_lookup: key '%s'\n", name); 6915 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 6916 dprintf("regex_map_lookup: arg '%s'\n", *cpp); 6917 } 6918 6919 map_p = (struct regex_map *)(map->map_db1); 6920 reg_res = regexec(&(map_p->regex_pattern_buf), 6921 name, MAX_MATCH, pmatch, 0); 6922 6923 if (bitset(MF_REGEX_NOT, map->map_mflags)) 6924 { 6925 /* option -n */ 6926 if (reg_res == REG_NOMATCH) 6927 return regex_map_rewrite(map, "", (size_t)0, av); 6928 else 6929 return NULL; 6930 } 6931 if (reg_res == REG_NOMATCH) 6932 return NULL; 6933 6934 if (map_p->regex_subfields != NULL) 6935 { 6936 /* option -s */ 6937 static char retbuf[MAXNAME]; 6938 int fields[MAX_MATCH + 1]; 6939 bool first = TRUE; 6940 int anglecnt = 0, cmntcnt = 0, spacecnt = 0; 6941 bool quotemode = FALSE, bslashmode = FALSE; 6942 register char *dp, *sp; 6943 char *endp, *ldp; 6944 int *ip; 6945 6946 dp = retbuf; 6947 ldp = retbuf + sizeof(retbuf) - 1; 6948 6949 if (av[1] != NULL) 6950 { 6951 if (parse_fields(av[1], fields, MAX_MATCH + 1, 6952 (int) map_p->regex_pattern_buf.re_nsub + 1) == -1) 6953 { 6954 *statp = EX_CONFIG; 6955 return NULL; 6956 } 6957 ip = fields; 6958 } 6959 else 6960 ip = map_p->regex_subfields; 6961 6962 for ( ; *ip != END_OF_FIELDS; ip++) 6963 { 6964 if (!first) 6965 { 6966 for (sp = map_p->regex_delim; *sp; sp++) 6967 { 6968 if (dp < ldp) 6969 *dp++ = *sp; 6970 } 6971 } 6972 else 6973 first = FALSE; 6974 6975 6976 if (pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0) 6977 continue; 6978 6979 sp = name + pmatch[*ip].rm_so; 6980 endp = name + pmatch[*ip].rm_eo; 6981 for (; endp > sp; sp++) 6982 { 6983 if (dp < ldp) 6984 { 6985 if (bslashmode) 6986 { 6987 *dp++ = *sp; 6988 bslashmode = FALSE; 6989 } 6990 else if (quotemode && *sp != '"' && 6991 *sp != '\\') 6992 { 6993 *dp++ = *sp; 6994 } 6995 else switch(*dp++ = *sp) 6996 { 6997 case '\\': 6998 bslashmode = TRUE; 6999 break; 7000 7001 case '(': 7002 cmntcnt++; 7003 break; 7004 7005 case ')': 7006 cmntcnt--; 7007 break; 7008 7009 case '<': 7010 anglecnt++; 7011 break; 7012 7013 case '>': 7014 anglecnt--; 7015 break; 7016 7017 case ' ': 7018 spacecnt++; 7019 break; 7020 7021 case '"': 7022 quotemode = !quotemode; 7023 break; 7024 } 7025 } 7026 } 7027 } 7028 if (anglecnt != 0 || cmntcnt != 0 || quotemode || 7029 bslashmode || spacecnt != 0) 7030 { 7031 sm_syslog(LOG_WARNING, NOQID, 7032 "Warning: regex may cause prescan() failure map=%s lookup=%s", 7033 map->map_mname, name); 7034 return NULL; 7035 } 7036 7037 *dp = '\0'; 7038 7039 return regex_map_rewrite(map, retbuf, strlen(retbuf), av); 7040 } 7041 return regex_map_rewrite(map, "", (size_t)0, av); 7042 } 7043 #endif /* MAP_REGEX */ 7044 /* 7045 ** NSD modules 7046 */ 7047 #ifdef MAP_NSD 7048 7049 # include <ndbm.h> 7050 # define _DATUM_DEFINED 7051 # include <ns_api.h> 7052 7053 typedef struct ns_map_list 7054 { 7055 ns_map_t *map; 7056 char *mapname; 7057 struct ns_map_list *next; 7058 } ns_map_list_t; 7059 7060 static ns_map_t * 7061 ns_map_t_find(mapname) 7062 char *mapname; 7063 { 7064 static ns_map_list_t *ns_maps = NULL; 7065 ns_map_list_t *ns_map; 7066 7067 /* walk the list of maps looking for the correctly named map */ 7068 for (ns_map = ns_maps; ns_map != NULL; ns_map = ns_map->next) 7069 { 7070 if (strcmp(ns_map->mapname, mapname) == 0) 7071 break; 7072 } 7073 7074 /* if we are looking at a NULL ns_map_list_t, then create a new one */ 7075 if (ns_map == NULL) 7076 { 7077 ns_map = (ns_map_list_t *) xalloc(sizeof *ns_map); 7078 ns_map->mapname = newstr(mapname); 7079 ns_map->map = (ns_map_t *) xalloc(sizeof *ns_map->map); 7080 ns_map->next = ns_maps; 7081 ns_maps = ns_map; 7082 } 7083 return ns_map->map; 7084 } 7085 7086 char * 7087 nsd_map_lookup(map, name, av, statp) 7088 MAP *map; 7089 char *name; 7090 char **av; 7091 int *statp; 7092 { 7093 int buflen; 7094 char *p; 7095 ns_map_t *ns_map; 7096 char keybuf[MAXNAME + 1]; 7097 char buf[MAXLINE]; 7098 7099 if (tTd(38, 20)) 7100 dprintf("nsd_map_lookup(%s, %s)\n", map->map_mname, name); 7101 7102 buflen = strlen(name); 7103 if (buflen > sizeof keybuf - 1) 7104 buflen = sizeof keybuf - 1; 7105 memmove(keybuf, name, buflen); 7106 keybuf[buflen] = '\0'; 7107 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 7108 makelower(keybuf); 7109 7110 ns_map = ns_map_t_find(map->map_file); 7111 if (ns_map == NULL) 7112 { 7113 if (tTd(38, 20)) 7114 dprintf("nsd_map_t_find failed\n"); 7115 return NULL; 7116 } 7117 7118 if (ns_lookup(ns_map, NULL, map->map_file, 7119 keybuf, NULL, buf, MAXLINE) == NULL) 7120 return NULL; 7121 7122 /* Null out trailing \n */ 7123 if ((p = strchr(buf, '\n')) != NULL) 7124 *p = '\0'; 7125 7126 return map_rewrite(map, buf, strlen(buf), av); 7127 } 7128 #endif /* MAP_NSD */ 7129 7130 char * 7131 arith_map_lookup(map, name, av, statp) 7132 MAP *map; 7133 char *name; 7134 char **av; 7135 int *statp; 7136 { 7137 long r; 7138 long v[2]; 7139 bool res = FALSE; 7140 bool boolres; 7141 static char result[16]; 7142 char **cpp; 7143 7144 if (tTd(38, 2)) 7145 { 7146 dprintf("arith_map_lookup: key '%s'\n", name); 7147 for (cpp = av; cpp != NULL && *cpp != NULL; cpp++) 7148 dprintf("arith_map_lookup: arg '%s'\n", *cpp); 7149 } 7150 r = 0; 7151 boolres = FALSE; 7152 cpp = av; 7153 *statp = EX_OK; 7154 7155 /* 7156 ** read arguments for arith map 7157 ** - no check is made whether they are really numbers 7158 ** - just ignores args after the second 7159 */ 7160 for (++cpp; cpp != NULL && *cpp != NULL && r < 2; cpp++) 7161 v[r++] = strtol(*cpp, NULL, 0); 7162 7163 /* operator and (at least) two operands given? */ 7164 if (name != NULL && r == 2) 7165 { 7166 switch(*name) 7167 { 7168 #if _FFR_ARITH 7169 case '|': 7170 r = v[0] | v[1]; 7171 break; 7172 7173 case '&': 7174 r = v[0] & v[1]; 7175 break; 7176 7177 case '%': 7178 if (v[1] == 0) 7179 return NULL; 7180 r = v[0] % v[1]; 7181 break; 7182 #endif /* _FFR_ARITH */ 7183 7184 case '+': 7185 r = v[0] + v[1]; 7186 break; 7187 7188 case '-': 7189 r = v[0] - v[1]; 7190 break; 7191 7192 case '*': 7193 r = v[0] * v[1]; 7194 break; 7195 7196 case '/': 7197 if (v[1] == 0) 7198 return NULL; 7199 r = v[0] / v[1]; 7200 break; 7201 7202 case 'l': 7203 res = v[0] < v[1]; 7204 boolres = TRUE; 7205 break; 7206 7207 case '=': 7208 res = v[0] == v[1]; 7209 boolres = TRUE; 7210 break; 7211 7212 default: 7213 /* XXX */ 7214 *statp = EX_CONFIG; 7215 if (LogLevel > 10) 7216 sm_syslog(LOG_WARNING, NOQID, 7217 "arith_map: unknown operator %c", 7218 isprint(*name) ? *name : '?'); 7219 return NULL; 7220 } 7221 if (boolres) 7222 snprintf(result, sizeof result, res ? "TRUE" : "FALSE"); 7223 else 7224 snprintf(result, sizeof result, "%ld", r); 7225 return result; 7226 } 7227 *statp = EX_CONFIG; 7228 return NULL; 7229 } 7230