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