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