map.c revision 42575
1/* 2 * Copyright (c) 1998 Sendmail, Inc. All rights reserved. 3 * Copyright (c) 1992, 1995-1997 Eric P. Allman. All rights reserved. 4 * Copyright (c) 1992, 1993 5 * The Regents of the University of California. All rights reserved. 6 * 7 * By using this file, you agree to the terms and conditions set 8 * forth in the LICENSE file which can be found at the top level of 9 * the sendmail distribution. 10 * 11 */ 12 13#ifndef lint 14static char sccsid[] = "@(#)map.c 8.256 (Berkeley) 11/15/1998"; 15#endif /* not lint */ 16 17#include "sendmail.h" 18 19#ifdef NDBM 20# include <ndbm.h> 21# ifdef R_FIRST 22 ERROR README: You are running the Berkeley DB version of ndbm.h. See 23 ERROR README: the README file about tweaking Berkeley DB so it can 24 ERROR README: coexist with NDBM, or delete -DNDBM from the Makefile 25 ERROR README: and use -DNEWDB instead. 26# endif 27#endif 28#ifdef NEWDB 29# include <db.h> 30# ifndef DB_VERSION_MAJOR 31# define DB_VERSION_MAJOR 1 32# endif 33#endif 34#ifdef NIS 35 struct dom_binding; /* forward reference needed on IRIX */ 36# include <rpcsvc/ypclnt.h> 37# ifdef NDBM 38# define NDBM_YP_COMPAT /* create YP-compatible NDBM files */ 39# endif 40#endif 41 42/* 43** MAP.C -- implementations for various map classes. 44** 45** Each map class implements a series of functions: 46** 47** bool map_parse(MAP *map, char *args) 48** Parse the arguments from the config file. Return TRUE 49** if they were ok, FALSE otherwise. Fill in map with the 50** values. 51** 52** char *map_lookup(MAP *map, char *key, char **args, int *pstat) 53** Look up the key in the given map. If found, do any 54** rewriting the map wants (including "args" if desired) 55** and return the value. Set *pstat to the appropriate status 56** on error and return NULL. Args will be NULL if called 57** from the alias routines, although this should probably 58** not be relied upon. It is suggested you call map_rewrite 59** to return the results -- it takes care of null termination 60** and uses a dynamically expanded buffer as needed. 61** 62** void map_store(MAP *map, char *key, char *value) 63** Store the key:value pair in the map. 64** 65** bool map_open(MAP *map, int mode) 66** Open the map for the indicated mode. Mode should 67** be either O_RDONLY or O_RDWR. Return TRUE if it 68** was opened successfully, FALSE otherwise. If the open 69** failed an the MF_OPTIONAL flag is not set, it should 70** also print an error. If the MF_ALIAS bit is set 71** and this map class understands the @:@ convention, it 72** should call aliaswait() before returning. 73** 74** void map_close(MAP *map) 75** Close the map. 76** 77** This file also includes the implementation for getcanonname. 78** It is currently implemented in a pretty ad-hoc manner; it ought 79** to be more properly integrated into the map structure. 80*/ 81 82#define DBMMODE 0644 83 84#ifndef EX_NOTFOUND 85# define EX_NOTFOUND EX_NOHOST 86#endif 87 88extern bool aliaswait __P((MAP *, char *, int)); 89extern bool extract_canonname __P((char *, char *, char[], int)); 90 91#if O_EXLOCK && HASFLOCK && !BOGUS_O_EXCL 92# define LOCK_ON_OPEN 1 /* we can open/create a locked file */ 93#else 94# define LOCK_ON_OPEN 0 /* no such luck -- bend over backwards */ 95#endif 96 97#ifndef O_ACCMODE 98# define O_ACCMODE (O_RDONLY|O_WRONLY|O_RDWR) 99#endif 100/* 101** MAP_PARSEARGS -- parse config line arguments for database lookup 102** 103** This is a generic version of the map_parse method. 104** 105** Parameters: 106** map -- the map being initialized. 107** ap -- a pointer to the args on the config line. 108** 109** Returns: 110** TRUE -- if everything parsed OK. 111** FALSE -- otherwise. 112** 113** Side Effects: 114** null terminates the filename; stores it in map 115*/ 116 117bool 118map_parseargs(map, ap) 119 MAP *map; 120 char *ap; 121{ 122 register char *p = ap; 123 124 map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL; 125 for (;;) 126 { 127 while (isascii(*p) && isspace(*p)) 128 p++; 129 if (*p != '-') 130 break; 131 switch (*++p) 132 { 133 case 'N': 134 map->map_mflags |= MF_INCLNULL; 135 map->map_mflags &= ~MF_TRY0NULL; 136 break; 137 138 case 'O': 139 map->map_mflags &= ~MF_TRY1NULL; 140 break; 141 142 case 'o': 143 map->map_mflags |= MF_OPTIONAL; 144 break; 145 146 case 'f': 147 map->map_mflags |= MF_NOFOLDCASE; 148 break; 149 150 case 'm': 151 map->map_mflags |= MF_MATCHONLY; 152 break; 153 154 case 'A': 155 map->map_mflags |= MF_APPEND; 156 break; 157 158 case 'q': 159 map->map_mflags |= MF_KEEPQUOTES; 160 break; 161 162 case 'a': 163 map->map_app = ++p; 164 break; 165 166 case 'T': 167 map->map_tapp = ++p; 168 break; 169 170 case 'k': 171 while (isascii(*++p) && isspace(*p)) 172 continue; 173 map->map_keycolnm = p; 174 break; 175 176 case 'v': 177 while (isascii(*++p) && isspace(*p)) 178 continue; 179 map->map_valcolnm = p; 180 break; 181 182 case 'z': 183 if (*++p != '\\') 184 map->map_coldelim = *p; 185 else 186 { 187 switch (*++p) 188 { 189 case 'n': 190 map->map_coldelim = '\n'; 191 break; 192 193 case 't': 194 map->map_coldelim = '\t'; 195 break; 196 197 default: 198 map->map_coldelim = '\\'; 199 } 200 } 201 break; 202 203 case 't': 204 map->map_mflags |= MF_NODEFER; 205 break; 206 207#ifdef RESERVED_FOR_SUN 208 case 'd': 209 map->map_mflags |= MF_DOMAIN_WIDE; 210 break; 211 212 case 's': 213 /* info type */ 214 break; 215#endif 216 } 217 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 218 p++; 219 if (*p != '\0') 220 *p++ = '\0'; 221 } 222 if (map->map_app != NULL) 223 map->map_app = newstr(map->map_app); 224 if (map->map_tapp != NULL) 225 map->map_tapp = newstr(map->map_tapp); 226 if (map->map_keycolnm != NULL) 227 map->map_keycolnm = newstr(map->map_keycolnm); 228 if (map->map_valcolnm != NULL) 229 map->map_valcolnm = newstr(map->map_valcolnm); 230 231 if (*p != '\0') 232 { 233 map->map_file = p; 234 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 235 p++; 236 if (*p != '\0') 237 *p++ = '\0'; 238 map->map_file = newstr(map->map_file); 239 } 240 241 while (*p != '\0' && isascii(*p) && isspace(*p)) 242 p++; 243 if (*p != '\0') 244 map->map_rebuild = newstr(p); 245 246 if (map->map_file == NULL && 247 !bitset(MCF_OPTFILE, map->map_class->map_cflags)) 248 { 249 syserr("No file name for %s map %s", 250 map->map_class->map_cname, map->map_mname); 251 return FALSE; 252 } 253 return TRUE; 254} 255/* 256** MAP_REWRITE -- rewrite a database key, interpolating %n indications. 257** 258** It also adds the map_app string. It can be used as a utility 259** in the map_lookup method. 260** 261** Parameters: 262** map -- the map that causes this. 263** s -- the string to rewrite, NOT necessarily null terminated. 264** slen -- the length of s. 265** av -- arguments to interpolate into buf. 266** 267** Returns: 268** Pointer to rewritten result. This is static data that 269** should be copied if it is to be saved! 270** 271** Side Effects: 272** none. 273*/ 274 275char * 276map_rewrite(map, s, slen, av) 277 register MAP *map; 278 register const char *s; 279 size_t slen; 280 char **av; 281{ 282 register char *bp; 283 register char c; 284 char **avp; 285 register char *ap; 286 size_t l; 287 size_t len; 288 static size_t buflen = 0; 289 static char *buf = NULL; 290 291 if (tTd(39, 1)) 292 { 293 printf("map_rewrite(%.*s), av =", (int)slen, s); 294 if (av == NULL) 295 printf(" (nullv)"); 296 else 297 { 298 for (avp = av; *avp != NULL; avp++) 299 printf("\n\t%s", *avp); 300 } 301 printf("\n"); 302 } 303 304 /* count expected size of output (can safely overestimate) */ 305 l = len = slen; 306 if (av != NULL) 307 { 308 const char *sp = s; 309 310 while (l-- > 0 && (c = *sp++) != '\0') 311 { 312 if (c != '%') 313 continue; 314 if (l-- <= 0) 315 break; 316 c = *sp++; 317 if (!(isascii(c) && isdigit(c))) 318 continue; 319 for (avp = av; --c >= '0' && *avp != NULL; avp++) 320 continue; 321 if (*avp == NULL) 322 continue; 323 len += strlen(*avp); 324 } 325 } 326 if (map->map_app != NULL) 327 len += strlen(map->map_app); 328 if (buflen < ++len) 329 { 330 /* need to malloc additional space */ 331 buflen = len; 332 if (buf != NULL) 333 free(buf); 334 buf = xalloc(buflen); 335 } 336 337 bp = buf; 338 if (av == NULL) 339 { 340 bcopy(s, bp, slen); 341 bp += slen; 342 } 343 else 344 { 345 while (slen-- > 0 && (c = *s++) != '\0') 346 { 347 if (c != '%') 348 { 349 pushc: 350 *bp++ = c; 351 continue; 352 } 353 if (slen-- <= 0 || (c = *s++) == '\0') 354 c = '%'; 355 if (c == '%') 356 goto pushc; 357 if (!(isascii(c) && isdigit(c))) 358 { 359 *bp++ = '%'; 360 goto pushc; 361 } 362 for (avp = av; --c >= '0' && *avp != NULL; avp++) 363 continue; 364 if (*avp == NULL) 365 continue; 366 367 /* transliterate argument into output string */ 368 for (ap = *avp; (c = *ap++) != '\0'; ) 369 *bp++ = c; 370 } 371 } 372 if (map->map_app != NULL) 373 strcpy(bp, map->map_app); 374 else 375 *bp = '\0'; 376 if (tTd(39, 1)) 377 printf("map_rewrite => %s\n", buf); 378 return buf; 379} 380/* 381** INITMAPS -- initialize for aliasing 382** 383** Parameters: 384** rebuild -- if TRUE, this rebuilds the cached versions. 385** e -- current envelope. 386** 387** Returns: 388** none. 389** 390** Side Effects: 391** initializes aliases: 392** if alias database: opens the database. 393** if no database available: reads aliases into the symbol table. 394*/ 395 396void 397initmaps(rebuild, e) 398 bool rebuild; 399 register ENVELOPE *e; 400{ 401 extern void map_init __P((STAB *, int)); 402 403#if XDEBUG 404 checkfd012("entering initmaps"); 405#endif 406 CurEnv = e; 407 408 stabapply(map_init, 0); 409 stabapply(map_init, rebuild ? 2 : 1); 410#if XDEBUG 411 checkfd012("exiting initmaps"); 412#endif 413} 414 415void 416map_init(s, pass) 417 register STAB *s; 418 int pass; 419{ 420 bool rebuildable; 421 register MAP *map; 422 423 /* has to be a map */ 424 if (s->s_type != ST_MAP) 425 return; 426 427 map = &s->s_map; 428 if (!bitset(MF_VALID, map->map_mflags)) 429 return; 430 431 if (tTd(38, 2)) 432 printf("map_init(%s:%s, %s, %d)\n", 433 map->map_class->map_cname == NULL ? "NULL" : 434 map->map_class->map_cname, 435 map->map_mname == NULL ? "NULL" : map->map_mname, 436 map->map_file == NULL ? "NULL" : map->map_file, 437 pass); 438 439 /* 440 ** Pass 0 opens all non-rebuildable maps. 441 ** Pass 1 opens all rebuildable maps for read. 442 ** Pass 2 rebuilds all rebuildable maps. 443 */ 444 445 rebuildable = (bitset(MF_ALIAS, map->map_mflags) && 446 bitset(MCF_REBUILDABLE, map->map_class->map_cflags)); 447 448 if ((pass == 0 && rebuildable) || 449 ((pass == 1 || pass == 2) && !rebuildable)) 450 { 451 if (tTd(38, 3)) 452 printf("\twrong pass (pass = %d, rebuildable = %d)\n", 453 pass, rebuildable); 454 return; 455 } 456 457 /* if already open, close it (for nested open) */ 458 if (bitset(MF_OPEN, map->map_mflags)) 459 { 460 map->map_class->map_close(map); 461 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 462 } 463 464 if (pass == 2) 465 { 466 (void) rebuildaliases(map, FALSE); 467 return; 468 } 469 470 if (map->map_class->map_open(map, O_RDONLY)) 471 { 472 if (tTd(38, 4)) 473 printf("\t%s:%s %s: valid\n", 474 map->map_class->map_cname == NULL ? "NULL" : 475 map->map_class->map_cname, 476 map->map_mname == NULL ? "NULL" : 477 map->map_mname, 478 map->map_file == NULL ? "NULL" : 479 map->map_file); 480 map->map_mflags |= MF_OPEN; 481 map->map_pid = getpid(); 482 } 483 else 484 { 485 if (tTd(38, 4)) 486 printf("\t%s:%s %s: invalid: %s\n", 487 map->map_class->map_cname == NULL ? "NULL" : 488 map->map_class->map_cname, 489 map->map_mname == NULL ? "NULL" : 490 map->map_mname, 491 map->map_file == NULL ? "NULL" : 492 map->map_file, 493 errstring(errno)); 494 if (!bitset(MF_OPTIONAL, map->map_mflags)) 495 { 496 extern MAPCLASS BogusMapClass; 497 498 map->map_class = &BogusMapClass; 499 map->map_mflags |= MF_OPEN; 500 map->map_pid = getpid(); 501 } 502 } 503} 504/* 505** CLOSEMAPS -- close all open maps opened by the current pid. 506** 507** Parameters: 508** none 509** 510** Returns: 511** none. 512*/ 513 514void 515closemaps() 516{ 517 extern void map_close __P((STAB *, int)); 518 519 stabapply(map_close, 0); 520} 521 522/* ARGSUSED1 */ 523void 524map_close(s, unused) 525 register STAB *s; 526 int unused; 527{ 528 MAP *map; 529 530 if (s->s_type != ST_MAP) 531 return; 532 533 map = &s->s_map; 534 535 if (!bitset(MF_VALID, map->map_mflags) || 536 !bitset(MF_OPEN, map->map_mflags) || 537 map->map_pid != getpid()) 538 return; 539 540 if (tTd(38, 5)) 541 printf("closemaps: closing %s (%s)\n", 542 map->map_mname == NULL ? "NULL" : map->map_mname, 543 map->map_file == NULL ? "NULL" : map->map_file); 544 545 map->map_class->map_close(map); 546 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 547} 548/* 549** GETCANONNAME -- look up name using service switch 550** 551** Parameters: 552** host -- the host name to look up. 553** hbsize -- the size of the host buffer. 554** trymx -- if set, try MX records. 555** 556** Returns: 557** TRUE -- if the host was found. 558** FALSE -- otherwise. 559*/ 560 561bool 562getcanonname(host, hbsize, trymx) 563 char *host; 564 int hbsize; 565 bool trymx; 566{ 567 int nmaps; 568 int mapno; 569 bool found = FALSE; 570 bool got_tempfail = FALSE; 571 auto int stat; 572 char *maptype[MAXMAPSTACK]; 573 short mapreturn[MAXMAPACTIONS]; 574 575 nmaps = switch_map_find("hosts", maptype, mapreturn); 576 for (mapno = 0; mapno < nmaps; mapno++) 577 { 578 int i; 579 580 if (tTd(38, 20)) 581 printf("getcanonname(%s), trying %s\n", 582 host, maptype[mapno]); 583 if (strcmp("files", maptype[mapno]) == 0) 584 { 585 extern bool text_getcanonname __P((char *, int, int *)); 586 587 found = text_getcanonname(host, hbsize, &stat); 588 } 589#ifdef NIS 590 else if (strcmp("nis", maptype[mapno]) == 0) 591 { 592 extern bool nis_getcanonname __P((char *, int, int *)); 593 594 found = nis_getcanonname(host, hbsize, &stat); 595 } 596#endif 597#ifdef NISPLUS 598 else if (strcmp("nisplus", maptype[mapno]) == 0) 599 { 600 extern bool nisplus_getcanonname __P((char *, int, int *)); 601 602 found = nisplus_getcanonname(host, hbsize, &stat); 603 } 604#endif 605#if NAMED_BIND 606 else if (strcmp("dns", maptype[mapno]) == 0) 607 { 608 extern bool dns_getcanonname __P((char *, int, bool, int *)); 609 610 found = dns_getcanonname(host, hbsize, trymx, &stat); 611 } 612#endif 613#if NETINFO 614 else if (strcmp("netinfo", maptype[mapno]) == 0) 615 { 616 extern bool ni_getcanonname __P((char *, int, int *)); 617 618 found = ni_getcanonname(host, hbsize, &stat); 619 } 620#endif 621 else 622 { 623 found = FALSE; 624 stat = EX_UNAVAILABLE; 625 } 626 627 /* 628 ** Heuristic: if $m is not set, we are running during system 629 ** startup. In this case, when a name is apparently found 630 ** but has no dot, treat is as not found. This avoids 631 ** problems if /etc/hosts has no FQDN but is listed first 632 ** in the service switch. 633 */ 634 635 if (found && 636 (macvalue('m', CurEnv) != NULL || strchr(host, '.') != NULL)) 637 break; 638 639 /* see if we should continue */ 640 if (stat == EX_TEMPFAIL) 641 { 642 i = MA_TRYAGAIN; 643 got_tempfail = TRUE; 644 } 645 else if (stat == EX_NOTFOUND) 646 i = MA_NOTFOUND; 647 else 648 i = MA_UNAVAIL; 649 if (bitset(1 << mapno, mapreturn[i])) 650 break; 651 } 652 653 if (found) 654 { 655 char *d; 656 657 if (tTd(38, 20)) 658 printf("getcanonname(%s), found\n", host); 659 660 /* 661 ** If returned name is still single token, compensate 662 ** by tagging on $m. This is because some sites set 663 ** up their DNS or NIS databases wrong. 664 */ 665 666 if ((d = strchr(host, '.')) == NULL || d[1] == '\0') 667 { 668 d = macvalue('m', CurEnv); 669 if (d != NULL && 670 hbsize > (int) (strlen(host) + strlen(d) + 1)) 671 { 672 if (host[strlen(host) - 1] != '.') 673 strcat(host, "."); 674 strcat(host, d); 675 } 676 else 677 { 678 return FALSE; 679 } 680 } 681 return TRUE; 682 } 683 684 if (tTd(38, 20)) 685 printf("getcanonname(%s), failed, stat=%d\n", host, stat); 686 687#if NAMED_BIND 688 if (got_tempfail) 689 h_errno = TRY_AGAIN; 690 else 691 h_errno = HOST_NOT_FOUND; 692#endif 693 694 return FALSE; 695} 696/* 697** EXTRACT_CANONNAME -- extract canonical name from /etc/hosts entry 698** 699** Parameters: 700** name -- the name against which to match. 701** line -- the /etc/hosts line. 702** cbuf -- the location to store the result. 703** cbuflen -- the size of cbuf. 704** 705** Returns: 706** TRUE -- if the line matched the desired name. 707** FALSE -- otherwise. 708*/ 709 710bool 711extract_canonname(name, line, cbuf, cbuflen) 712 char *name; 713 char *line; 714 char cbuf[]; 715 int cbuflen; 716{ 717 int i; 718 char *p; 719 bool found = FALSE; 720 extern char *get_column __P((char *, int, char, char *, int)); 721 722 cbuf[0] = '\0'; 723 if (line[0] == '#') 724 return FALSE; 725 726 for (i = 1; ; i++) 727 { 728 char nbuf[MAXNAME + 1]; 729 730 p = get_column(line, i, '\0', nbuf, sizeof nbuf); 731 if (p == NULL) 732 break; 733 if (*p == '\0') 734 continue; 735 if (cbuf[0] == '\0' || 736 (strchr(cbuf, '.') == NULL && strchr(p, '.') != NULL)) 737 { 738 snprintf(cbuf, cbuflen, "%s", p); 739 } 740 if (strcasecmp(name, p) == 0) 741 found = TRUE; 742 } 743 if (found && strchr(cbuf, '.') == NULL) 744 { 745 /* try to add a domain on the end of the name */ 746 char *domain = macvalue('m', CurEnv); 747 748 if (domain != NULL && 749 strlen(domain) + strlen(cbuf) + 1 < cbuflen) 750 { 751 p = &cbuf[strlen(cbuf)]; 752 *p++ = '.'; 753 strcpy(p, domain); 754 } 755 } 756 return found; 757} 758/* 759** NDBM modules 760*/ 761 762#ifdef NDBM 763 764/* 765** NDBM_MAP_OPEN -- DBM-style map open 766*/ 767 768bool 769ndbm_map_open(map, mode) 770 MAP *map; 771 int mode; 772{ 773 register DBM *dbm; 774 struct stat st; 775 int dfd; 776 int pfd; 777 int sff; 778 int ret; 779 int smode = S_IREAD; 780 char dirfile[MAXNAME + 1]; 781 char pagfile[MAXNAME + 1]; 782 struct stat std, stp; 783 784 if (tTd(38, 2)) 785 printf("ndbm_map_open(%s, %s, %d)\n", 786 map->map_mname, map->map_file, mode); 787 map->map_lockfd = -1; 788 mode &= O_ACCMODE; 789 790 /* do initial file and directory checks */ 791 snprintf(dirfile, sizeof dirfile, "%s.dir", map->map_file); 792 snprintf(pagfile, sizeof pagfile, "%s.pag", map->map_file); 793 sff = SFF_ROOTOK|SFF_REGONLY; 794 if (mode == O_RDWR) 795 { 796 sff |= SFF_CREAT; 797 if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 798 sff |= SFF_NOSLINK; 799 if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 800 sff |= SFF_NOHLINK; 801 smode = S_IWRITE; 802 } 803 else 804 { 805 if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 806 sff |= SFF_NOWLINK; 807 } 808 if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 809 sff |= SFF_SAFEDIRPATH; 810 ret = safefile(dirfile, RunAsUid, RunAsGid, RunAsUserName, 811 sff, smode, &std); 812 if (ret == 0) 813 ret = safefile(pagfile, RunAsUid, RunAsGid, RunAsUserName, 814 sff, smode, &stp); 815 if (ret == ENOENT && AutoRebuild && 816 bitset(MCF_REBUILDABLE, map->map_class->map_cflags) && 817 (bitset(MF_IMPL_NDBM, map->map_mflags) || 818 bitset(MF_ALIAS, map->map_mflags)) && 819 mode == O_RDONLY) 820 { 821 bool impl = bitset(MF_IMPL_NDBM, map->map_mflags); 822 extern bool impl_map_open __P((MAP *, int)); 823 824 /* may be able to rebuild */ 825 map->map_mflags &= ~MF_IMPL_NDBM; 826 if (!rebuildaliases(map, TRUE)) 827 return FALSE; 828 if (impl) 829 return impl_map_open(map, O_RDONLY); 830 else 831 return ndbm_map_open(map, O_RDONLY); 832 } 833 if (ret != 0) 834 { 835 char *prob = "unsafe"; 836 837 /* cannot open this map */ 838 if (ret == ENOENT) 839 prob = "missing"; 840 if (tTd(38, 2)) 841 printf("\t%s map file: %d\n", prob, ret); 842 if (!bitset(MF_OPTIONAL, map->map_mflags)) 843 syserr("dbm map \"%s\": %s map file %s", 844 map->map_mname, prob, map->map_file); 845 return FALSE; 846 } 847 if (std.st_mode == ST_MODE_NOFILE) 848 mode |= O_CREAT|O_EXCL; 849 850#if LOCK_ON_OPEN 851 if (mode == O_RDONLY) 852 mode |= O_SHLOCK; 853 else 854 mode |= O_TRUNC|O_EXLOCK; 855#else 856 if ((mode & O_ACCMODE) == O_RDWR) 857 { 858# if NOFTRUNCATE 859 /* 860 ** Warning: race condition. Try to lock the file as 861 ** quickly as possible after opening it. 862 ** This may also have security problems on some systems, 863 ** but there isn't anything we can do about it. 864 */ 865 866 mode |= O_TRUNC; 867# else 868 /* 869 ** This ugly code opens the map without truncating it, 870 ** locks the file, then truncates it. Necessary to 871 ** avoid race conditions. 872 */ 873 874 int dirfd; 875 int pagfd; 876 int sff = SFF_CREAT|SFF_OPENASROOT; 877 878 if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 879 sff |= SFF_NOSLINK; 880 if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 881 sff |= SFF_NOHLINK; 882 883 dirfd = safeopen(dirfile, mode, DBMMODE, sff); 884 pagfd = safeopen(pagfile, mode, DBMMODE, sff); 885 886 if (dirfd < 0 || pagfd < 0) 887 { 888 int save_errno = errno; 889 890 if (dirfd >= 0) 891 (void) close(dirfd); 892 if (pagfd >= 0) 893 (void) close(pagfd); 894 errno = save_errno; 895 syserr("ndbm_map_open: cannot create database %s", 896 map->map_file); 897 return FALSE; 898 } 899 if (ftruncate(dirfd, (off_t) 0) < 0 || 900 ftruncate(pagfd, (off_t) 0) < 0) 901 { 902 int save_errno = errno; 903 904 (void) close(dirfd); 905 (void) close(pagfd); 906 errno = save_errno; 907 syserr("ndbm_map_open: cannot truncate %s.{dir,pag}", 908 map->map_file); 909 return FALSE; 910 } 911 912 /* if new file, get "before" bits for later filechanged check */ 913 if (std.st_mode == ST_MODE_NOFILE && 914 (fstat(dirfd, &std) < 0 || fstat(pagfd, &stp) < 0)) 915 { 916 int save_errno = errno; 917 918 (void) close(dirfd); 919 (void) close(pagfd); 920 errno = save_errno; 921 syserr("ndbm_map_open(%s.{dir,pag}): cannot fstat pre-opened file", 922 map->map_file); 923 return FALSE; 924 } 925 926 /* have to save the lock for the duration (bletch) */ 927 map->map_lockfd = dirfd; 928 close(pagfd); 929 930 /* twiddle bits for dbm_open */ 931 mode &= ~(O_CREAT|O_EXCL); 932# endif 933 } 934#endif 935 936 /* open the database */ 937 dbm = dbm_open(map->map_file, mode, DBMMODE); 938 if (dbm == NULL) 939 { 940 int save_errno = errno; 941 942 if (bitset(MF_ALIAS, map->map_mflags) && 943 aliaswait(map, ".pag", FALSE)) 944 return TRUE; 945#if !LOCK_ON_OPEN && !NOFTRUNCATE 946 if (map->map_lockfd >= 0) 947 close(map->map_lockfd); 948#endif 949 errno = save_errno; 950 if (!bitset(MF_OPTIONAL, map->map_mflags)) 951 syserr("Cannot open DBM database %s", map->map_file); 952 return FALSE; 953 } 954 dfd = dbm_dirfno(dbm); 955 pfd = dbm_pagfno(dbm); 956 if (dfd == pfd) 957 { 958 /* heuristic: if files are linked, this is actually gdbm */ 959 dbm_close(dbm); 960#if !LOCK_ON_OPEN && !NOFTRUNCATE 961 if (map->map_lockfd >= 0) 962 close(map->map_lockfd); 963#endif 964 errno = 0; 965 syserr("dbm map \"%s\": cannot support GDBM", 966 map->map_mname); 967 return FALSE; 968 } 969 970 if (filechanged(dirfile, dfd, &std) || 971 filechanged(pagfile, pfd, &stp)) 972 { 973 int save_errno = errno; 974 975 dbm_close(dbm); 976#if !LOCK_ON_OPEN && !NOFTRUNCATE 977 if (map->map_lockfd >= 0) 978 close(map->map_lockfd); 979#endif 980 errno = save_errno; 981 syserr("ndbm_map_open(%s): file changed after open", 982 map->map_file); 983 return FALSE; 984 } 985 986 map->map_db1 = (ARBPTR_T) dbm; 987 if (mode == O_RDONLY) 988 { 989#if LOCK_ON_OPEN 990 if (dfd >= 0) 991 (void) lockfile(dfd, map->map_file, ".dir", LOCK_UN); 992 if (pfd >= 0) 993 (void) lockfile(pfd, map->map_file, ".pag", LOCK_UN); 994#endif 995 if (bitset(MF_ALIAS, map->map_mflags) && 996 !aliaswait(map, ".pag", TRUE)) 997 return FALSE; 998 } 999 else 1000 { 1001 map->map_mflags |= MF_LOCKED; 1002#if _FFR_TRUSTED_USER 1003 if (geteuid() == 0 && TrustedUid != 0) 1004 { 1005 if (fchown(dfd, TrustedUid, -1) < 0 || 1006 fchown(pfd, TrustedUid, -1) < 0) 1007 { 1008 int err = errno; 1009 1010 sm_syslog(LOG_ALERT, NOQID, 1011 "ownership change on %s failed: %s", 1012 map->map_file, errstring(err)); 1013 message("050 ownership change on %s failed: %s", 1014 map->map_file, errstring(err)); 1015 } 1016 } 1017#endif 1018 } 1019 if (fstat(dfd, &st) >= 0) 1020 map->map_mtime = st.st_mtime; 1021 return TRUE; 1022} 1023 1024 1025/* 1026** NDBM_MAP_LOOKUP -- look up a datum in a DBM-type map 1027*/ 1028 1029char * 1030ndbm_map_lookup(map, name, av, statp) 1031 MAP *map; 1032 char *name; 1033 char **av; 1034 int *statp; 1035{ 1036 datum key, val; 1037 int fd; 1038 char keybuf[MAXNAME + 1]; 1039 struct stat stbuf; 1040 1041 if (tTd(38, 20)) 1042 printf("ndbm_map_lookup(%s, %s)\n", 1043 map->map_mname, name); 1044 1045 key.dptr = name; 1046 key.dsize = strlen(name); 1047 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 1048 { 1049 if (key.dsize > sizeof keybuf - 1) 1050 key.dsize = sizeof keybuf - 1; 1051 bcopy(key.dptr, keybuf, key.dsize); 1052 keybuf[key.dsize] = '\0'; 1053 makelower(keybuf); 1054 key.dptr = keybuf; 1055 } 1056lockdbm: 1057 fd = dbm_dirfno((DBM *) map->map_db1); 1058 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1059 (void) lockfile(fd, map->map_file, ".dir", LOCK_SH); 1060 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime) 1061 { 1062 /* Reopen the database to sync the cache */ 1063 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR 1064 : O_RDONLY; 1065 1066 map->map_class->map_close(map); 1067 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 1068 if (map->map_class->map_open(map, omode)) 1069 { 1070 map->map_mflags |= MF_OPEN; 1071 map->map_pid = getpid(); 1072 if ((omode && O_ACCMODE) == O_RDWR) 1073 map->map_mflags |= MF_WRITABLE; 1074 goto lockdbm; 1075 } 1076 else 1077 { 1078 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1079 { 1080 extern MAPCLASS BogusMapClass; 1081 1082 *statp = EX_TEMPFAIL; 1083 map->map_class = &BogusMapClass; 1084 map->map_mflags |= MF_OPEN; 1085 map->map_pid = getpid(); 1086 syserr("Cannot reopen NDBM database %s", 1087 map->map_file); 1088 } 1089 return NULL; 1090 } 1091 } 1092 val.dptr = NULL; 1093 if (bitset(MF_TRY0NULL, map->map_mflags)) 1094 { 1095 val = dbm_fetch((DBM *) map->map_db1, key); 1096 if (val.dptr != NULL) 1097 map->map_mflags &= ~MF_TRY1NULL; 1098 } 1099 if (val.dptr == NULL && bitset(MF_TRY1NULL, map->map_mflags)) 1100 { 1101 key.dsize++; 1102 val = dbm_fetch((DBM *) map->map_db1, key); 1103 if (val.dptr != NULL) 1104 map->map_mflags &= ~MF_TRY0NULL; 1105 } 1106 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1107 (void) lockfile(fd, map->map_file, ".dir", LOCK_UN); 1108 if (val.dptr == NULL) 1109 return NULL; 1110 if (bitset(MF_MATCHONLY, map->map_mflags)) 1111 return map_rewrite(map, name, strlen(name), NULL); 1112 else 1113 return map_rewrite(map, val.dptr, val.dsize, av); 1114} 1115 1116 1117/* 1118** NDBM_MAP_STORE -- store a datum in the database 1119*/ 1120 1121void 1122ndbm_map_store(map, lhs, rhs) 1123 register MAP *map; 1124 char *lhs; 1125 char *rhs; 1126{ 1127 datum key; 1128 datum data; 1129 int stat; 1130 char keybuf[MAXNAME + 1]; 1131 1132 if (tTd(38, 12)) 1133 printf("ndbm_map_store(%s, %s, %s)\n", 1134 map->map_mname, lhs, rhs); 1135 1136 key.dsize = strlen(lhs); 1137 key.dptr = lhs; 1138 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 1139 { 1140 if (key.dsize > sizeof keybuf - 1) 1141 key.dsize = sizeof keybuf - 1; 1142 bcopy(key.dptr, keybuf, key.dsize); 1143 keybuf[key.dsize] = '\0'; 1144 makelower(keybuf); 1145 key.dptr = keybuf; 1146 } 1147 1148 data.dsize = strlen(rhs); 1149 data.dptr = rhs; 1150 1151 if (bitset(MF_INCLNULL, map->map_mflags)) 1152 { 1153 key.dsize++; 1154 data.dsize++; 1155 } 1156 1157 stat = dbm_store((DBM *) map->map_db1, key, data, DBM_INSERT); 1158 if (stat > 0) 1159 { 1160 if (!bitset(MF_APPEND, map->map_mflags)) 1161 message("050 Warning: duplicate alias name %s", lhs); 1162 else 1163 { 1164 static char *buf = NULL; 1165 static int bufsiz = 0; 1166 auto int xstat; 1167 datum old; 1168 1169 old.dptr = ndbm_map_lookup(map, key.dptr, 1170 (char **)NULL, &xstat); 1171 if (old.dptr != NULL && *(char *) old.dptr != '\0') 1172 { 1173 old.dsize = strlen(old.dptr); 1174 if (data.dsize + old.dsize + 2 > bufsiz) 1175 { 1176 if (buf != NULL) 1177 (void) free(buf); 1178 bufsiz = data.dsize + old.dsize + 2; 1179 buf = xalloc(bufsiz); 1180 } 1181 snprintf(buf, bufsiz, "%s,%s", 1182 data.dptr, old.dptr); 1183 data.dsize = data.dsize + old.dsize + 1; 1184 data.dptr = buf; 1185 if (tTd(38, 9)) 1186 printf("ndbm_map_store append=%s\n", data.dptr); 1187 } 1188 } 1189 stat = dbm_store((DBM *) map->map_db1, key, data, DBM_REPLACE); 1190 } 1191 if (stat != 0) 1192 syserr("readaliases: dbm put (%s)", lhs); 1193} 1194 1195 1196/* 1197** NDBM_MAP_CLOSE -- close the database 1198*/ 1199 1200void 1201ndbm_map_close(map) 1202 register MAP *map; 1203{ 1204 if (tTd(38, 9)) 1205 printf("ndbm_map_close(%s, %s, %lx)\n", 1206 map->map_mname, map->map_file, map->map_mflags); 1207 1208 if (bitset(MF_WRITABLE, map->map_mflags)) 1209 { 1210#ifdef NDBM_YP_COMPAT 1211 bool inclnull; 1212 char buf[MAXHOSTNAMELEN]; 1213 1214 inclnull = bitset(MF_INCLNULL, map->map_mflags); 1215 map->map_mflags &= ~MF_INCLNULL; 1216 1217 if (strstr(map->map_file, "/yp/") != NULL) 1218 { 1219 long save_mflags = map->map_mflags; 1220 1221 map->map_mflags |= MF_NOFOLDCASE; 1222 1223 (void) snprintf(buf, sizeof buf, "%010ld", curtime()); 1224 ndbm_map_store(map, "YP_LAST_MODIFIED", buf); 1225 1226 (void) gethostname(buf, sizeof buf); 1227 ndbm_map_store(map, "YP_MASTER_NAME", buf); 1228 1229 map->map_mflags = save_mflags; 1230 } 1231 1232 if (inclnull) 1233 map->map_mflags |= MF_INCLNULL; 1234#endif 1235 1236 /* write out the distinguished alias */ 1237 ndbm_map_store(map, "@", "@"); 1238 } 1239 dbm_close((DBM *) map->map_db1); 1240 1241 /* release lock (if needed) */ 1242#if !LOCK_ON_OPEN 1243 if (map->map_lockfd >= 0) 1244 (void) close(map->map_lockfd); 1245#endif 1246} 1247 1248#endif 1249/* 1250** NEWDB (Hash and BTree) Modules 1251*/ 1252 1253#ifdef NEWDB 1254 1255/* 1256** BT_MAP_OPEN, HASH_MAP_OPEN -- database open primitives. 1257** 1258** These do rather bizarre locking. If you can lock on open, 1259** do that to avoid the condition of opening a database that 1260** is being rebuilt. If you don't, we'll try to fake it, but 1261** there will be a race condition. If opening for read-only, 1262** we immediately release the lock to avoid freezing things up. 1263** We really ought to hold the lock, but guarantee that we won't 1264** be pokey about it. That's hard to do. 1265*/ 1266 1267#if DB_VERSION_MAJOR < 2 1268extern bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *)); 1269#else 1270extern bool db_map_open __P((MAP *, int, char *, DBTYPE, DB_INFO *)); 1271#endif 1272 1273/* these should be K line arguments */ 1274#if DB_VERSION_MAJOR < 2 1275# define db_cachesize cachesize 1276# define h_nelem nelem 1277# ifndef DB_CACHE_SIZE 1278# define DB_CACHE_SIZE (1024 * 1024) /* database memory cache size */ 1279# endif 1280# ifndef DB_HASH_NELEM 1281# define DB_HASH_NELEM 4096 /* (starting) size of hash table */ 1282# endif 1283#endif 1284 1285bool 1286bt_map_open(map, mode) 1287 MAP *map; 1288 int mode; 1289{ 1290#if DB_VERSION_MAJOR < 2 1291 BTREEINFO btinfo; 1292#else 1293 DB_INFO btinfo; 1294#endif 1295 1296 if (tTd(38, 2)) 1297 printf("bt_map_open(%s, %s, %d)\n", 1298 map->map_mname, map->map_file, mode); 1299 1300 bzero(&btinfo, sizeof btinfo); 1301#ifdef DB_CACHE_SIZE 1302 btinfo.db_cachesize = DB_CACHE_SIZE; 1303#endif 1304 return db_map_open(map, mode, "btree", DB_BTREE, &btinfo); 1305} 1306 1307bool 1308hash_map_open(map, mode) 1309 MAP *map; 1310 int mode; 1311{ 1312#if DB_VERSION_MAJOR < 2 1313 HASHINFO hinfo; 1314#else 1315 DB_INFO hinfo; 1316#endif 1317 1318 if (tTd(38, 2)) 1319 printf("hash_map_open(%s, %s, %d)\n", 1320 map->map_mname, map->map_file, mode); 1321 1322 bzero(&hinfo, sizeof hinfo); 1323#ifdef DB_HASH_NELEM 1324 hinfo.h_nelem = DB_HASH_NELEM; 1325#endif 1326#ifdef DB_CACHE_SIZE 1327 hinfo.db_cachesize = DB_CACHE_SIZE; 1328#endif 1329 return db_map_open(map, mode, "hash", DB_HASH, &hinfo); 1330} 1331 1332bool 1333db_map_open(map, mode, mapclassname, dbtype, openinfo) 1334 MAP *map; 1335 int mode; 1336 char *mapclassname; 1337 DBTYPE dbtype; 1338#if DB_VERSION_MAJOR < 2 1339 const void *openinfo; 1340#else 1341 DB_INFO *openinfo; 1342#endif 1343{ 1344 DB *db = NULL; 1345 int i; 1346 int omode; 1347 int smode = S_IREAD; 1348 int fd; 1349 int sff; 1350 int saveerrno; 1351 struct stat st; 1352 char buf[MAXNAME + 1]; 1353 1354 /* do initial file and directory checks */ 1355 snprintf(buf, sizeof buf - 3, "%s", map->map_file); 1356 i = strlen(buf); 1357 if (i < 3 || strcmp(&buf[i - 3], ".db") != 0) 1358 (void) strcat(buf, ".db"); 1359 1360 mode &= O_ACCMODE; 1361 omode = mode; 1362 1363 sff = SFF_ROOTOK|SFF_REGONLY; 1364 if (mode == O_RDWR) 1365 { 1366 sff |= SFF_CREAT; 1367 if (!bitset(DBS_WRITEMAPTOSYMLINK, DontBlameSendmail)) 1368 sff |= SFF_NOSLINK; 1369 if (!bitset(DBS_WRITEMAPTOHARDLINK, DontBlameSendmail)) 1370 sff |= SFF_NOHLINK; 1371 smode = S_IWRITE; 1372 } 1373 else 1374 { 1375 if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 1376 sff |= SFF_NOWLINK; 1377 } 1378 if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 1379 sff |= SFF_SAFEDIRPATH; 1380 i = safefile(buf, RunAsUid, RunAsGid, RunAsUserName, sff, smode, &st); 1381 if (i == ENOENT && AutoRebuild && 1382 bitset(MCF_REBUILDABLE, map->map_class->map_cflags) && 1383 (bitset(MF_IMPL_HASH, map->map_mflags) || 1384 bitset(MF_ALIAS, map->map_mflags)) && 1385 mode == O_RDONLY) 1386 { 1387 bool impl = bitset(MF_IMPL_HASH, map->map_mflags); 1388 extern bool impl_map_open __P((MAP *, int)); 1389 1390 /* may be able to rebuild */ 1391 map->map_mflags &= ~MF_IMPL_HASH; 1392 if (!rebuildaliases(map, TRUE)) 1393 return FALSE; 1394 if (impl) 1395 return impl_map_open(map, O_RDONLY); 1396 else 1397 return db_map_open(map, O_RDONLY, mapclassname, 1398 dbtype, openinfo); 1399 } 1400 1401 if (i != 0) 1402 { 1403 char *prob = "unsafe"; 1404 1405 /* cannot open this map */ 1406 if (i == ENOENT) 1407 prob = "missing"; 1408 if (tTd(38, 2)) 1409 printf("\t%s map file: %s\n", prob, errstring(i)); 1410 errno = i; 1411 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1412 syserr("%s map \"%s\": %s map file %s", 1413 mapclassname, map->map_mname, prob, buf); 1414 return FALSE; 1415 } 1416 if (st.st_mode == ST_MODE_NOFILE) 1417 omode |= O_CREAT|O_EXCL; 1418 1419 map->map_lockfd = -1; 1420 1421#if LOCK_ON_OPEN 1422 if (mode == O_RDWR) 1423 omode |= O_TRUNC|O_EXLOCK; 1424 else 1425 omode |= O_SHLOCK; 1426#else 1427 /* 1428 ** Pre-lock the file to avoid race conditions. In particular, 1429 ** since dbopen returns NULL if the file is zero length, we 1430 ** must have a locked instance around the dbopen. 1431 */ 1432 1433 fd = open(buf, omode, DBMMODE); 1434 if (fd < 0) 1435 { 1436 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1437 syserr("db_map_open: cannot pre-open database %s", buf); 1438 return FALSE; 1439 } 1440 1441 /* make sure no baddies slipped in just before the open... */ 1442 if (filechanged(buf, fd, &st)) 1443 { 1444 int save_errno = errno; 1445 1446 (void) close(fd); 1447 errno = save_errno; 1448 syserr("db_map_open(%s): file changed after pre-open", buf); 1449 return FALSE; 1450 } 1451 1452 /* if new file, get the "before" bits for later filechanged check */ 1453 if (st.st_mode == ST_MODE_NOFILE && fstat(fd, &st) < 0) 1454 { 1455 int save_errno = errno; 1456 1457 (void) close(fd); 1458 errno = save_errno; 1459 syserr("db_map_open(%s): cannot fstat pre-opened file", 1460 buf); 1461 return FALSE; 1462 } 1463 1464 /* actually lock the pre-opened file */ 1465 if (!lockfile(fd, buf, NULL, mode == O_RDONLY ? LOCK_SH : LOCK_EX)) 1466 syserr("db_map_open: cannot lock %s", buf); 1467 1468 /* set up mode bits for dbopen */ 1469 if (mode == O_RDWR) 1470 omode |= O_TRUNC; 1471 omode &= ~(O_EXCL|O_CREAT); 1472#endif 1473 1474#if DB_VERSION_MAJOR < 2 1475 db = dbopen(buf, omode, DBMMODE, dbtype, openinfo); 1476#else 1477 { 1478 int flags = 0; 1479 1480 if (mode == O_RDONLY) 1481 flags |= DB_RDONLY; 1482 if (bitset(O_CREAT, omode)) 1483 flags |= DB_CREATE; 1484 if (bitset(O_TRUNC, omode)) 1485 flags |= DB_TRUNCATE; 1486 1487 errno = db_open(buf, dbtype, flags, DBMMODE, 1488 NULL, openinfo, &db); 1489 } 1490#endif 1491 saveerrno = errno; 1492 1493#if !LOCK_ON_OPEN 1494 if (mode == O_RDWR) 1495 map->map_lockfd = fd; 1496 else 1497 (void) close(fd); 1498#endif 1499 1500 if (db == NULL) 1501 { 1502 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && 1503 aliaswait(map, ".db", FALSE)) 1504 return TRUE; 1505#if !LOCK_ON_OPEN 1506 if (map->map_lockfd >= 0) 1507 (void) close(map->map_lockfd); 1508#endif 1509 errno = saveerrno; 1510 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1511 syserr("Cannot open %s database %s", 1512 mapclassname, buf); 1513 return FALSE; 1514 } 1515 1516#if DB_VERSION_MAJOR < 2 1517 fd = db->fd(db); 1518#else 1519 fd = -1; 1520 errno = db->fd(db, &fd); 1521#endif 1522 if (filechanged(buf, fd, &st)) 1523 { 1524 int save_errno = errno; 1525 1526#if DB_VERSION_MAJOR < 2 1527 db->close(db); 1528#else 1529 errno = db->close(db, 0); 1530#endif 1531#if !LOCK_ON_OPEN 1532 if (map->map_lockfd >= 0) 1533 close(map->map_lockfd); 1534#endif 1535 errno = save_errno; 1536 syserr("db_map_open(%s): file changed after open", buf); 1537 return FALSE; 1538 } 1539 1540 if (mode == O_RDWR) 1541 map->map_mflags |= MF_LOCKED; 1542#if LOCK_ON_OPEN 1543 if (fd >= 0 && mode == O_RDONLY) 1544 { 1545 (void) lockfile(fd, buf, NULL, LOCK_UN); 1546 } 1547#endif 1548 1549 /* try to make sure that at least the database header is on disk */ 1550 if (mode == O_RDWR) 1551 { 1552 (void) db->sync(db, 0); 1553#if _FFR_TRUSTED_USER 1554 if (geteuid() == 0 && TrustedUid != 0) 1555 { 1556 if (fchown(fd, TrustedUid, -1) < 0) 1557 { 1558 int err = errno; 1559 1560 sm_syslog(LOG_ALERT, NOQID, 1561 "ownership change on %s failed: %s", 1562 buf, errstring(err)); 1563 message("050 ownership change on %s failed: %s", 1564 buf, errstring(err)); 1565 } 1566 } 1567#endif 1568 } 1569 1570 if (fd >= 0 && fstat(fd, &st) >= 0) 1571 map->map_mtime = st.st_mtime; 1572 1573 map->map_db2 = (ARBPTR_T) db; 1574 if (mode == O_RDONLY && bitset(MF_ALIAS, map->map_mflags) && 1575 !aliaswait(map, ".db", TRUE)) 1576 return FALSE; 1577 return TRUE; 1578} 1579 1580 1581/* 1582** DB_MAP_LOOKUP -- look up a datum in a BTREE- or HASH-type map 1583*/ 1584 1585char * 1586db_map_lookup(map, name, av, statp) 1587 MAP *map; 1588 char *name; 1589 char **av; 1590 int *statp; 1591{ 1592 DBT key, val; 1593 register DB *db = (DB *) map->map_db2; 1594 int i; 1595 int st; 1596 int saveerrno; 1597 int fd; 1598 struct stat stbuf; 1599 char keybuf[MAXNAME + 1]; 1600 char buf[MAXNAME + 1]; 1601 1602 bzero(&key, sizeof key); 1603 bzero(&val, sizeof val); 1604 1605 if (tTd(38, 20)) 1606 printf("db_map_lookup(%s, %s)\n", 1607 map->map_mname, name); 1608 1609 i = strlen(map->map_file); 1610 if (i > MAXNAME) 1611 i = MAXNAME; 1612 strncpy(buf, map->map_file, i); 1613 buf[i] = '\0'; 1614 if (i > 3 && strcmp(&buf[i - 3], ".db") == 0) 1615 buf[i - 3] = '\0'; 1616 1617 key.size = strlen(name); 1618 if (key.size > sizeof keybuf - 1) 1619 key.size = sizeof keybuf - 1; 1620 key.data = keybuf; 1621 bcopy(name, keybuf, key.size); 1622 keybuf[key.size] = '\0'; 1623 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 1624 makelower(keybuf); 1625 lockdb: 1626#if DB_VERSION_MAJOR < 2 1627 fd = db->fd(db); 1628#else 1629 fd = -1; 1630 errno = db->fd(db, &fd); 1631#endif 1632 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1633 (void) lockfile(fd, buf, ".db", LOCK_SH); 1634 if (fd < 0 || fstat(fd, &stbuf) < 0 || stbuf.st_mtime > map->map_mtime) 1635 { 1636 /* Reopen the database to sync the cache */ 1637 int omode = bitset(map->map_mflags, MF_WRITABLE) ? O_RDWR 1638 : O_RDONLY; 1639 1640 map->map_class->map_close(map); 1641 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 1642 if (map->map_class->map_open(map, omode)) 1643 { 1644 map->map_mflags |= MF_OPEN; 1645 map->map_pid = getpid(); 1646 if ((omode && O_ACCMODE) == O_RDWR) 1647 map->map_mflags |= MF_WRITABLE; 1648 db = (DB *) map->map_db2; 1649 goto lockdb; 1650 } 1651 else 1652 { 1653 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1654 { 1655 extern MAPCLASS BogusMapClass; 1656 1657 *statp = EX_TEMPFAIL; 1658 map->map_class = &BogusMapClass; 1659 map->map_mflags |= MF_OPEN; 1660 map->map_pid = getpid(); 1661 syserr("Cannot reopen DB database %s", 1662 map->map_file); 1663 } 1664 return NULL; 1665 } 1666 } 1667 1668 st = 1; 1669 if (bitset(MF_TRY0NULL, map->map_mflags)) 1670 { 1671#if DB_VERSION_MAJOR < 2 1672 st = db->get(db, &key, &val, 0); 1673#else 1674 errno = db->get(db, NULL, &key, &val, 0); 1675 switch (errno) 1676 { 1677 case DB_NOTFOUND: 1678 case DB_KEYEMPTY: 1679 st = 1; 1680 break; 1681 1682 case 0: 1683 st = 0; 1684 break; 1685 1686 default: 1687 st = -1; 1688 break; 1689 } 1690#endif 1691 if (st == 0) 1692 map->map_mflags &= ~MF_TRY1NULL; 1693 } 1694 if (st != 0 && bitset(MF_TRY1NULL, map->map_mflags)) 1695 { 1696 key.size++; 1697#if DB_VERSION_MAJOR < 2 1698 st = db->get(db, &key, &val, 0); 1699#else 1700 errno = db->get(db, NULL, &key, &val, 0); 1701 switch (errno) 1702 { 1703 case DB_NOTFOUND: 1704 case DB_KEYEMPTY: 1705 st = 1; 1706 break; 1707 1708 case 0: 1709 st = 0; 1710 break; 1711 1712 default: 1713 st = -1; 1714 break; 1715 } 1716#endif 1717 if (st == 0) 1718 map->map_mflags &= ~MF_TRY0NULL; 1719 } 1720 saveerrno = errno; 1721 if (fd >= 0 && !bitset(MF_LOCKED, map->map_mflags)) 1722 (void) lockfile(fd, buf, ".db", LOCK_UN); 1723 if (st != 0) 1724 { 1725 errno = saveerrno; 1726 if (st < 0) 1727 syserr("db_map_lookup: get (%s)", name); 1728 return NULL; 1729 } 1730 if (bitset(MF_MATCHONLY, map->map_mflags)) 1731 return map_rewrite(map, name, strlen(name), NULL); 1732 else 1733 return map_rewrite(map, val.data, val.size, av); 1734} 1735 1736 1737/* 1738** DB_MAP_STORE -- store a datum in the NEWDB database 1739*/ 1740 1741void 1742db_map_store(map, lhs, rhs) 1743 register MAP *map; 1744 char *lhs; 1745 char *rhs; 1746{ 1747 int stat; 1748 DBT key; 1749 DBT data; 1750 register DB *db = map->map_db2; 1751 char keybuf[MAXNAME + 1]; 1752 1753 bzero(&key, sizeof key); 1754 bzero(&data, sizeof data); 1755 1756 if (tTd(38, 12)) 1757 printf("db_map_store(%s, %s, %s)\n", 1758 map->map_mname, lhs, rhs); 1759 1760 key.size = strlen(lhs); 1761 key.data = lhs; 1762 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 1763 { 1764 if (key.size > sizeof keybuf - 1) 1765 key.size = sizeof keybuf - 1; 1766 bcopy(key.data, keybuf, key.size); 1767 keybuf[key.size] = '\0'; 1768 makelower(keybuf); 1769 key.data = keybuf; 1770 } 1771 1772 data.size = strlen(rhs); 1773 data.data = rhs; 1774 1775 if (bitset(MF_INCLNULL, map->map_mflags)) 1776 { 1777 key.size++; 1778 data.size++; 1779 } 1780 1781#if DB_VERSION_MAJOR < 2 1782 stat = db->put(db, &key, &data, R_NOOVERWRITE); 1783#else 1784 errno = db->put(db, NULL, &key, &data, DB_NOOVERWRITE); 1785 switch (errno) 1786 { 1787 case DB_KEYEXIST: 1788 stat = 1; 1789 break; 1790 1791 case 0: 1792 stat = 0; 1793 break; 1794 1795 default: 1796 stat = -1; 1797 break; 1798 } 1799#endif 1800 if (stat > 0) 1801 { 1802 if (!bitset(MF_APPEND, map->map_mflags)) 1803 message("050 Warning: duplicate alias name %s", lhs); 1804 else 1805 { 1806 static char *buf = NULL; 1807 static int bufsiz = 0; 1808 DBT old; 1809 1810 bzero(&old, sizeof old); 1811 1812 old.data = db_map_lookup(map, key.data, 1813 (char **)NULL, &stat); 1814 if (old.data != NULL) 1815 { 1816 old.size = strlen(old.data); 1817 if (data.size + old.size + 2 > bufsiz) 1818 { 1819 if (buf != NULL) 1820 (void) free(buf); 1821 bufsiz = data.size + old.size + 2; 1822 buf = xalloc(bufsiz); 1823 } 1824 snprintf(buf, bufsiz, "%s,%s", 1825 (char *) data.data, (char *) old.data); 1826 data.size = data.size + old.size + 1; 1827 data.data = buf; 1828 if (tTd(38, 9)) 1829 printf("db_map_store append=%s\n", 1830 (char *) data.data); 1831 } 1832 } 1833#if DB_VERSION_MAJOR < 2 1834 stat = db->put(db, &key, &data, 0); 1835#else 1836 stat = errno = db->put(db, NULL, &key, &data, 0); 1837#endif 1838 } 1839 if (stat != 0) 1840 syserr("readaliases: db put (%s)", lhs); 1841} 1842 1843 1844/* 1845** DB_MAP_CLOSE -- add distinguished entries and close the database 1846*/ 1847 1848void 1849db_map_close(map) 1850 MAP *map; 1851{ 1852 register DB *db = map->map_db2; 1853 1854 if (tTd(38, 9)) 1855 printf("db_map_close(%s, %s, %lx)\n", 1856 map->map_mname, map->map_file, map->map_mflags); 1857 1858 if (bitset(MF_WRITABLE, map->map_mflags)) 1859 { 1860 /* write out the distinguished alias */ 1861 db_map_store(map, "@", "@"); 1862 } 1863 1864 (void) db->sync(db, 0); 1865 1866#if !LOCK_ON_OPEN 1867 if (map->map_lockfd >= 0) 1868 (void) close(map->map_lockfd); 1869#endif 1870 1871#if DB_VERSION_MAJOR < 2 1872 if (db->close(db) != 0) 1873#else 1874 /* 1875 ** Berkeley DB can use internal shared memory 1876 ** locking for its memory pool. Closing a map 1877 ** opened by another process will interfere 1878 ** with the shared memory and locks of the parent 1879 ** process leaving things in a bad state. 1880 ** 1881 ** If this map was not opened by the current 1882 ** process, do not close it here but recover 1883 ** the file descriptor. 1884 */ 1885 if (map->map_pid != getpid()) 1886 { 1887 int fd = -1; 1888 1889 errno = db->fd(db, &fd); 1890 if (fd >= 0) 1891 (void) close(fd); 1892 return; 1893 } 1894 1895 if ((errno = db->close(db, 0)) != 0) 1896#endif 1897 syserr("db_map_close(%s, %s, %lx): db close failure", 1898 map->map_mname, map->map_file, map->map_mflags); 1899} 1900 1901#endif 1902/* 1903** NIS Modules 1904*/ 1905 1906# ifdef NIS 1907 1908# ifndef YPERR_BUSY 1909# define YPERR_BUSY 16 1910# endif 1911 1912/* 1913** NIS_MAP_OPEN -- open DBM map 1914*/ 1915 1916bool 1917nis_map_open(map, mode) 1918 MAP *map; 1919 int mode; 1920{ 1921 int yperr; 1922 register char *p; 1923 auto char *vp; 1924 auto int vsize; 1925 1926 if (tTd(38, 2)) 1927 printf("nis_map_open(%s, %s, %d)\n", 1928 map->map_mname, map->map_file, mode); 1929 1930 mode &= O_ACCMODE; 1931 if (mode != O_RDONLY) 1932 { 1933 /* issue a pseudo-error message */ 1934#ifdef ENOSYS 1935 errno = ENOSYS; 1936#else 1937# ifdef EFTYPE 1938 errno = EFTYPE; 1939# else 1940 errno = ENXIO; 1941# endif 1942#endif 1943 return FALSE; 1944 } 1945 1946 p = strchr(map->map_file, '@'); 1947 if (p != NULL) 1948 { 1949 *p++ = '\0'; 1950 if (*p != '\0') 1951 map->map_domain = p; 1952 } 1953 1954 if (*map->map_file == '\0') 1955 map->map_file = "mail.aliases"; 1956 1957 if (map->map_domain == NULL) 1958 { 1959 yperr = yp_get_default_domain(&map->map_domain); 1960 if (yperr != 0) 1961 { 1962 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1963 syserr("421 NIS map %s specified, but NIS not running", 1964 map->map_file); 1965 return FALSE; 1966 } 1967 } 1968 1969 /* check to see if this map actually exists */ 1970 yperr = yp_match(map->map_domain, map->map_file, "@", 1, 1971 &vp, &vsize); 1972 if (tTd(38, 10)) 1973 printf("nis_map_open: yp_match(@, %s, %s) => %s\n", 1974 map->map_domain, map->map_file, yperr_string(yperr)); 1975 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY) 1976 { 1977 /* 1978 ** We ought to be calling aliaswait() here if this is an 1979 ** alias file, but powerful HP-UX NIS servers apparently 1980 ** don't insert the @:@ token into the alias map when it 1981 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX. 1982 */ 1983 1984#if 0 1985 if (!bitset(MF_ALIAS, map->map_mflags) || 1986 aliaswait(map, NULL, TRUE)) 1987#endif 1988 return TRUE; 1989 } 1990 1991 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1992 { 1993 syserr("421 Cannot bind to map %s in domain %s: %s", 1994 map->map_file, map->map_domain, yperr_string(yperr)); 1995 } 1996 1997 return FALSE; 1998} 1999 2000 2001/* 2002** NIS_MAP_LOOKUP -- look up a datum in a NIS map 2003*/ 2004 2005/* ARGSUSED3 */ 2006char * 2007nis_map_lookup(map, name, av, statp) 2008 MAP *map; 2009 char *name; 2010 char **av; 2011 int *statp; 2012{ 2013 char *vp; 2014 auto int vsize; 2015 int buflen; 2016 int yperr; 2017 char keybuf[MAXNAME + 1]; 2018 2019 if (tTd(38, 20)) 2020 printf("nis_map_lookup(%s, %s)\n", 2021 map->map_mname, name); 2022 2023 buflen = strlen(name); 2024 if (buflen > sizeof keybuf - 1) 2025 buflen = sizeof keybuf - 1; 2026 bcopy(name, keybuf, buflen); 2027 keybuf[buflen] = '\0'; 2028 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2029 makelower(keybuf); 2030 yperr = YPERR_KEY; 2031 if (bitset(MF_TRY0NULL, map->map_mflags)) 2032 { 2033 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, 2034 &vp, &vsize); 2035 if (yperr == 0) 2036 map->map_mflags &= ~MF_TRY1NULL; 2037 } 2038 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags)) 2039 { 2040 buflen++; 2041 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, 2042 &vp, &vsize); 2043 if (yperr == 0) 2044 map->map_mflags &= ~MF_TRY0NULL; 2045 } 2046 if (yperr != 0) 2047 { 2048 if (yperr != YPERR_KEY && yperr != YPERR_BUSY) 2049 map->map_mflags &= ~(MF_VALID|MF_OPEN); 2050 return NULL; 2051 } 2052 if (bitset(MF_MATCHONLY, map->map_mflags)) 2053 return map_rewrite(map, name, strlen(name), NULL); 2054 else 2055 return map_rewrite(map, vp, vsize, av); 2056} 2057 2058 2059/* 2060** NIS_GETCANONNAME -- look up canonical name in NIS 2061*/ 2062 2063bool 2064nis_getcanonname(name, hbsize, statp) 2065 char *name; 2066 int hbsize; 2067 int *statp; 2068{ 2069 char *vp; 2070 auto int vsize; 2071 int keylen; 2072 int yperr; 2073 static bool try0null = TRUE; 2074 static bool try1null = TRUE; 2075 static char *yp_domain = NULL; 2076 char host_record[MAXLINE]; 2077 char cbuf[MAXNAME]; 2078 char nbuf[MAXNAME + 1]; 2079 2080 if (tTd(38, 20)) 2081 printf("nis_getcanonname(%s)\n", name); 2082 2083 if (strlen(name) >= sizeof nbuf) 2084 { 2085 *statp = EX_UNAVAILABLE; 2086 return FALSE; 2087 } 2088 (void) strcpy(nbuf, name); 2089 shorten_hostname(nbuf); 2090 keylen = strlen(nbuf); 2091 2092 if (yp_domain == NULL) 2093 yp_get_default_domain(&yp_domain); 2094 makelower(nbuf); 2095 yperr = YPERR_KEY; 2096 if (try0null) 2097 { 2098 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, 2099 &vp, &vsize); 2100 if (yperr == 0) 2101 try1null = FALSE; 2102 } 2103 if (yperr == YPERR_KEY && try1null) 2104 { 2105 keylen++; 2106 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, 2107 &vp, &vsize); 2108 if (yperr == 0) 2109 try0null = FALSE; 2110 } 2111 if (yperr != 0) 2112 { 2113 if (yperr == YPERR_KEY) 2114 *statp = EX_NOHOST; 2115 else if (yperr == YPERR_BUSY) 2116 *statp = EX_TEMPFAIL; 2117 else 2118 *statp = EX_UNAVAILABLE; 2119 return FALSE; 2120 } 2121 if (vsize >= sizeof host_record) 2122 vsize = sizeof host_record - 1; 2123 strncpy(host_record, vp, vsize); 2124 host_record[vsize] = '\0'; 2125 if (tTd(38, 44)) 2126 printf("got record `%s'\n", host_record); 2127 if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf)) 2128 { 2129 /* this should not happen, but.... */ 2130 *statp = EX_NOHOST; 2131 return FALSE; 2132 } 2133 if (hbsize < strlen(cbuf)) 2134 { 2135 *statp = EX_UNAVAILABLE; 2136 return FALSE; 2137 } 2138 strcpy(name, cbuf); 2139 *statp = EX_OK; 2140 return TRUE; 2141} 2142 2143#endif 2144/* 2145** NISPLUS Modules 2146** 2147** This code donated by Sun Microsystems. 2148*/ 2149 2150#ifdef NISPLUS 2151 2152#undef NIS /* symbol conflict in nis.h */ 2153#undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */ 2154#include <rpcsvc/nis.h> 2155#include <rpcsvc/nislib.h> 2156 2157#define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val 2158#define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name 2159#define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len) 2160#define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.') 2161 2162/* 2163** NISPLUS_MAP_OPEN -- open nisplus table 2164*/ 2165 2166bool 2167nisplus_map_open(map, mode) 2168 MAP *map; 2169 int mode; 2170{ 2171 nis_result *res = NULL; 2172 int retry_cnt, max_col, i; 2173 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 2174 2175 if (tTd(38, 2)) 2176 printf("nisplus_map_open(%s, %s, %d)\n", 2177 map->map_mname, map->map_file, mode); 2178 2179 mode &= O_ACCMODE; 2180 if (mode != O_RDONLY) 2181 { 2182 errno = EPERM; 2183 return FALSE; 2184 } 2185 2186 if (*map->map_file == '\0') 2187 map->map_file = "mail_aliases.org_dir"; 2188 2189 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL) 2190 { 2191 /* set default NISPLUS Domain to $m */ 2192 extern char *nisplus_default_domain __P((void)); 2193 2194 map->map_domain = newstr(nisplus_default_domain()); 2195 if (tTd(38, 2)) 2196 printf("nisplus_map_open(%s): using domain %s\n", 2197 map->map_file, map->map_domain); 2198 } 2199 if (!PARTIAL_NAME(map->map_file)) 2200 { 2201 map->map_domain = newstr(""); 2202 snprintf(qbuf, sizeof qbuf, "%s", map->map_file); 2203 } 2204 else 2205 { 2206 /* check to see if this map actually exists */ 2207 snprintf(qbuf, sizeof qbuf, "%s.%s", 2208 map->map_file, map->map_domain); 2209 } 2210 2211 retry_cnt = 0; 2212 while (res == NULL || res->status != NIS_SUCCESS) 2213 { 2214 res = nis_lookup(qbuf, FOLLOW_LINKS); 2215 switch (res->status) 2216 { 2217 case NIS_SUCCESS: 2218 break; 2219 2220 case NIS_TRYAGAIN: 2221 case NIS_RPCERROR: 2222 case NIS_NAMEUNREACHABLE: 2223 if (retry_cnt++ > 4) 2224 { 2225 errno = EAGAIN; 2226 return FALSE; 2227 } 2228 /* try not to overwhelm hosed server */ 2229 sleep(2); 2230 break; 2231 2232 default: /* all other nisplus errors */ 2233#if 0 2234 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2235 syserr("421 Cannot find table %s.%s: %s", 2236 map->map_file, map->map_domain, 2237 nis_sperrno(res->status)); 2238#endif 2239 errno = EAGAIN; 2240 return FALSE; 2241 } 2242 } 2243 2244 if (NIS_RES_NUMOBJ(res) != 1 || 2245 (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ)) 2246 { 2247 if (tTd(38, 10)) 2248 printf("nisplus_map_open: %s is not a table\n", qbuf); 2249#if 0 2250 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2251 syserr("421 %s.%s: %s is not a table", 2252 map->map_file, map->map_domain, 2253 nis_sperrno(res->status)); 2254#endif 2255 errno = EBADF; 2256 return FALSE; 2257 } 2258 /* default key column is column 0 */ 2259 if (map->map_keycolnm == NULL) 2260 map->map_keycolnm = newstr(COL_NAME(res,0)); 2261 2262 max_col = COL_MAX(res); 2263 2264 /* verify the key column exist */ 2265 for (i=0; i< max_col; i++) 2266 { 2267 if (!strcmp(map->map_keycolnm, COL_NAME(res,i))) 2268 break; 2269 } 2270 if (i == max_col) 2271 { 2272 if (tTd(38, 2)) 2273 printf("nisplus_map_open(%s): can not find key column %s\n", 2274 map->map_file, map->map_keycolnm); 2275 errno = ENOENT; 2276 return FALSE; 2277 } 2278 2279 /* default value column is the last column */ 2280 if (map->map_valcolnm == NULL) 2281 { 2282 map->map_valcolno = max_col - 1; 2283 return TRUE; 2284 } 2285 2286 for (i=0; i< max_col; i++) 2287 { 2288 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0) 2289 { 2290 map->map_valcolno = i; 2291 return TRUE; 2292 } 2293 } 2294 2295 if (tTd(38, 2)) 2296 printf("nisplus_map_open(%s): can not find column %s\n", 2297 map->map_file, map->map_keycolnm); 2298 errno = ENOENT; 2299 return FALSE; 2300} 2301 2302 2303/* 2304** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table 2305*/ 2306 2307char * 2308nisplus_map_lookup(map, name, av, statp) 2309 MAP *map; 2310 char *name; 2311 char **av; 2312 int *statp; 2313{ 2314 char *p; 2315 auto int vsize; 2316 char *skp; 2317 int skleft; 2318 char search_key[MAXNAME + 4]; 2319 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 2320 nis_result *result; 2321 2322 if (tTd(38, 20)) 2323 printf("nisplus_map_lookup(%s, %s)\n", 2324 map->map_mname, name); 2325 2326 if (!bitset(MF_OPEN, map->map_mflags)) 2327 { 2328 if (nisplus_map_open(map, O_RDONLY)) 2329 { 2330 map->map_mflags |= MF_OPEN; 2331 map->map_pid = getpid(); 2332 } 2333 else 2334 { 2335 *statp = EX_UNAVAILABLE; 2336 return NULL; 2337 } 2338 } 2339 2340 /* 2341 ** Copy the name to the key buffer, escaping double quote characters 2342 ** by doubling them and quoting "]" and "," to avoid having the 2343 ** NIS+ parser choke on them. 2344 */ 2345 2346 skleft = sizeof search_key - 4; 2347 skp = search_key; 2348 for (p = name; *p != '\0' && skleft > 0; p++) 2349 { 2350 switch (*p) 2351 { 2352 case ']': 2353 case ',': 2354 /* quote the character */ 2355 *skp++ = '"'; 2356 *skp++ = *p; 2357 *skp++ = '"'; 2358 skleft -= 3; 2359 break; 2360 2361 case '"': 2362 /* double the quote */ 2363 *skp++ = '"'; 2364 skleft--; 2365 /* fall through... */ 2366 2367 default: 2368 *skp++ = *p; 2369 skleft--; 2370 break; 2371 } 2372 } 2373 *skp = '\0'; 2374 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2375 makelower(search_key); 2376 2377 /* construct the query */ 2378 if (PARTIAL_NAME(map->map_file)) 2379 snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s", 2380 map->map_keycolnm, search_key, map->map_file, 2381 map->map_domain); 2382 else 2383 snprintf(qbuf, sizeof qbuf, "[%s=%s],%s", 2384 map->map_keycolnm, search_key, map->map_file); 2385 2386 if (tTd(38, 20)) 2387 printf("qbuf=%s\n", qbuf); 2388 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL); 2389 if (result->status == NIS_SUCCESS) 2390 { 2391 int count; 2392 char *str; 2393 2394 if ((count = NIS_RES_NUMOBJ(result)) != 1) 2395 { 2396 if (LogLevel > 10) 2397 sm_syslog(LOG_WARNING, CurEnv->e_id, 2398 "%s: lookup error, expected 1 entry, got %d", 2399 map->map_file, count); 2400 2401 /* ignore second entry */ 2402 if (tTd(38, 20)) 2403 printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n", 2404 name, count); 2405 } 2406 2407 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno)); 2408 /* set the length of the result */ 2409 if (p == NULL) 2410 p = ""; 2411 vsize = strlen(p); 2412 if (tTd(38, 20)) 2413 printf("nisplus_map_lookup(%s), found %s\n", 2414 name, p); 2415 if (bitset(MF_MATCHONLY, map->map_mflags)) 2416 str = map_rewrite(map, name, strlen(name), NULL); 2417 else 2418 str = map_rewrite(map, p, vsize, av); 2419 nis_freeresult(result); 2420 *statp = EX_OK; 2421 return str; 2422 } 2423 else 2424 { 2425 if (result->status == NIS_NOTFOUND) 2426 *statp = EX_NOTFOUND; 2427 else if (result->status == NIS_TRYAGAIN) 2428 *statp = EX_TEMPFAIL; 2429 else 2430 { 2431 *statp = EX_UNAVAILABLE; 2432 map->map_mflags &= ~(MF_VALID|MF_OPEN); 2433 } 2434 } 2435 if (tTd(38, 20)) 2436 printf("nisplus_map_lookup(%s), failed\n", name); 2437 nis_freeresult(result); 2438 return NULL; 2439} 2440 2441 2442 2443/* 2444** NISPLUS_GETCANONNAME -- look up canonical name in NIS+ 2445*/ 2446 2447bool 2448nisplus_getcanonname(name, hbsize, statp) 2449 char *name; 2450 int hbsize; 2451 int *statp; 2452{ 2453 char *vp; 2454 auto int vsize; 2455 nis_result *result; 2456 char *p; 2457 char nbuf[MAXNAME + 1]; 2458 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 2459 2460 if (strlen(name) >= sizeof nbuf) 2461 { 2462 *statp = EX_UNAVAILABLE; 2463 return FALSE; 2464 } 2465 (void) strcpy(nbuf, name); 2466 shorten_hostname(nbuf); 2467 2468 p = strchr(nbuf, '.'); 2469 if (p == NULL) 2470 { 2471 /* single token */ 2472 snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf); 2473 } 2474 else if (p[1] != '\0') 2475 { 2476 /* multi token -- take only first token in nbuf */ 2477 *p = '\0'; 2478 snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s", 2479 nbuf, &p[1]); 2480 } 2481 else 2482 { 2483 *statp = EX_NOHOST; 2484 return FALSE; 2485 } 2486 2487 if (tTd(38, 20)) 2488 printf("\nnisplus_getcanoname(%s), qbuf=%s\n", 2489 name, qbuf); 2490 2491 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH, 2492 NULL, NULL); 2493 2494 if (result->status == NIS_SUCCESS) 2495 { 2496 int count; 2497 char *domain; 2498 2499 if ((count = NIS_RES_NUMOBJ(result)) != 1) 2500 { 2501 if (LogLevel > 10) 2502 sm_syslog(LOG_WARNING, CurEnv->e_id, 2503 "nisplus_getcanonname: lookup error, expected 1 entry, got %d", 2504 count); 2505 2506 /* ignore second entry */ 2507 if (tTd(38, 20)) 2508 printf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n", 2509 name, count); 2510 } 2511 2512 if (tTd(38, 20)) 2513 printf("nisplus_getcanoname(%s), found in directory \"%s\"\n", 2514 name, (NIS_RES_OBJECT(result))->zo_domain); 2515 2516 2517 vp = ((NIS_RES_OBJECT(result))->EN_col(0)); 2518 vsize = strlen(vp); 2519 if (tTd(38, 20)) 2520 printf("nisplus_getcanonname(%s), found %s\n", 2521 name, vp); 2522 if (strchr(vp, '.') != NULL) 2523 { 2524 domain = ""; 2525 } 2526 else 2527 { 2528 domain = macvalue('m', CurEnv); 2529 if (domain == NULL) 2530 domain = ""; 2531 } 2532 if (hbsize > vsize + (int) strlen(domain) + 1) 2533 { 2534 if (domain[0] == '\0') 2535 strcpy(name, vp); 2536 else 2537 snprintf(name, hbsize, "%s.%s", vp, domain); 2538 *statp = EX_OK; 2539 } 2540 else 2541 *statp = EX_NOHOST; 2542 nis_freeresult(result); 2543 return TRUE; 2544 } 2545 else 2546 { 2547 if (result->status == NIS_NOTFOUND) 2548 *statp = EX_NOHOST; 2549 else if (result->status == NIS_TRYAGAIN) 2550 *statp = EX_TEMPFAIL; 2551 else 2552 *statp = EX_UNAVAILABLE; 2553 } 2554 if (tTd(38, 20)) 2555 printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n", 2556 name, result->status, *statp); 2557 nis_freeresult(result); 2558 return FALSE; 2559} 2560 2561 2562char * 2563nisplus_default_domain() 2564{ 2565 static char default_domain[MAXNAME + 1] = ""; 2566 char *p; 2567 2568 if (default_domain[0] != '\0') 2569 return(default_domain); 2570 2571 p = nis_local_directory(); 2572 snprintf(default_domain, sizeof default_domain, "%s", p); 2573 return default_domain; 2574} 2575 2576#endif /* NISPLUS */ 2577/* 2578** LDAP Modules 2579** 2580** Contributed by Booker C. Bense <bbense@networking.stanford.edu>. 2581** Get your support from him. 2582*/ 2583 2584#ifdef LDAPMAP 2585 2586# undef NEEDGETOPT /* used for something else in LDAP */ 2587 2588# include <lber.h> 2589# include <ldap.h> 2590# include "ldap_map.h" 2591 2592/* 2593** LDAP_MAP_OPEN -- open LDAP map 2594** 2595** Since LDAP is TCP-based there is not much we can or should do 2596** here. It might be a good idea to attempt an open/close here. 2597*/ 2598 2599bool 2600ldap_map_open(map, mode) 2601 MAP *map; 2602 int mode; 2603{ 2604 if (tTd(38, 2)) 2605 printf("ldap_map_open(%s, %d)\n", map->map_mname, mode); 2606 2607 mode &= O_ACCMODE; 2608 if (mode != O_RDONLY) 2609 { 2610 /* issue a pseudo-error message */ 2611#ifdef ENOSYS 2612 errno = ENOSYS; 2613#else 2614# ifdef EFTYPE 2615 errno = EFTYPE; 2616# else 2617 errno = ENXIO; 2618# endif 2619#endif 2620 return FALSE; 2621 } 2622 return TRUE; 2623} 2624 2625 2626/* 2627** LDAP_MAP_START -- actually open LDAP map 2628** 2629** Caching should be investigated. 2630*/ 2631 2632static jmp_buf LDAPTimeout; 2633 2634static void 2635ldaptimeout(sig_no) 2636 int sig_no; 2637{ 2638 longjmp(LDAPTimeout, 1); 2639} 2640 2641bool 2642ldap_map_start(map) 2643 MAP *map; 2644{ 2645 LDAP_MAP_STRUCT *lmap; 2646 LDAP *ld; 2647 register EVENT *ev = NULL; 2648 2649 if (tTd(38, 2)) 2650 printf("ldap_map_start(%s)\n", map->map_mname); 2651 2652 lmap = (LDAP_MAP_STRUCT *) map->map_db1; 2653 2654 if (tTd(38,9)) 2655 printf("ldap_open(%s, %d)\n", lmap->ldaphost, lmap->ldapport); 2656 2657 /* Need to set an alarm here, ldap_open is hopelessly broken. */ 2658 2659 /* set the timeout */ 2660 if (lmap->timeout.tv_sec != 0) 2661 { 2662 if (setjmp(LDAPTimeout) != 0) 2663 { 2664 if (LogLevel > 1) 2665 sm_syslog(LOG_NOTICE, CurEnv->e_id, 2666 "timeout waiting for ldap_open to %.100s", 2667 lmap->ldaphost); 2668 return (FALSE); 2669 } 2670 ev = setevent(lmap->timeout.tv_sec, ldaptimeout, 0); 2671 } 2672 2673#ifdef LDAP_VERSION3 2674 ld = ldap_init(lmap->ldaphost,lmap->ldapport); 2675#else 2676 ld = ldap_open(lmap->ldaphost,lmap->ldapport); 2677#endif 2678 2679 /* clear the event if it has not sprung */ 2680 if (lmap->timeout.tv_sec != 0) 2681 clrevent(ev); 2682 2683 if (ld == NULL) 2684 { 2685 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2686 { 2687 syserr("%sldapopen failed to %s in map %s", 2688 bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ", 2689 lmap->ldaphost, map->map_mname); 2690 } 2691 return FALSE; 2692 } 2693 2694#ifdef LDAP_VERSION3 2695 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->deref); 2696 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->timelimit); 2697 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->sizelimit); 2698 ldap_set_option(ld, LDAP_OPT_REFERRALS, &lmap->ldap_options); 2699 2700 /* ld needs to be cast into the map struct */ 2701 lmap->ld = ld; 2702 return TRUE; 2703#else 2704 2705 /* From here on in we can use ldap internal timelimits */ 2706 ld->ld_deref = lmap->deref; 2707 ld->ld_timelimit = lmap->timelimit; 2708 ld->ld_sizelimit = lmap->sizelimit; 2709 ld->ld_options = lmap->ldap_options; 2710 2711 if (ldap_bind_s(ld, lmap->binddn,lmap->passwd,lmap->method) != LDAP_SUCCESS) 2712 { 2713 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2714 { 2715 syserr("421 Cannot bind to map %s in ldap server %s", 2716 map->map_mname, lmap->ldaphost); 2717 } 2718 } 2719 else 2720 { 2721 /* We need to cast ld into the map structure */ 2722 lmap->ld = ld; 2723 return TRUE; 2724 } 2725 2726 return FALSE; 2727#endif 2728} 2729 2730 2731/* 2732** LDAP_MAP_CLOSE -- close ldap map 2733*/ 2734 2735void 2736ldap_map_close(map) 2737 MAP *map; 2738{ 2739 LDAP_MAP_STRUCT *lmap ; 2740 lmap = (LDAP_MAP_STRUCT *) map->map_db1; 2741 if (lmap->ld != NULL) 2742 ldap_unbind(lmap->ld); 2743} 2744 2745 2746#ifdef SUNET_ID 2747/* 2748** SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form 2749** This only makes sense at Stanford University. 2750*/ 2751 2752char * 2753sunet_id_hash(str) 2754 char *str; 2755{ 2756 char *p, *p_last; 2757 2758 p = str; 2759 p_last = p; 2760 while (*p != '\0') 2761 { 2762 if (islower(*p) || isdigit(*p)) 2763 { 2764 *p_last = *p; 2765 p_last++; 2766 } 2767 else if (isupper(*p)) 2768 { 2769 *p_last = tolower(*p); 2770 p_last++; 2771 } 2772 ++p; 2773 } 2774 if (*p_last != '\0') 2775 *p_last = '\0'; 2776 return (str); 2777} 2778 2779 2780 2781#endif /* SUNET_ID */ 2782/* 2783** LDAP_MAP_LOOKUP -- look up a datum in a LDAP map 2784*/ 2785 2786char * 2787ldap_map_lookup(map, name, av, statp) 2788 MAP *map; 2789 char *name; 2790 char **av; 2791 int *statp; 2792{ 2793 LDAP_MAP_STRUCT *lmap = NULL; 2794 LDAPMessage *entry; 2795 char *vp; 2796 auto int vsize; 2797 char keybuf[MAXNAME + 1]; 2798 char filter[LDAP_MAP_MAX_FILTER + 1]; 2799 char **attr_values = NULL; 2800 char *result; 2801 int name_len; 2802 char *fp, *p, *q; 2803 2804 if (tTd(38, 20)) 2805 printf("ldap_map_lookup(%s, %s)\n", map->map_mname, name); 2806 2807 /* actually open the map */ 2808 if (!ldap_map_start(map)) 2809 { 2810 result = NULL; 2811 *statp = EX_TEMPFAIL; 2812 goto quick_exit; 2813 } 2814 2815 /* Get ldap struct pointer from map */ 2816 lmap = (LDAP_MAP_STRUCT *) map->map_db1; 2817 2818 name_len = strlen(name); 2819 if (name_len > MAXNAME) 2820 name_len = MAXNAME; 2821 strncpy(keybuf, name, name_len); 2822 keybuf[name_len] = '\0'; 2823 2824 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2825#ifdef SUNET_ID 2826 sunet_id_hash(keybuf); 2827#else 2828 makelower(keybuf); 2829#endif /*SUNET_ID */ 2830 2831 /* substitute keybuf into filter, perhaps multiple times */ 2832 fp = filter; 2833 p = lmap->filter; 2834 while ((q = strchr(p, '%')) != NULL) 2835 { 2836 if (q[1] == 's') 2837 { 2838 snprintf(fp, SPACELEFT(filter, fp), "%.*s%s", 2839 q - p, p, keybuf); 2840 p = q + 2; 2841 } 2842 else 2843 { 2844 snprintf(fp, SPACELEFT(filter, fp), "%.*s", 2845 q - p + 1, p); 2846 p = q + (q[1] == '%' ? 2 : 1); 2847 } 2848 fp += strlen(fp); 2849 } 2850 snprintf(fp, SPACELEFT(filter, fp), "%s", p); 2851 if (tTd(38, 20)) 2852 printf("ldap search filter=%s\n", filter); 2853 2854 if (ldap_search_st(lmap->ld, lmap->base,lmap->scope,filter, 2855 lmap->attr, lmap->attrsonly, &(lmap->timeout), 2856 &(lmap->res)) != LDAP_SUCCESS) 2857 { 2858 /* try close/opening map */ 2859 ldap_map_close(map); 2860 if (!ldap_map_start(map)) 2861 { 2862 result = NULL; 2863 *statp = EX_TEMPFAIL; 2864 goto quick_exit; 2865 } 2866 if (ldap_search_st(lmap->ld, lmap->base, lmap->scope, filter, 2867 lmap->attr, lmap->attrsonly, 2868 &(lmap->timeout), &(lmap->res)) 2869 != LDAP_SUCCESS) 2870 { 2871 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2872 { 2873 syserr("%sError in ldap_search_st using %s in map %s", 2874 bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ", 2875 filter, map->map_mname); 2876 } 2877 result = NULL; 2878 *statp = EX_TEMPFAIL; 2879 goto quick_exit; 2880 } 2881 } 2882 2883 entry = ldap_first_entry(lmap->ld,lmap->res); 2884 if (entry == NULL) 2885 { 2886 result = NULL; 2887 *statp = EX_NOTFOUND; 2888 goto quick_exit; 2889 } 2890 2891 /* Need to build the args for map_rewrite here */ 2892 attr_values = ldap_get_values(lmap->ld,entry,lmap->attr[0]); 2893 if (attr_values == NULL) 2894 { 2895 /* bad things happened */ 2896 result = NULL; 2897 *statp = EX_NOTFOUND; 2898 goto quick_exit; 2899 } 2900 2901 *statp = EX_OK; 2902 2903 /* If there is more that one use the first */ 2904 vp = attr_values[0]; 2905 vsize = strlen(vp); 2906 2907 if (LogLevel > 9) 2908 sm_syslog(LOG_INFO, CurEnv->e_id, 2909 "ldap %.100s => %s", 2910 name, vp); 2911 if (bitset(MF_MATCHONLY, map->map_mflags)) 2912 result = map_rewrite(map, name, strlen(name), NULL); 2913 else 2914 result = map_rewrite(map, vp, vsize, av); 2915 2916 quick_exit: 2917 if (attr_values != NULL) 2918 ldap_value_free(attr_values); 2919 if (lmap != NULL) 2920 ldap_msgfree(lmap->res); 2921 ldap_map_close(map); 2922 return result ; 2923} 2924 2925 2926/* 2927** LDAP_MAP_DEQUOTE - helper routine for ldap_map_parseargs 2928*/ 2929 2930char * 2931ldap_map_dequote(str) 2932 char *str; 2933{ 2934 char *p; 2935 char *start; 2936 p = str; 2937 2938 if (*p == '"') 2939 { 2940 start = ++p; 2941 /* Should probably swallow initial whitespace here */ 2942 } 2943 else 2944 { 2945 return(str); 2946 } 2947 while (*p != '"' && *p != '\0') 2948 { 2949 p++; 2950 } 2951 if (*p != '\0') 2952 *p = '\0'; 2953 return start; 2954} 2955 2956/* 2957** LDAP_MAP_PARSEARGS -- parse ldap map definition args. 2958*/ 2959 2960bool 2961ldap_map_parseargs(map,args) 2962 MAP *map; 2963 char *args; 2964{ 2965 register char *p = args; 2966 register int done; 2967 LDAP_MAP_STRUCT *lmap; 2968 2969 /* We need to alloc an LDAP_MAP_STRUCT struct */ 2970 lmap = (LDAP_MAP_STRUCT *) xalloc(sizeof(LDAP_MAP_STRUCT)); 2971 2972 /* Set default int's here , default strings below */ 2973 lmap->ldapport = DEFAULT_LDAP_MAP_PORT; 2974 lmap->deref = DEFAULT_LDAP_MAP_DEREF; 2975 lmap->timelimit = DEFAULT_LDAP_MAP_TIMELIMIT; 2976 lmap->sizelimit = DEFAULT_LDAP_MAP_SIZELIMIT; 2977 lmap->ldap_options = DEFAULT_LDAP_MAP_LDAP_OPTIONS; 2978 lmap->method = DEFAULT_LDAP_MAP_METHOD; 2979 lmap->scope = DEFAULT_LDAP_MAP_SCOPE; 2980 lmap->attrsonly = DEFAULT_LDAP_MAP_ATTRSONLY; 2981 lmap->timeout.tv_sec = DEFAULT_LDAP_MAP_TIMELIMIT; 2982 lmap->timeout.tv_usec = 0; 2983 2984 /* Default char ptrs to NULL */ 2985 lmap->binddn = NULL; 2986 lmap->passwd = NULL; 2987 lmap->base = NULL; 2988 lmap->ldaphost = NULL; 2989 2990 /* Default general ptrs to NULL */ 2991 lmap->ld = NULL; 2992 lmap->res = NULL; 2993 2994 map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL; 2995 for (;;) 2996 { 2997 while (isascii(*p) && isspace(*p)) 2998 p++; 2999 if (*p != '-') 3000 break; 3001 switch (*++p) 3002 { 3003 case 'N': 3004 map->map_mflags |= MF_INCLNULL; 3005 map->map_mflags &= ~MF_TRY0NULL; 3006 break; 3007 3008 case 'O': 3009 map->map_mflags &= ~MF_TRY1NULL; 3010 break; 3011 3012 case 'o': 3013 map->map_mflags |= MF_OPTIONAL; 3014 break; 3015 3016 case 'f': 3017 map->map_mflags |= MF_NOFOLDCASE; 3018 break; 3019 3020 case 'm': 3021 map->map_mflags |= MF_MATCHONLY; 3022 break; 3023 3024 case 'A': 3025 map->map_mflags |= MF_APPEND; 3026 break; 3027 3028 case 'q': 3029 map->map_mflags |= MF_KEEPQUOTES; 3030 break; 3031 3032 case 't': 3033 map->map_mflags |= MF_NODEFER; 3034 break; 3035 3036 case 'a': 3037 map->map_app = ++p; 3038 break; 3039 3040 case 'T': 3041 map->map_tapp = ++p; 3042 break; 3043 3044 /* Start of ldap_map specific args */ 3045 case 'k': /* search field */ 3046 while (isascii(*++p) && isspace(*p)) 3047 continue; 3048 lmap->filter = p; 3049 break; 3050 3051 case 'v': /* attr to return */ 3052 while (isascii(*++p) && isspace(*p)) 3053 continue; 3054 lmap->attr[0] = p; 3055 lmap->attr[1] = NULL; 3056 break; 3057 3058 /* args stolen from ldapsearch.c */ 3059 case 'R': /* don't auto chase referrals */ 3060#ifdef LDAP_REFERRALS 3061 lmap->ldap_options &= ~LDAP_OPT_REFERRALS; 3062#else /* LDAP_REFERRALS */ 3063 syserr("compile with -DLDAP_REFERRALS for referral support\n"); 3064#endif /* LDAP_REFERRALS */ 3065 break; 3066 3067 case 'n': /* retrieve attribute names only -- no values */ 3068 lmap->attrsonly += 1; 3069 break; 3070 3071 case 's': /* search scope */ 3072 if (strncasecmp(++p, "base", 4) == 0) 3073 { 3074 lmap->scope = LDAP_SCOPE_BASE; 3075 } 3076 else if (strncasecmp(p, "one", 3) == 0) 3077 { 3078 lmap->scope = LDAP_SCOPE_ONELEVEL; 3079 } 3080 else if (strncasecmp(p, "sub", 3) == 0) 3081 { 3082 lmap->scope = LDAP_SCOPE_SUBTREE; 3083 } 3084 else 3085 { /* bad config line */ 3086 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 3087 { 3088 char *ptr; 3089 3090 if ((ptr = strchr(p, ' ')) != NULL) 3091 *ptr = '\0'; 3092 syserr("Scope must be [base|one|sub] not %s in map %s", 3093 p, map->map_mname); 3094 if (ptr != NULL) 3095 *ptr = ' '; 3096 return FALSE; 3097 } 3098 } 3099 break; 3100 3101 case 'h': /* ldap host */ 3102 while (isascii(*++p) && isspace(*p)) 3103 continue; 3104 map->map_domain = p; 3105 lmap->ldaphost = p; 3106 break; 3107 3108 case 'b': /* search base */ 3109 while (isascii(*++p) && isspace(*p)) 3110 continue; 3111 lmap->base = p; 3112 break; 3113 3114 case 'p': /* ldap port */ 3115 while (isascii(*++p) && isspace(*p)) 3116 continue; 3117 lmap->ldapport = atoi(p); 3118 break; 3119 3120 case 'l': /* time limit */ 3121 while (isascii(*++p) && isspace(*p)) 3122 continue; 3123 lmap->timelimit = atoi(p); 3124 lmap->timeout.tv_sec = lmap->timelimit; 3125 break; 3126 3127 } 3128 3129 /* need to account for quoted strings here arggg... */ 3130 done = isascii(*p) && isspace(*p); 3131 while (*p != '\0' && !done) 3132 { 3133 if (*p == '"') 3134 { 3135 while (*++p != '"' && *p != '\0') 3136 { 3137 continue; 3138 } 3139 if (*p != '\0') 3140 p++; 3141 } 3142 else 3143 { 3144 p++; 3145 } 3146 done = isascii(*p) && isspace(*p); 3147 } 3148 3149 if (*p != '\0') 3150 *p++ = '\0'; 3151 } 3152 3153 if (map->map_app != NULL) 3154 map->map_app = newstr(ldap_map_dequote(map->map_app)); 3155 if (map->map_tapp != NULL) 3156 map->map_tapp = newstr(ldap_map_dequote(map->map_tapp)); 3157 if (map->map_domain != NULL) 3158 map->map_domain = newstr(ldap_map_dequote(map->map_domain)); 3159 3160 /* 3161 ** We need to swallow up all the stuff into a struct 3162 ** and dump it into map->map_dbptr1 3163 */ 3164 3165 if (lmap->ldaphost != NULL) 3166 lmap->ldaphost = newstr(ldap_map_dequote(lmap->ldaphost)); 3167 else 3168 { 3169 syserr("LDAP map: -h flag is required"); 3170 return FALSE; 3171 } 3172 3173 if (lmap->binddn != NULL) 3174 lmap->binddn = newstr(ldap_map_dequote(lmap->binddn)); 3175 else 3176 lmap->binddn = DEFAULT_LDAP_MAP_BINDDN; 3177 3178 3179 if (lmap->passwd != NULL) 3180 lmap->passwd = newstr(ldap_map_dequote(lmap->passwd)); 3181 else 3182 lmap->passwd = DEFAULT_LDAP_MAP_PASSWD; 3183 3184 if (lmap->base != NULL) 3185 lmap->base = newstr(ldap_map_dequote(lmap->base)); 3186 else 3187 { 3188 syserr("LDAP map: -b flag is required"); 3189 return FALSE; 3190 } 3191 3192 3193 if (lmap->filter != NULL) 3194 lmap->filter = newstr(ldap_map_dequote(lmap->filter)); 3195 else 3196 { 3197 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 3198 { 3199 syserr("No filter given in map %s", map->map_mname); 3200 return FALSE; 3201 } 3202 } 3203 if (lmap->attr[0] != NULL) 3204 lmap->attr[0] = newstr(ldap_map_dequote(lmap->attr[0])); 3205 else 3206 { 3207 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 3208 { 3209 syserr("No return attribute in %s", map->map_mname); 3210 return FALSE; 3211 } 3212 } 3213 3214 map->map_db1 = (ARBPTR_T) lmap; 3215 return TRUE; 3216} 3217 3218#endif /* LDAP Modules */ 3219/* 3220** syslog map 3221*/ 3222 3223#if _FFR_MAP_SYSLOG 3224 3225#define map_prio map_lockfd /* overload field */ 3226 3227/* 3228** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages. 3229*/ 3230 3231bool 3232syslog_map_parseargs(map, args) 3233 MAP *map; 3234 char *args; 3235{ 3236 char *p = args; 3237 char *priority = NULL; 3238 3239 for (;;) 3240 { 3241 while (isascii(*p) && isspace(*p)) 3242 p++; 3243 if (*p != '-') 3244 break; 3245 if (*++p == 'L') 3246 priority = ++p; 3247 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 3248 p++; 3249 if (*p != '\0') 3250 *p++ = '\0'; 3251 } 3252 3253 if (priority == NULL) 3254 map->map_prio = LOG_INFO; 3255 else 3256 { 3257 if (strncasecmp("LOG_", priority, 4) == 0) 3258 priority += 4; 3259 3260#ifdef LOG_EMERG 3261 if (strcasecmp("EMERG", priority) == 0) 3262 map->map_prio = LOG_EMERG; 3263 else 3264#endif 3265#ifdef LOG_ALERT 3266 if (strcasecmp("ALERT", priority) == 0) 3267 map->map_prio = LOG_ALERT; 3268 else 3269#endif 3270#ifdef LOG_CRIT 3271 if (strcasecmp("CRIT", priority) == 0) 3272 map->map_prio = LOG_CRIT; 3273 else 3274#endif 3275#ifdef LOG_ERR 3276 if (strcasecmp("ERR", priority) == 0) 3277 map->map_prio = LOG_ERR; 3278 else 3279#endif 3280#ifdef LOG_WARNING 3281 if (strcasecmp("WARNING", priority) == 0) 3282 map->map_prio = LOG_WARNING; 3283 else 3284#endif 3285#ifdef LOG_NOTICE 3286 if (strcasecmp("NOTICE", priority) == 0) 3287 map->map_prio = LOG_NOTICE; 3288 else 3289#endif 3290#ifdef LOG_INFO 3291 if (strcasecmp("INFO", priority) == 0) 3292 map->map_prio = LOG_INFO; 3293 else 3294#endif 3295#ifdef LOG_DEBUG 3296 if (strcasecmp("DEBUG", priority) == 0) 3297 map->map_prio = LOG_DEBUG; 3298 else 3299#endif 3300 { 3301 syserr("syslog_map_parseargs: Unknown priority %s\n", 3302 priority); 3303 return FALSE; 3304 } 3305 } 3306 return TRUE; 3307} 3308 3309/* 3310** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string 3311*/ 3312 3313char * 3314syslog_map_lookup(map, string, args, statp) 3315 MAP *map; 3316 char *string; 3317 char **args; 3318 int *statp; 3319{ 3320 char *ptr = map_rewrite(map, string, strlen(string), args); 3321 3322 if (ptr != NULL) 3323 { 3324 if (tTd(38, 20)) 3325 printf("syslog_map_lookup(%s (priority %d): %s\n", 3326 map->map_mname, map->map_prio, ptr); 3327 3328 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr); 3329 } 3330 3331 *statp = EX_OK; 3332 return ""; 3333} 3334 3335#endif /* _FFR_MAP_SYSLOG */ 3336/* 3337** HESIOD Modules 3338*/ 3339 3340#ifdef HESIOD 3341 3342bool 3343hes_map_open(map, mode) 3344 MAP *map; 3345 int mode; 3346{ 3347 if (tTd(38, 2)) 3348 printf("hes_map_open(%s, %s, %d)\n", 3349 map->map_mname, map->map_file, mode); 3350 3351 if (mode != O_RDONLY) 3352 { 3353 /* issue a pseudo-error message */ 3354#ifdef ENOSYS 3355 errno = ENOSYS; 3356#else 3357# ifdef EFTYPE 3358 errno = EFTYPE; 3359# else 3360 errno = ENXIO; 3361# endif 3362#endif 3363 return FALSE; 3364 } 3365 3366#ifdef HESIOD_INIT 3367 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0) 3368 return TRUE; 3369 3370 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3371 syserr("421 cannot initialize Hesiod map (%s)", 3372 errstring(errno)); 3373 return FALSE; 3374#else 3375 if (hes_error() == HES_ER_UNINIT) 3376 hes_init(); 3377 switch (hes_error()) 3378 { 3379 case HES_ER_OK: 3380 case HES_ER_NOTFOUND: 3381 return TRUE; 3382 } 3383 3384 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3385 syserr("421 cannot initialize Hesiod map (%d)", hes_error()); 3386 3387 return FALSE; 3388#endif /* HESIOD_INIT */ 3389} 3390 3391char * 3392hes_map_lookup(map, name, av, statp) 3393 MAP *map; 3394 char *name; 3395 char **av; 3396 int *statp; 3397{ 3398 char **hp; 3399 3400 if (tTd(38, 20)) 3401 printf("hes_map_lookup(%s, %s)\n", map->map_file, name); 3402 3403 if (name[0] == '\\') 3404 { 3405 char *np; 3406 int nl; 3407 char nbuf[MAXNAME]; 3408 3409 nl = strlen(name); 3410 if (nl < sizeof nbuf - 1) 3411 np = nbuf; 3412 else 3413 np = xalloc(strlen(name) + 2); 3414 np[0] = '\\'; 3415 strcpy(&np[1], name); 3416#ifdef HESIOD_INIT 3417 hp = hesiod_resolve(HesiodContext, np, map->map_file); 3418#else 3419 hp = hes_resolve(np, map->map_file); 3420#endif /* HESIOD_INIT */ 3421 if (np != nbuf) 3422 free(np); 3423 } 3424 else 3425 { 3426#ifdef HESIOD_INIT 3427 hp = hesiod_resolve(HesiodContext, name, map->map_file); 3428#else 3429 hp = hes_resolve(name, map->map_file); 3430#endif /* HESIOD_INIT */ 3431 } 3432#ifdef HESIOD_INIT 3433 if (hp == NULL) 3434 return NULL; 3435 if (*hp == NULL) 3436 { 3437 hesiod_free_list(HesiodContext, hp); 3438 switch (errno) 3439 { 3440 case ENOENT: 3441 *statp = EX_NOTFOUND; 3442 break; 3443 case ECONNREFUSED: 3444 case EMSGSIZE: 3445 *statp = EX_TEMPFAIL; 3446 break; 3447 case ENOMEM: 3448 default: 3449 *statp = EX_UNAVAILABLE; 3450 break; 3451 } 3452 return NULL; 3453 } 3454#else 3455 if (hp == NULL || hp[0] == NULL) 3456 { 3457 switch (hes_error()) 3458 { 3459 case HES_ER_OK: 3460 *statp = EX_OK; 3461 break; 3462 3463 case HES_ER_NOTFOUND: 3464 *statp = EX_NOTFOUND; 3465 break; 3466 3467 case HES_ER_CONFIG: 3468 *statp = EX_UNAVAILABLE; 3469 break; 3470 3471 case HES_ER_NET: 3472 *statp = EX_TEMPFAIL; 3473 break; 3474 } 3475 return NULL; 3476 } 3477#endif /* HESIOD_INIT */ 3478 3479 if (bitset(MF_MATCHONLY, map->map_mflags)) 3480 return map_rewrite(map, name, strlen(name), NULL); 3481 else 3482 return map_rewrite(map, hp[0], strlen(hp[0]), av); 3483} 3484 3485#endif 3486/* 3487** NeXT NETINFO Modules 3488*/ 3489 3490#if NETINFO 3491 3492# define NETINFO_DEFAULT_DIR "/aliases" 3493# define NETINFO_DEFAULT_PROPERTY "members" 3494 3495extern char *ni_propval __P((char *, char *, char *, char *, int)); 3496 3497 3498/* 3499** NI_MAP_OPEN -- open NetInfo Aliases 3500*/ 3501 3502bool 3503ni_map_open(map, mode) 3504 MAP *map; 3505 int mode; 3506{ 3507 if (tTd(38, 2)) 3508 printf("ni_map_open(%s, %s, %d)\n", 3509 map->map_mname, map->map_file, mode); 3510 mode &= O_ACCMODE; 3511 3512 if (*map->map_file == '\0') 3513 map->map_file = NETINFO_DEFAULT_DIR; 3514 3515 if (map->map_valcolnm == NULL) 3516 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY; 3517 3518 if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags)) 3519 map->map_coldelim = ','; 3520 3521 return TRUE; 3522} 3523 3524 3525/* 3526** NI_MAP_LOOKUP -- look up a datum in NetInfo 3527*/ 3528 3529char * 3530ni_map_lookup(map, name, av, statp) 3531 MAP *map; 3532 char *name; 3533 char **av; 3534 int *statp; 3535{ 3536 char *res; 3537 char *propval; 3538 3539 if (tTd(38, 20)) 3540 printf("ni_map_lookup(%s, %s)\n", map->map_mname, name); 3541 3542 propval = ni_propval(map->map_file, map->map_keycolnm, name, 3543 map->map_valcolnm, map->map_coldelim); 3544 3545 if (propval == NULL) 3546 return NULL; 3547 3548 if (bitset(MF_MATCHONLY, map->map_mflags)) 3549 res = map_rewrite(map, name, strlen(name), NULL); 3550 else 3551 res = map_rewrite(map, propval, strlen(propval), av); 3552 free(propval); 3553 return res; 3554} 3555 3556 3557bool 3558ni_getcanonname(name, hbsize, statp) 3559 char *name; 3560 int hbsize; 3561 int *statp; 3562{ 3563 char *vptr; 3564 char *ptr; 3565 char nbuf[MAXNAME + 1]; 3566 3567 if (tTd(38, 20)) 3568 printf("ni_getcanonname(%s)\n", name); 3569 3570 if (strlen(name) >= sizeof nbuf) 3571 { 3572 *statp = EX_UNAVAILABLE; 3573 return FALSE; 3574 } 3575 (void) strcpy(nbuf, name); 3576 shorten_hostname(nbuf); 3577 3578 /* we only accept single token search key */ 3579 if (strchr(nbuf, '.')) 3580 { 3581 *statp = EX_NOHOST; 3582 return FALSE; 3583 } 3584 3585 /* Do the search */ 3586 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n'); 3587 3588 if (vptr == NULL) 3589 { 3590 *statp = EX_NOHOST; 3591 return FALSE; 3592 } 3593 3594 /* Only want the first machine name */ 3595 if ((ptr = strchr(vptr, '\n')) != NULL) 3596 *ptr = '\0'; 3597 3598 if (hbsize >= strlen(vptr)) 3599 { 3600 strcpy(name, vptr); 3601 *statp = EX_OK; 3602 return TRUE; 3603 } 3604 *statp = EX_UNAVAILABLE; 3605 free(vptr); 3606 return FALSE; 3607} 3608 3609 3610/* 3611** NI_PROPVAL -- NetInfo property value lookup routine 3612** 3613** Parameters: 3614** keydir -- the NetInfo directory name in which to search 3615** for the key. 3616** keyprop -- the name of the property in which to find the 3617** property we are interested. Defaults to "name". 3618** keyval -- the value for which we are really searching. 3619** valprop -- the property name for the value in which we 3620** are interested. 3621** sepchar -- if non-nil, this can be multiple-valued, and 3622** we should return a string separated by this 3623** character. 3624** 3625** Returns: 3626** NULL -- if: 3627** 1. the directory is not found 3628** 2. the property name is not found 3629** 3. the property contains multiple values 3630** 4. some error occured 3631** else -- the value of the lookup. 3632** 3633** Example: 3634** To search for an alias value, use: 3635** ni_propval("/aliases", "name", aliasname, "members", ',') 3636** 3637** Notes: 3638** Caller should free the return value of ni_proval 3639*/ 3640 3641# include <netinfo/ni.h> 3642 3643# define LOCAL_NETINFO_DOMAIN "." 3644# define PARENT_NETINFO_DOMAIN ".." 3645# define MAX_NI_LEVELS 256 3646 3647char * 3648ni_propval(keydir, keyprop, keyval, valprop, sepchar) 3649 char *keydir; 3650 char *keyprop; 3651 char *keyval; 3652 char *valprop; 3653 int sepchar; 3654{ 3655 char *propval = NULL; 3656 int i; 3657 int j, alen; 3658 void *ni = NULL; 3659 void *lastni = NULL; 3660 ni_status nis; 3661 ni_id nid; 3662 ni_namelist ninl; 3663 register char *p; 3664 char keybuf[1024]; 3665 3666 /* 3667 ** Create the full key from the two parts. 3668 ** 3669 ** Note that directory can end with, e.g., "name=" to specify 3670 ** an alternate search property. 3671 */ 3672 3673 i = strlen(keydir) + strlen(keyval) + 2; 3674 if (keyprop != NULL) 3675 i += strlen(keyprop) + 1; 3676 if (i > sizeof keybuf) 3677 return NULL; 3678 strcpy(keybuf, keydir); 3679 strcat(keybuf, "/"); 3680 if (keyprop != NULL) 3681 { 3682 strcat(keybuf, keyprop); 3683 strcat(keybuf, "="); 3684 } 3685 strcat(keybuf, keyval); 3686 3687 if (tTd(38, 21)) 3688 printf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n", 3689 keydir, keyprop, keyval, valprop, sepchar, keybuf); 3690 /* 3691 ** If the passed directory and property name are found 3692 ** in one of netinfo domains we need to search (starting 3693 ** from the local domain moving all the way back to the 3694 ** root domain) set propval to the property's value 3695 ** and return it. 3696 */ 3697 3698 for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++) 3699 { 3700 if (i == 0) 3701 { 3702 nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni); 3703 if (tTd(38, 20)) 3704 printf("ni_open(LOCAL) = %d\n", nis); 3705 } 3706 else 3707 { 3708 if (lastni != NULL) 3709 ni_free(lastni); 3710 lastni = ni; 3711 nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni); 3712 if (tTd(38, 20)) 3713 printf("ni_open(PARENT) = %d\n", nis); 3714 } 3715 3716 /* 3717 ** Don't bother if we didn't get a handle on a 3718 ** proper domain. This is not necessarily an error. 3719 ** We would get a positive ni_status if, for instance 3720 ** we never found the directory or property and tried 3721 ** to open the parent of the root domain! 3722 */ 3723 3724 if (nis != 0) 3725 break; 3726 3727 /* 3728 ** Find the path to the server information. 3729 */ 3730 3731 if (ni_pathsearch(ni, &nid, keybuf) != 0) 3732 continue; 3733 3734 /* 3735 ** Find associated value information. 3736 */ 3737 3738 if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0) 3739 continue; 3740 3741 if (tTd(38, 20)) 3742 printf("ni_lookupprop: len=%d\n", ninl.ni_namelist_len); 3743 /* 3744 ** See if we have an acceptable number of values. 3745 */ 3746 3747 if (ninl.ni_namelist_len <= 0) 3748 continue; 3749 3750 if (sepchar == '\0' && ninl.ni_namelist_len > 1) 3751 { 3752 ni_namelist_free(&ninl); 3753 continue; 3754 } 3755 3756 /* 3757 ** Calculate number of bytes needed and build result 3758 */ 3759 3760 alen = 1; 3761 for (j = 0; j < ninl.ni_namelist_len; j++) 3762 alen += strlen(ninl.ni_namelist_val[j]) + 1; 3763 propval = p = xalloc(alen); 3764 for (j = 0; j < ninl.ni_namelist_len; j++) 3765 { 3766 strcpy(p, ninl.ni_namelist_val[j]); 3767 p += strlen(p); 3768 *p++ = sepchar; 3769 } 3770 *--p = '\0'; 3771 3772 ni_namelist_free(&ninl); 3773 } 3774 3775 /* 3776 ** Clean up. 3777 */ 3778 3779 if (ni != NULL) 3780 ni_free(ni); 3781 if (lastni != NULL && ni != lastni) 3782 ni_free(lastni); 3783 if (tTd(38, 20)) 3784 printf("ni_propval returns: '%s'\n", propval); 3785 3786 return propval; 3787} 3788 3789#endif /* NETINFO */ 3790/* 3791** TEXT (unindexed text file) Modules 3792** 3793** This code donated by Sun Microsystems. 3794*/ 3795 3796#define map_sff map_lockfd /* overload field */ 3797 3798 3799/* 3800** TEXT_MAP_OPEN -- open text table 3801*/ 3802 3803bool 3804text_map_open(map, mode) 3805 MAP *map; 3806 int mode; 3807{ 3808 int sff; 3809 int i; 3810 3811 if (tTd(38, 2)) 3812 printf("text_map_open(%s, %s, %d)\n", 3813 map->map_mname, map->map_file, mode); 3814 3815 mode &= O_ACCMODE; 3816 if (mode != O_RDONLY) 3817 { 3818 errno = EPERM; 3819 return FALSE; 3820 } 3821 3822 if (*map->map_file == '\0') 3823 { 3824 syserr("text map \"%s\": file name required", 3825 map->map_mname); 3826 return FALSE; 3827 } 3828 3829 if (map->map_file[0] != '/') 3830 { 3831 syserr("text map \"%s\": file name must be fully qualified", 3832 map->map_mname); 3833 return FALSE; 3834 } 3835 3836 sff = SFF_ROOTOK|SFF_REGONLY; 3837 if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 3838 sff |= SFF_NOWLINK; 3839 if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 3840 sff |= SFF_SAFEDIRPATH; 3841 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName, 3842 sff, S_IRUSR, NULL)) != 0) 3843 { 3844 /* cannot open this map */ 3845 if (tTd(38, 2)) 3846 printf("\tunsafe map file: %d\n", i); 3847 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3848 syserr("text map \"%s\": unsafe map file %s", 3849 map->map_mname, map->map_file); 3850 return FALSE; 3851 } 3852 3853 if (map->map_keycolnm == NULL) 3854 map->map_keycolno = 0; 3855 else 3856 { 3857 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm))) 3858 { 3859 syserr("text map \"%s\", file %s: -k should specify a number, not %s", 3860 map->map_mname, map->map_file, 3861 map->map_keycolnm); 3862 return FALSE; 3863 } 3864 map->map_keycolno = atoi(map->map_keycolnm); 3865 } 3866 3867 if (map->map_valcolnm == NULL) 3868 map->map_valcolno = 0; 3869 else 3870 { 3871 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm))) 3872 { 3873 syserr("text map \"%s\", file %s: -v should specify a number, not %s", 3874 map->map_mname, map->map_file, 3875 map->map_valcolnm); 3876 return FALSE; 3877 } 3878 map->map_valcolno = atoi(map->map_valcolnm); 3879 } 3880 3881 if (tTd(38, 2)) 3882 { 3883 printf("text_map_open(%s, %s): delimiter = ", 3884 map->map_mname, map->map_file); 3885 if (map->map_coldelim == '\0') 3886 printf("(white space)\n"); 3887 else 3888 printf("%c\n", map->map_coldelim); 3889 } 3890 3891 map->map_sff = sff; 3892 return TRUE; 3893} 3894 3895 3896/* 3897** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table 3898*/ 3899 3900char * 3901text_map_lookup(map, name, av, statp) 3902 MAP *map; 3903 char *name; 3904 char **av; 3905 int *statp; 3906{ 3907 char *vp; 3908 auto int vsize; 3909 int buflen; 3910 FILE *f; 3911 char delim; 3912 int key_idx; 3913 bool found_it; 3914 int sff = map->map_sff; 3915 char search_key[MAXNAME + 1]; 3916 char linebuf[MAXLINE]; 3917 char buf[MAXNAME + 1]; 3918 extern char *get_column __P((char *, int, char, char *, int)); 3919 3920 found_it = FALSE; 3921 if (tTd(38, 20)) 3922 printf("text_map_lookup(%s, %s)\n", map->map_mname, name); 3923 3924 buflen = strlen(name); 3925 if (buflen > sizeof search_key - 1) 3926 buflen = sizeof search_key - 1; 3927 bcopy(name, search_key, buflen); 3928 search_key[buflen] = '\0'; 3929 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3930 makelower(search_key); 3931 3932 f = safefopen(map->map_file, O_RDONLY, FileMode, sff); 3933 if (f == NULL) 3934 { 3935 map->map_mflags &= ~(MF_VALID|MF_OPEN); 3936 *statp = EX_UNAVAILABLE; 3937 return NULL; 3938 } 3939 key_idx = map->map_keycolno; 3940 delim = map->map_coldelim; 3941 while (fgets(linebuf, MAXLINE, f) != NULL) 3942 { 3943 char *p; 3944 3945 /* skip comment line */ 3946 if (linebuf[0] == '#') 3947 continue; 3948 p = strchr(linebuf, '\n'); 3949 if (p != NULL) 3950 *p = '\0'; 3951 p = get_column(linebuf, key_idx, delim, buf, sizeof buf); 3952 if (p != NULL && strcasecmp(search_key, p) == 0) 3953 { 3954 found_it = TRUE; 3955 break; 3956 } 3957 } 3958 fclose(f); 3959 if (!found_it) 3960 { 3961 *statp = EX_NOTFOUND; 3962 return NULL; 3963 } 3964 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf); 3965 if (vp == NULL) 3966 { 3967 *statp = EX_NOTFOUND; 3968 return NULL; 3969 } 3970 vsize = strlen(vp); 3971 *statp = EX_OK; 3972 if (bitset(MF_MATCHONLY, map->map_mflags)) 3973 return map_rewrite(map, name, strlen(name), NULL); 3974 else 3975 return map_rewrite(map, vp, vsize, av); 3976} 3977 3978 3979/* 3980** TEXT_GETCANONNAME -- look up canonical name in hosts file 3981*/ 3982 3983bool 3984text_getcanonname(name, hbsize, statp) 3985 char *name; 3986 int hbsize; 3987 int *statp; 3988{ 3989 bool found; 3990 FILE *f; 3991 char linebuf[MAXLINE]; 3992 char cbuf[MAXNAME + 1]; 3993 char nbuf[MAXNAME + 1]; 3994 3995 if (tTd(38, 20)) 3996 printf("text_getcanonname(%s)\n", name); 3997 3998 if (strlen(name) >= (SIZE_T) sizeof nbuf) 3999 { 4000 *statp = EX_UNAVAILABLE; 4001 return FALSE; 4002 } 4003 (void) strcpy(nbuf, name); 4004 shorten_hostname(nbuf); 4005 4006 f = fopen(HostsFile, "r"); 4007 if (f == NULL) 4008 { 4009 *statp = EX_UNAVAILABLE; 4010 return FALSE; 4011 } 4012 found = FALSE; 4013 while (!found && fgets(linebuf, MAXLINE, f) != NULL) 4014 { 4015 char *p = strpbrk(linebuf, "#\n"); 4016 4017 if (p != NULL) 4018 *p = '\0'; 4019 if (linebuf[0] != '\0') 4020 found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf); 4021 } 4022 fclose(f); 4023 if (!found) 4024 { 4025 *statp = EX_NOHOST; 4026 return FALSE; 4027 } 4028 4029 if ((SIZE_T) hbsize >= strlen(cbuf)) 4030 { 4031 strcpy(name, cbuf); 4032 *statp = EX_OK; 4033 return TRUE; 4034 } 4035 *statp = EX_UNAVAILABLE; 4036 return FALSE; 4037} 4038/* 4039** STAB (Symbol Table) Modules 4040*/ 4041 4042 4043/* 4044** STAB_MAP_LOOKUP -- look up alias in symbol table 4045*/ 4046 4047/* ARGSUSED2 */ 4048char * 4049stab_map_lookup(map, name, av, pstat) 4050 register MAP *map; 4051 char *name; 4052 char **av; 4053 int *pstat; 4054{ 4055 register STAB *s; 4056 4057 if (tTd(38, 20)) 4058 printf("stab_lookup(%s, %s)\n", 4059 map->map_mname, name); 4060 4061 s = stab(name, ST_ALIAS, ST_FIND); 4062 if (s != NULL) 4063 return (s->s_alias); 4064 return (NULL); 4065} 4066 4067 4068/* 4069** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild) 4070*/ 4071 4072void 4073stab_map_store(map, lhs, rhs) 4074 register MAP *map; 4075 char *lhs; 4076 char *rhs; 4077{ 4078 register STAB *s; 4079 4080 s = stab(lhs, ST_ALIAS, ST_ENTER); 4081 s->s_alias = newstr(rhs); 4082} 4083 4084 4085/* 4086** STAB_MAP_OPEN -- initialize (reads data file) 4087** 4088** This is a wierd case -- it is only intended as a fallback for 4089** aliases. For this reason, opens for write (only during a 4090** "newaliases") always fails, and opens for read open the 4091** actual underlying text file instead of the database. 4092*/ 4093 4094bool 4095stab_map_open(map, mode) 4096 register MAP *map; 4097 int mode; 4098{ 4099 FILE *af; 4100 int sff; 4101 struct stat st; 4102 4103 if (tTd(38, 2)) 4104 printf("stab_map_open(%s, %s, %d)\n", 4105 map->map_mname, map->map_file, mode); 4106 4107 mode &= O_ACCMODE; 4108 if (mode != O_RDONLY) 4109 { 4110 errno = EPERM; 4111 return FALSE; 4112 } 4113 4114 sff = SFF_ROOTOK|SFF_REGONLY; 4115 if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 4116 sff |= SFF_NOWLINK; 4117 if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 4118 sff |= SFF_SAFEDIRPATH; 4119 af = safefopen(map->map_file, O_RDONLY, 0444, sff); 4120 if (af == NULL) 4121 return FALSE; 4122 readaliases(map, af, FALSE, FALSE); 4123 4124 if (fstat(fileno(af), &st) >= 0) 4125 map->map_mtime = st.st_mtime; 4126 fclose(af); 4127 4128 return TRUE; 4129} 4130/* 4131** Implicit Modules 4132** 4133** Tries several types. For back compatibility of aliases. 4134*/ 4135 4136 4137/* 4138** IMPL_MAP_LOOKUP -- lookup in best open database 4139*/ 4140 4141char * 4142impl_map_lookup(map, name, av, pstat) 4143 MAP *map; 4144 char *name; 4145 char **av; 4146 int *pstat; 4147{ 4148 if (tTd(38, 20)) 4149 printf("impl_map_lookup(%s, %s)\n", 4150 map->map_mname, name); 4151 4152#ifdef NEWDB 4153 if (bitset(MF_IMPL_HASH, map->map_mflags)) 4154 return db_map_lookup(map, name, av, pstat); 4155#endif 4156#ifdef NDBM 4157 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 4158 return ndbm_map_lookup(map, name, av, pstat); 4159#endif 4160 return stab_map_lookup(map, name, av, pstat); 4161} 4162 4163/* 4164** IMPL_MAP_STORE -- store in open databases 4165*/ 4166 4167void 4168impl_map_store(map, lhs, rhs) 4169 MAP *map; 4170 char *lhs; 4171 char *rhs; 4172{ 4173 if (tTd(38, 12)) 4174 printf("impl_map_store(%s, %s, %s)\n", 4175 map->map_mname, lhs, rhs); 4176#ifdef NEWDB 4177 if (bitset(MF_IMPL_HASH, map->map_mflags)) 4178 db_map_store(map, lhs, rhs); 4179#endif 4180#ifdef NDBM 4181 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 4182 ndbm_map_store(map, lhs, rhs); 4183#endif 4184 stab_map_store(map, lhs, rhs); 4185} 4186 4187/* 4188** IMPL_MAP_OPEN -- implicit database open 4189*/ 4190 4191bool 4192impl_map_open(map, mode) 4193 MAP *map; 4194 int mode; 4195{ 4196 if (tTd(38, 2)) 4197 printf("impl_map_open(%s, %s, %d)\n", 4198 map->map_mname, map->map_file, mode); 4199 4200 mode &= O_ACCMODE; 4201#ifdef NEWDB 4202 map->map_mflags |= MF_IMPL_HASH; 4203 if (hash_map_open(map, mode)) 4204 { 4205# ifdef NDBM_YP_COMPAT 4206 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL) 4207# endif 4208 return TRUE; 4209 } 4210 else 4211 map->map_mflags &= ~MF_IMPL_HASH; 4212#endif 4213#ifdef NDBM 4214 map->map_mflags |= MF_IMPL_NDBM; 4215 if (ndbm_map_open(map, mode)) 4216 { 4217 return TRUE; 4218 } 4219 else 4220 map->map_mflags &= ~MF_IMPL_NDBM; 4221#endif 4222 4223#if defined(NEWDB) || defined(NDBM) 4224 if (Verbose) 4225 message("WARNING: cannot open alias database %s%s", 4226 map->map_file, 4227 mode == O_RDONLY ? "; reading text version" : ""); 4228#else 4229 if (mode != O_RDONLY) 4230 usrerr("Cannot rebuild aliases: no database format defined"); 4231#endif 4232 4233 if (mode == O_RDONLY) 4234 return stab_map_open(map, mode); 4235 else 4236 return FALSE; 4237} 4238 4239 4240/* 4241** IMPL_MAP_CLOSE -- close any open database(s) 4242*/ 4243 4244void 4245impl_map_close(map) 4246 MAP *map; 4247{ 4248 if (tTd(38, 9)) 4249 printf("impl_map_close(%s, %s, %lx)\n", 4250 map->map_mname, map->map_file, map->map_mflags); 4251#ifdef NEWDB 4252 if (bitset(MF_IMPL_HASH, map->map_mflags)) 4253 { 4254 db_map_close(map); 4255 map->map_mflags &= ~MF_IMPL_HASH; 4256 } 4257#endif 4258 4259#ifdef NDBM 4260 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 4261 { 4262 ndbm_map_close(map); 4263 map->map_mflags &= ~MF_IMPL_NDBM; 4264 } 4265#endif 4266} 4267/* 4268** User map class. 4269** 4270** Provides access to the system password file. 4271*/ 4272 4273/* 4274** USER_MAP_OPEN -- open user map 4275** 4276** Really just binds field names to field numbers. 4277*/ 4278 4279bool 4280user_map_open(map, mode) 4281 MAP *map; 4282 int mode; 4283{ 4284 if (tTd(38, 2)) 4285 printf("user_map_open(%s, %d)\n", 4286 map->map_mname, mode); 4287 4288 mode &= O_ACCMODE; 4289 if (mode != O_RDONLY) 4290 { 4291 /* issue a pseudo-error message */ 4292#ifdef ENOSYS 4293 errno = ENOSYS; 4294#else 4295# ifdef EFTYPE 4296 errno = EFTYPE; 4297# else 4298 errno = ENXIO; 4299# endif 4300#endif 4301 return FALSE; 4302 } 4303 if (map->map_valcolnm == NULL) 4304 /* nothing */ ; 4305 else if (strcasecmp(map->map_valcolnm, "name") == 0) 4306 map->map_valcolno = 1; 4307 else if (strcasecmp(map->map_valcolnm, "passwd") == 0) 4308 map->map_valcolno = 2; 4309 else if (strcasecmp(map->map_valcolnm, "uid") == 0) 4310 map->map_valcolno = 3; 4311 else if (strcasecmp(map->map_valcolnm, "gid") == 0) 4312 map->map_valcolno = 4; 4313 else if (strcasecmp(map->map_valcolnm, "gecos") == 0) 4314 map->map_valcolno = 5; 4315 else if (strcasecmp(map->map_valcolnm, "dir") == 0) 4316 map->map_valcolno = 6; 4317 else if (strcasecmp(map->map_valcolnm, "shell") == 0) 4318 map->map_valcolno = 7; 4319 else 4320 { 4321 syserr("User map %s: unknown column name %s", 4322 map->map_mname, map->map_valcolnm); 4323 return FALSE; 4324 } 4325 return TRUE; 4326} 4327 4328 4329/* 4330** USER_MAP_LOOKUP -- look up a user in the passwd file. 4331*/ 4332 4333/* ARGSUSED3 */ 4334char * 4335user_map_lookup(map, key, av, statp) 4336 MAP *map; 4337 char *key; 4338 char **av; 4339 int *statp; 4340{ 4341 struct passwd *pw; 4342 auto bool fuzzy; 4343 4344 if (tTd(38, 20)) 4345 printf("user_map_lookup(%s, %s)\n", 4346 map->map_mname, key); 4347 4348 pw = finduser(key, &fuzzy); 4349 if (pw == NULL) 4350 return NULL; 4351 if (bitset(MF_MATCHONLY, map->map_mflags)) 4352 return map_rewrite(map, key, strlen(key), NULL); 4353 else 4354 { 4355 char *rwval = NULL; 4356 char buf[30]; 4357 4358 switch (map->map_valcolno) 4359 { 4360 case 0: 4361 case 1: 4362 rwval = pw->pw_name; 4363 break; 4364 4365 case 2: 4366 rwval = pw->pw_passwd; 4367 break; 4368 4369 case 3: 4370 snprintf(buf, sizeof buf, "%d", pw->pw_uid); 4371 rwval = buf; 4372 break; 4373 4374 case 4: 4375 snprintf(buf, sizeof buf, "%d", pw->pw_gid); 4376 rwval = buf; 4377 break; 4378 4379 case 5: 4380 rwval = pw->pw_gecos; 4381 break; 4382 4383 case 6: 4384 rwval = pw->pw_dir; 4385 break; 4386 4387 case 7: 4388 rwval = pw->pw_shell; 4389 break; 4390 } 4391 return map_rewrite(map, rwval, strlen(rwval), av); 4392 } 4393} 4394/* 4395** Program map type. 4396** 4397** This provides access to arbitrary programs. It should be used 4398** only very sparingly, since there is no way to bound the cost 4399** of invoking an arbitrary program. 4400*/ 4401 4402char * 4403prog_map_lookup(map, name, av, statp) 4404 MAP *map; 4405 char *name; 4406 char **av; 4407 int *statp; 4408{ 4409 int i; 4410 register char *p; 4411 int fd; 4412 auto pid_t pid; 4413 char *rval; 4414 int stat; 4415 char *argv[MAXPV + 1]; 4416 char buf[MAXLINE]; 4417 4418 if (tTd(38, 20)) 4419 printf("prog_map_lookup(%s, %s) %s\n", 4420 map->map_mname, name, map->map_file); 4421 4422 i = 0; 4423 argv[i++] = map->map_file; 4424 if (map->map_rebuild != NULL) 4425 { 4426 snprintf(buf, sizeof buf, "%s", map->map_rebuild); 4427 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) 4428 { 4429 if (i >= MAXPV - 1) 4430 break; 4431 argv[i++] = p; 4432 } 4433 } 4434 argv[i++] = name; 4435 argv[i] = NULL; 4436 if (tTd(38, 21)) 4437 { 4438 printf("prog_open:"); 4439 for (i = 0; argv[i] != NULL; i++) 4440 printf(" %s", argv[i]); 4441 printf("\n"); 4442 } 4443 (void) blocksignal(SIGCHLD); 4444 pid = prog_open(argv, &fd, CurEnv); 4445 if (pid < 0) 4446 { 4447 if (!bitset(MF_OPTIONAL, map->map_mflags)) 4448 syserr("prog_map_lookup(%s) failed (%s) -- closing", 4449 map->map_mname, errstring(errno)); 4450 else if (tTd(38, 9)) 4451 printf("prog_map_lookup(%s) failed (%s) -- closing", 4452 map->map_mname, errstring(errno)); 4453 map->map_mflags &= ~(MF_VALID|MF_OPEN); 4454 *statp = EX_OSFILE; 4455 return NULL; 4456 } 4457 i = read(fd, buf, sizeof buf - 1); 4458 if (i < 0) 4459 { 4460 syserr("prog_map_lookup(%s): read error %s\n", 4461 map->map_mname, errstring(errno)); 4462 rval = NULL; 4463 } 4464 else if (i == 0) 4465 { 4466 if (tTd(38, 20)) 4467 printf("prog_map_lookup(%s): empty answer\n", 4468 map->map_mname); 4469 rval = NULL; 4470 } 4471 else 4472 { 4473 buf[i] = '\0'; 4474 p = strchr(buf, '\n'); 4475 if (p != NULL) 4476 *p = '\0'; 4477 4478 /* collect the return value */ 4479 if (bitset(MF_MATCHONLY, map->map_mflags)) 4480 rval = map_rewrite(map, name, strlen(name), NULL); 4481 else 4482 rval = map_rewrite(map, buf, strlen(buf), NULL); 4483 4484 /* now flush any additional output */ 4485 while ((i = read(fd, buf, sizeof buf)) > 0) 4486 continue; 4487 } 4488 4489 /* wait for the process to terminate */ 4490 close(fd); 4491 stat = waitfor(pid); 4492 (void) releasesignal(SIGCHLD); 4493 4494 if (stat == -1) 4495 { 4496 syserr("prog_map_lookup(%s): wait error %s\n", 4497 map->map_mname, errstring(errno)); 4498 *statp = EX_SOFTWARE; 4499 rval = NULL; 4500 } 4501 else if (WIFEXITED(stat)) 4502 { 4503 if ((*statp = WEXITSTATUS(stat)) != EX_OK) 4504 rval = NULL; 4505 } 4506 else 4507 { 4508 syserr("prog_map_lookup(%s): child died on signal %d", 4509 map->map_mname, stat); 4510 *statp = EX_UNAVAILABLE; 4511 rval = NULL; 4512 } 4513 return rval; 4514} 4515/* 4516** Sequenced map type. 4517** 4518** Tries each map in order until something matches, much like 4519** implicit. Stores go to the first map in the list that can 4520** support storing. 4521** 4522** This is slightly unusual in that there are two interfaces. 4523** The "sequence" interface lets you stack maps arbitrarily. 4524** The "switch" interface builds a sequence map by looking 4525** at a system-dependent configuration file such as 4526** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. 4527** 4528** We don't need an explicit open, since all maps are 4529** opened during startup, including underlying maps. 4530*/ 4531 4532/* 4533** SEQ_MAP_PARSE -- Sequenced map parsing 4534*/ 4535 4536bool 4537seq_map_parse(map, ap) 4538 MAP *map; 4539 char *ap; 4540{ 4541 int maxmap; 4542 4543 if (tTd(38, 2)) 4544 printf("seq_map_parse(%s, %s)\n", map->map_mname, ap); 4545 maxmap = 0; 4546 while (*ap != '\0') 4547 { 4548 register char *p; 4549 STAB *s; 4550 4551 /* find beginning of map name */ 4552 while (isascii(*ap) && isspace(*ap)) 4553 ap++; 4554 for (p = ap; isascii(*p) && isalnum(*p); p++) 4555 continue; 4556 if (*p != '\0') 4557 *p++ = '\0'; 4558 while (*p != '\0' && (!isascii(*p) || !isalnum(*p))) 4559 p++; 4560 if (*ap == '\0') 4561 { 4562 ap = p; 4563 continue; 4564 } 4565 s = stab(ap, ST_MAP, ST_FIND); 4566 if (s == NULL) 4567 { 4568 syserr("Sequence map %s: unknown member map %s", 4569 map->map_mname, ap); 4570 } 4571 else if (maxmap == MAXMAPSTACK) 4572 { 4573 syserr("Sequence map %s: too many member maps (%d max)", 4574 map->map_mname, MAXMAPSTACK); 4575 maxmap++; 4576 } 4577 else if (maxmap < MAXMAPSTACK) 4578 { 4579 map->map_stack[maxmap++] = &s->s_map; 4580 } 4581 ap = p; 4582 } 4583 return TRUE; 4584} 4585 4586 4587/* 4588** SWITCH_MAP_OPEN -- open a switched map 4589** 4590** This looks at the system-dependent configuration and builds 4591** a sequence map that does the same thing. 4592** 4593** Every system must define a switch_map_find routine in conf.c 4594** that will return the list of service types associated with a 4595** given service class. 4596*/ 4597 4598bool 4599switch_map_open(map, mode) 4600 MAP *map; 4601 int mode; 4602{ 4603 int mapno; 4604 int nmaps; 4605 char *maptype[MAXMAPSTACK]; 4606 4607 if (tTd(38, 2)) 4608 printf("switch_map_open(%s, %s, %d)\n", 4609 map->map_mname, map->map_file, mode); 4610 4611 mode &= O_ACCMODE; 4612 nmaps = switch_map_find(map->map_file, maptype, map->map_return); 4613 if (tTd(38, 19)) 4614 { 4615 printf("\tswitch_map_find => %d\n", nmaps); 4616 for (mapno = 0; mapno < nmaps; mapno++) 4617 printf("\t\t%s\n", maptype[mapno]); 4618 } 4619 if (nmaps <= 0 || nmaps > MAXMAPSTACK) 4620 return FALSE; 4621 4622 for (mapno = 0; mapno < nmaps; mapno++) 4623 { 4624 register STAB *s; 4625 char nbuf[MAXNAME + 1]; 4626 4627 if (maptype[mapno] == NULL) 4628 continue; 4629 (void) snprintf(nbuf, sizeof nbuf, "%s.%s", 4630 map->map_mname, maptype[mapno]); 4631 s = stab(nbuf, ST_MAP, ST_FIND); 4632 if (s == NULL) 4633 { 4634 syserr("Switch map %s: unknown member map %s", 4635 map->map_mname, nbuf); 4636 } 4637 else 4638 { 4639 map->map_stack[mapno] = &s->s_map; 4640 if (tTd(38, 4)) 4641 printf("\tmap_stack[%d] = %s:%s\n", 4642 mapno, s->s_map.map_class->map_cname, 4643 nbuf); 4644 } 4645 } 4646 return TRUE; 4647} 4648 4649 4650/* 4651** SEQ_MAP_CLOSE -- close all underlying maps 4652*/ 4653 4654void 4655seq_map_close(map) 4656 MAP *map; 4657{ 4658 int mapno; 4659 4660 if (tTd(38, 9)) 4661 printf("seq_map_close(%s)\n", map->map_mname); 4662 4663 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 4664 { 4665 MAP *mm = map->map_stack[mapno]; 4666 4667 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags)) 4668 continue; 4669 mm->map_class->map_close(mm); 4670 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 4671 } 4672} 4673 4674 4675/* 4676** SEQ_MAP_LOOKUP -- sequenced map lookup 4677*/ 4678 4679char * 4680seq_map_lookup(map, key, args, pstat) 4681 MAP *map; 4682 char *key; 4683 char **args; 4684 int *pstat; 4685{ 4686 int mapno; 4687 int mapbit = 0x01; 4688 bool tempfail = FALSE; 4689 4690 if (tTd(38, 20)) 4691 printf("seq_map_lookup(%s, %s)\n", map->map_mname, key); 4692 4693 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) 4694 { 4695 MAP *mm = map->map_stack[mapno]; 4696 char *rv; 4697 4698 if (mm == NULL) 4699 continue; 4700 if (!bitset(MF_OPEN, mm->map_mflags)) 4701 { 4702 if (bitset(mapbit, map->map_return[MA_UNAVAIL])) 4703 { 4704 *pstat = EX_UNAVAILABLE; 4705 return NULL; 4706 } 4707 continue; 4708 } 4709 *pstat = EX_OK; 4710 rv = mm->map_class->map_lookup(mm, key, args, pstat); 4711 if (rv != NULL) 4712 return rv; 4713 if (*pstat == EX_TEMPFAIL) 4714 { 4715 if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) 4716 return NULL; 4717 tempfail = TRUE; 4718 } 4719 else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) 4720 break; 4721 } 4722 if (tempfail) 4723 *pstat = EX_TEMPFAIL; 4724 else if (*pstat == EX_OK) 4725 *pstat = EX_NOTFOUND; 4726 return NULL; 4727} 4728 4729 4730/* 4731** SEQ_MAP_STORE -- sequenced map store 4732*/ 4733 4734void 4735seq_map_store(map, key, val) 4736 MAP *map; 4737 char *key; 4738 char *val; 4739{ 4740 int mapno; 4741 4742 if (tTd(38, 12)) 4743 printf("seq_map_store(%s, %s, %s)\n", 4744 map->map_mname, key, val); 4745 4746 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 4747 { 4748 MAP *mm = map->map_stack[mapno]; 4749 4750 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags)) 4751 continue; 4752 4753 mm->map_class->map_store(mm, key, val); 4754 return; 4755 } 4756 syserr("seq_map_store(%s, %s, %s): no writable map", 4757 map->map_mname, key, val); 4758} 4759/* 4760** NULL stubs 4761*/ 4762 4763/* ARGSUSED */ 4764bool 4765null_map_open(map, mode) 4766 MAP *map; 4767 int mode; 4768{ 4769 return TRUE; 4770} 4771 4772/* ARGSUSED */ 4773void 4774null_map_close(map) 4775 MAP *map; 4776{ 4777 return; 4778} 4779 4780char * 4781null_map_lookup(map, key, args, pstat) 4782 MAP *map; 4783 char *key; 4784 char **args; 4785 int *pstat; 4786{ 4787 *pstat = EX_NOTFOUND; 4788 return NULL; 4789} 4790 4791/* ARGSUSED */ 4792void 4793null_map_store(map, key, val) 4794 MAP *map; 4795 char *key; 4796 char *val; 4797{ 4798 return; 4799} 4800 4801 4802/* 4803** BOGUS stubs 4804*/ 4805 4806char * 4807bogus_map_lookup(map, key, args, pstat) 4808 MAP *map; 4809 char *key; 4810 char **args; 4811 int *pstat; 4812{ 4813 *pstat = EX_TEMPFAIL; 4814 return NULL; 4815} 4816 4817MAPCLASS BogusMapClass = 4818{ 4819 "bogus-map", NULL, 0, 4820 NULL, bogus_map_lookup, null_map_store, 4821 null_map_open, null_map_close, 4822}; 4823/* 4824** REGEX modules 4825*/ 4826 4827#ifdef MAP_REGEX 4828 4829# include <regex.h> 4830 4831# define DEFAULT_DELIM CONDELSE 4832 4833# define END_OF_FIELDS -1 4834 4835# define ERRBUF_SIZE 80 4836# define MAX_MATCH 32 4837 4838# define xnalloc(s) memset(xalloc(s), 0, s); 4839 4840struct regex_map 4841{ 4842 regex_t pattern_buf; /* xalloc it */ 4843 int *regex_subfields; /* move to type MAP */ 4844 char *delim; /* move to type MAP */ 4845}; 4846 4847static int 4848parse_fields(s, ibuf, blen, nr_substrings) 4849 char *s; 4850 int *ibuf; /* array */ 4851 int blen; /* number of elements in ibuf */ 4852 int nr_substrings; /* number of substrings in the pattern */ 4853{ 4854 register char *cp; 4855 int i = 0; 4856 bool lastone = FALSE; 4857 4858 blen--; /* for terminating END_OF_FIELDS */ 4859 cp = s; 4860 do 4861 { 4862 for (;; cp++) 4863 { 4864 if (*cp == ',') 4865 { 4866 *cp = '\0'; 4867 break; 4868 } 4869 if (*cp == '\0') 4870 { 4871 lastone = TRUE; 4872 break; 4873 } 4874 } 4875 if (i < blen) 4876 { 4877 int val = atoi(s); 4878 4879 if (val < 0 || val >= nr_substrings) 4880 { 4881 syserr("field (%d) out of range, only %d substrings in pattern", 4882 val, nr_substrings); 4883 return -1; 4884 } 4885 ibuf[i++] = val; 4886 } 4887 else 4888 { 4889 syserr("too many fields, %d max\n", blen); 4890 return -1; 4891 } 4892 s = ++cp; 4893 } while (!lastone); 4894 ibuf[i] = END_OF_FIELDS; 4895 return i; 4896} 4897 4898bool 4899regex_map_init(map, ap) 4900 MAP *map; 4901 char *ap; 4902{ 4903 int regerr; 4904 struct regex_map *map_p; 4905 register char *p; 4906 char *sub_param = NULL; 4907 int pflags; 4908 static char defdstr[] = { (char)DEFAULT_DELIM, '\0' }; 4909 4910 if (tTd(38, 2)) 4911 printf("regex_map_init: mapname '%s', args '%s'\n", 4912 map->map_mname, ap); 4913 4914 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; 4915 4916 p = ap; 4917 4918 map_p = (struct regex_map *) xnalloc(sizeof(struct regex_map)); 4919 4920 for (;;) 4921 { 4922 while (isascii(*p) && isspace(*p)) 4923 p++; 4924 if (*p != '-') 4925 break; 4926 switch (*++p) 4927 { 4928 case 'n': /* not */ 4929 map->map_mflags |= MF_REGEX_NOT; 4930 break; 4931 4932 case 'f': /* case sensitive */ 4933 map->map_mflags |= MF_NOFOLDCASE; 4934 pflags &= ~REG_ICASE; 4935 break; 4936 4937 case 'b': /* basic regular expressions */ 4938 pflags &= ~REG_EXTENDED; 4939 break; 4940 4941 case 's': /* substring match () syntax */ 4942 sub_param = ++p; 4943 pflags &= ~REG_NOSUB; 4944 break; 4945 4946 case 'd': /* delimiter */ 4947 map_p->delim = ++p; 4948 break; 4949 4950 case 'a': /* map append */ 4951 map->map_app = ++p; 4952 break; 4953 4954 case 'm': /* matchonly */ 4955 map->map_mflags |= MF_MATCHONLY; 4956 break; 4957 4958 } 4959 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 4960 p++; 4961 if (*p != '\0') 4962 *p++ = '\0'; 4963 } 4964 if (tTd(38, 3)) 4965 printf("regex_map_init: compile '%s' 0x%x\n", p, pflags); 4966 4967 if ((regerr = regcomp(&(map_p->pattern_buf), p, pflags)) != 0) 4968 { 4969 /* Errorhandling */ 4970 char errbuf[ERRBUF_SIZE]; 4971 4972 regerror(regerr, &(map_p->pattern_buf), errbuf, ERRBUF_SIZE); 4973 syserr("pattern-compile-error: %s\n", errbuf); 4974 free(map_p); 4975 return FALSE; 4976 } 4977 4978 if (map->map_app != NULL) 4979 map->map_app = newstr(map->map_app); 4980 if (map_p->delim != NULL) 4981 map_p->delim = newstr(map_p->delim); 4982 else 4983 map_p->delim = defdstr; 4984 4985 if (!bitset(REG_NOSUB, pflags)) 4986 { 4987 /* substring matching */ 4988 int substrings; 4989 int *fields = (int *)xalloc(sizeof(int) * (MAX_MATCH + 1)); 4990 4991 substrings = map_p->pattern_buf.re_nsub + 1; 4992 4993 if (tTd(38, 3)) 4994 printf("regex_map_init: nr of substrings %d\n", substrings); 4995 4996 if (substrings >= MAX_MATCH) 4997 { 4998 syserr("too many substrings, %d max\n", MAX_MATCH); 4999 free(map_p); 5000 return FALSE; 5001 } 5002 if (sub_param != NULL && sub_param[0] != '\0') 5003 { 5004 /* optional parameter -sfields */ 5005 if (parse_fields(sub_param, fields, 5006 MAX_MATCH + 1, substrings) == -1) 5007 return FALSE; 5008 } 5009 else 5010 { 5011 /* set default fields */ 5012 int i; 5013 5014 for (i = 0; i < substrings; i++) 5015 fields[i] = i; 5016 fields[i] = END_OF_FIELDS; 5017 } 5018 map_p->regex_subfields = fields; 5019 if (tTd(38, 3)) 5020 { 5021 int *ip; 5022 5023 printf("regex_map_init: subfields"); 5024 for (ip = fields; *ip != END_OF_FIELDS; ip++) 5025 printf(" %d", *ip); 5026 printf("\n"); 5027 } 5028 } 5029 map->map_db1 = (ARBPTR_T)map_p; /* dirty hack */ 5030 5031 return TRUE; 5032} 5033 5034static char * 5035regex_map_rewrite(map, s, slen, av) 5036 MAP *map; 5037 const char *s; 5038 size_t slen; 5039 char **av; 5040{ 5041 if (bitset(MF_MATCHONLY, map->map_mflags)) 5042 return map_rewrite(map, av[0], strlen(av[0]), NULL); 5043 else 5044 return map_rewrite(map, s, slen, NULL); 5045} 5046 5047char * 5048regex_map_lookup(map, name, av, statp) 5049 MAP *map; 5050 char *name; 5051 char **av; 5052 int *statp; 5053{ 5054 int reg_res; 5055 struct regex_map *map_p; 5056 regmatch_t pmatch[MAX_MATCH]; 5057 5058 if (tTd(38, 20)) 5059 { 5060 char **cpp; 5061 5062 printf("regex_map_lookup: key '%s'\n", name); 5063 for (cpp = av; cpp && *cpp; cpp++) 5064 printf("regex_map_lookup: arg '%s'\n", *cpp); 5065 } 5066 5067 map_p = (struct regex_map *)(map->map_db1); 5068 reg_res = regexec(&(map_p->pattern_buf), name, MAX_MATCH, pmatch, 0); 5069 5070 if (bitset(MF_REGEX_NOT, map->map_mflags)) 5071 { 5072 /* option -n */ 5073 if (reg_res == REG_NOMATCH) 5074 return regex_map_rewrite(map, "", (size_t)0, av); 5075 else 5076 return NULL; 5077 } 5078 if (reg_res == REG_NOMATCH) 5079 return NULL; 5080 5081 if (map_p->regex_subfields != NULL) 5082 { 5083 /* option -s */ 5084 static char retbuf[MAXNAME]; 5085 int fields[MAX_MATCH + 1]; 5086 bool first = TRUE; 5087 int anglecnt = 0, cmntcnt = 0, spacecnt = 0; 5088 bool quotemode = FALSE, bslashmode = FALSE; 5089 register char *dp, *sp; 5090 char *endp, *ldp; 5091 int *ip; 5092 5093 dp = retbuf; 5094 ldp = retbuf + sizeof(retbuf) - 1; 5095 5096 if (av[1] != NULL) 5097 { 5098 if (parse_fields(av[1], fields, MAX_MATCH + 1, 5099 (int) map_p->pattern_buf.re_nsub + 1) == -1) 5100 { 5101 *statp = EX_CONFIG; 5102 return NULL; 5103 } 5104 ip = fields; 5105 } 5106 else 5107 ip = map_p->regex_subfields; 5108 5109 for ( ; *ip != END_OF_FIELDS; ip++) 5110 { 5111 if (!first) 5112 { 5113 for (sp = map_p->delim; *sp; sp++) 5114 { 5115 if (dp < ldp) 5116 *dp++ = *sp; 5117 } 5118 } 5119 else 5120 first = FALSE; 5121 5122 5123 if (pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0) 5124 continue; 5125 5126 sp = name + pmatch[*ip].rm_so; 5127 endp = name + pmatch[*ip].rm_eo; 5128 for (; endp > sp; sp++) 5129 { 5130 if (dp < ldp) 5131 { 5132 if(bslashmode) 5133 { 5134 *dp++ = *sp; 5135 bslashmode = FALSE; 5136 } 5137 else if(quotemode && *sp != '"' && 5138 *sp != '\\') 5139 { 5140 *dp++ = *sp; 5141 } 5142 else switch(*dp++ = *sp) 5143 { 5144 case '\\': 5145 bslashmode = TRUE; 5146 break; 5147 5148 case '(': 5149 cmntcnt++; 5150 break; 5151 5152 case ')': 5153 cmntcnt--; 5154 break; 5155 5156 case '<': 5157 anglecnt++; 5158 break; 5159 5160 case '>': 5161 anglecnt--; 5162 break; 5163 5164 case ' ': 5165 spacecnt++; 5166 break; 5167 5168 case '"': 5169 quotemode = !quotemode; 5170 break; 5171 } 5172 } 5173 } 5174 } 5175 if (anglecnt != 0 || cmntcnt != 0 || quotemode || 5176 bslashmode || spacecnt != 0) 5177 { 5178 sm_syslog(LOG_WARNING, NOQID, 5179 "Warning: regex may cause prescan() failure map=%s lookup=%s", 5180 map->map_mname, name); 5181 return NULL; 5182 } 5183 5184 *dp = '\0'; 5185 5186 return regex_map_rewrite(map, retbuf, strlen(retbuf), av); 5187 } 5188 return regex_map_rewrite(map, "", (size_t)0, av); 5189} 5190#endif /* MAP_REGEX */ 5191