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 14 static char sccsid[] = "@(#)map.c 8.261 (Berkeley) 2/2/1999"; 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 88 extern bool aliaswait __P((MAP *, char *, int)); 89 extern 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 117 bool 118 map_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 275 char * 276 map_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 396 void 397 initmaps(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 415 void 416 map_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 514 void 515 closemaps() 516 { 517 extern void map_close __P((STAB *, int)); 518 519 stabapply(map_close, 0); 520 } 521 522 /* ARGSUSED1 */ 523 void 524 map_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 561 bool 562 getcanonname(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 710 bool 711 extract_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 768 bool 769 ndbm_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 1029 char * 1030 ndbm_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 } 1056 lockdbm: 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 1121 void 1122 ndbm_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 1200 void 1201 ndbm_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 1268 extern bool db_map_open __P((MAP *, int, char *, DBTYPE, const void *)); 1269 #else 1270 extern 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 1285 bool 1286 bt_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 1307 bool 1308 hash_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 1332 bool 1333 db_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 1585 char * 1586 db_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 1741 void 1742 db_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 1848 void 1849 db_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 1882 /* 1883 ** If this map was not opened by the current 1884 ** process, do not close the map but recover 1885 ** the file descriptor. 1886 */ 1887 if (map->map_pid != getpid()) 1888 { 1889 int fd = -1; 1890 1891 errno = db->fd(db, &fd); 1892 if (fd >= 0) 1893 (void) close(fd); 1894 return; 1895 } 1896 1897 if ((errno = db->close(db, 0)) != 0) 1898 #endif 1899 syserr("db_map_close(%s, %s, %lx): db close failure", 1900 map->map_mname, map->map_file, map->map_mflags); 1901 } 1902 1903 #endif 1904 /* 1905 ** NIS Modules 1906 */ 1907 1908 # ifdef NIS 1909 1910 # ifndef YPERR_BUSY 1911 # define YPERR_BUSY 16 1912 # endif 1913 1914 /* 1915 ** NIS_MAP_OPEN -- open DBM map 1916 */ 1917 1918 bool 1919 nis_map_open(map, mode) 1920 MAP *map; 1921 int mode; 1922 { 1923 int yperr; 1924 register char *p; 1925 auto char *vp; 1926 auto int vsize; 1927 1928 if (tTd(38, 2)) 1929 printf("nis_map_open(%s, %s, %d)\n", 1930 map->map_mname, map->map_file, mode); 1931 1932 mode &= O_ACCMODE; 1933 if (mode != O_RDONLY) 1934 { 1935 /* issue a pseudo-error message */ 1936 #ifdef ENOSYS 1937 errno = ENOSYS; 1938 #else 1939 # ifdef EFTYPE 1940 errno = EFTYPE; 1941 # else 1942 errno = ENXIO; 1943 # endif 1944 #endif 1945 return FALSE; 1946 } 1947 1948 p = strchr(map->map_file, '@'); 1949 if (p != NULL) 1950 { 1951 *p++ = '\0'; 1952 if (*p != '\0') 1953 map->map_domain = p; 1954 } 1955 1956 if (*map->map_file == '\0') 1957 map->map_file = "mail.aliases"; 1958 1959 if (map->map_domain == NULL) 1960 { 1961 yperr = yp_get_default_domain(&map->map_domain); 1962 if (yperr != 0) 1963 { 1964 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1965 syserr("421 NIS map %s specified, but NIS not running", 1966 map->map_file); 1967 return FALSE; 1968 } 1969 } 1970 1971 /* check to see if this map actually exists */ 1972 yperr = yp_match(map->map_domain, map->map_file, "@", 1, 1973 &vp, &vsize); 1974 if (tTd(38, 10)) 1975 printf("nis_map_open: yp_match(@, %s, %s) => %s\n", 1976 map->map_domain, map->map_file, yperr_string(yperr)); 1977 if (yperr == 0 || yperr == YPERR_KEY || yperr == YPERR_BUSY) 1978 { 1979 /* 1980 ** We ought to be calling aliaswait() here if this is an 1981 ** alias file, but powerful HP-UX NIS servers apparently 1982 ** don't insert the @:@ token into the alias map when it 1983 ** is rebuilt, so aliaswait() just hangs. I hate HP-UX. 1984 */ 1985 1986 #if 0 1987 if (!bitset(MF_ALIAS, map->map_mflags) || 1988 aliaswait(map, NULL, TRUE)) 1989 #endif 1990 return TRUE; 1991 } 1992 1993 if (!bitset(MF_OPTIONAL, map->map_mflags)) 1994 { 1995 syserr("421 Cannot bind to map %s in domain %s: %s", 1996 map->map_file, map->map_domain, yperr_string(yperr)); 1997 } 1998 1999 return FALSE; 2000 } 2001 2002 2003 /* 2004 ** NIS_MAP_LOOKUP -- look up a datum in a NIS map 2005 */ 2006 2007 /* ARGSUSED3 */ 2008 char * 2009 nis_map_lookup(map, name, av, statp) 2010 MAP *map; 2011 char *name; 2012 char **av; 2013 int *statp; 2014 { 2015 char *vp; 2016 auto int vsize; 2017 int buflen; 2018 int yperr; 2019 char keybuf[MAXNAME + 1]; 2020 2021 if (tTd(38, 20)) 2022 printf("nis_map_lookup(%s, %s)\n", 2023 map->map_mname, name); 2024 2025 buflen = strlen(name); 2026 if (buflen > sizeof keybuf - 1) 2027 buflen = sizeof keybuf - 1; 2028 bcopy(name, keybuf, buflen); 2029 keybuf[buflen] = '\0'; 2030 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2031 makelower(keybuf); 2032 yperr = YPERR_KEY; 2033 if (bitset(MF_TRY0NULL, map->map_mflags)) 2034 { 2035 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, 2036 &vp, &vsize); 2037 if (yperr == 0) 2038 map->map_mflags &= ~MF_TRY1NULL; 2039 } 2040 if (yperr == YPERR_KEY && bitset(MF_TRY1NULL, map->map_mflags)) 2041 { 2042 buflen++; 2043 yperr = yp_match(map->map_domain, map->map_file, keybuf, buflen, 2044 &vp, &vsize); 2045 if (yperr == 0) 2046 map->map_mflags &= ~MF_TRY0NULL; 2047 } 2048 if (yperr != 0) 2049 { 2050 if (yperr != YPERR_KEY && yperr != YPERR_BUSY) 2051 map->map_mflags &= ~(MF_VALID|MF_OPEN); 2052 return NULL; 2053 } 2054 if (bitset(MF_MATCHONLY, map->map_mflags)) 2055 return map_rewrite(map, name, strlen(name), NULL); 2056 else 2057 return map_rewrite(map, vp, vsize, av); 2058 } 2059 2060 2061 /* 2062 ** NIS_GETCANONNAME -- look up canonical name in NIS 2063 */ 2064 2065 bool 2066 nis_getcanonname(name, hbsize, statp) 2067 char *name; 2068 int hbsize; 2069 int *statp; 2070 { 2071 char *vp; 2072 auto int vsize; 2073 int keylen; 2074 int yperr; 2075 static bool try0null = TRUE; 2076 static bool try1null = TRUE; 2077 static char *yp_domain = NULL; 2078 char host_record[MAXLINE]; 2079 char cbuf[MAXNAME]; 2080 char nbuf[MAXNAME + 1]; 2081 2082 if (tTd(38, 20)) 2083 printf("nis_getcanonname(%s)\n", name); 2084 2085 if (strlen(name) >= sizeof nbuf) 2086 { 2087 *statp = EX_UNAVAILABLE; 2088 return FALSE; 2089 } 2090 (void) strcpy(nbuf, name); 2091 shorten_hostname(nbuf); 2092 keylen = strlen(nbuf); 2093 2094 if (yp_domain == NULL) 2095 yp_get_default_domain(&yp_domain); 2096 makelower(nbuf); 2097 yperr = YPERR_KEY; 2098 if (try0null) 2099 { 2100 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, 2101 &vp, &vsize); 2102 if (yperr == 0) 2103 try1null = FALSE; 2104 } 2105 if (yperr == YPERR_KEY && try1null) 2106 { 2107 keylen++; 2108 yperr = yp_match(yp_domain, "hosts.byname", nbuf, keylen, 2109 &vp, &vsize); 2110 if (yperr == 0) 2111 try0null = FALSE; 2112 } 2113 if (yperr != 0) 2114 { 2115 if (yperr == YPERR_KEY) 2116 *statp = EX_NOHOST; 2117 else if (yperr == YPERR_BUSY) 2118 *statp = EX_TEMPFAIL; 2119 else 2120 *statp = EX_UNAVAILABLE; 2121 return FALSE; 2122 } 2123 if (vsize >= sizeof host_record) 2124 vsize = sizeof host_record - 1; 2125 strncpy(host_record, vp, vsize); 2126 host_record[vsize] = '\0'; 2127 if (tTd(38, 44)) 2128 printf("got record `%s'\n", host_record); 2129 if (!extract_canonname(nbuf, host_record, cbuf, sizeof cbuf)) 2130 { 2131 /* this should not happen, but.... */ 2132 *statp = EX_NOHOST; 2133 return FALSE; 2134 } 2135 if (hbsize < strlen(cbuf)) 2136 { 2137 *statp = EX_UNAVAILABLE; 2138 return FALSE; 2139 } 2140 strcpy(name, cbuf); 2141 *statp = EX_OK; 2142 return TRUE; 2143 } 2144 2145 #endif 2146 /* 2147 ** NISPLUS Modules 2148 ** 2149 ** This code donated by Sun Microsystems. 2150 */ 2151 2152 #ifdef NISPLUS 2153 2154 #undef NIS /* symbol conflict in nis.h */ 2155 #undef T_UNSPEC /* symbol conflict in nis.h -> ... -> sys/tiuser.h */ 2156 #include <rpcsvc/nis.h> 2157 #include <rpcsvc/nislib.h> 2158 2159 #define EN_col(col) zo_data.objdata_u.en_data.en_cols.en_cols_val[(col)].ec_value.ec_value_val 2160 #define COL_NAME(res,i) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_val)[i].tc_name 2161 #define COL_MAX(res) ((res->objects.objects_val)->TA_data.ta_cols.ta_cols_len) 2162 #define PARTIAL_NAME(x) ((x)[strlen(x) - 1] != '.') 2163 2164 /* 2165 ** NISPLUS_MAP_OPEN -- open nisplus table 2166 */ 2167 2168 bool 2169 nisplus_map_open(map, mode) 2170 MAP *map; 2171 int mode; 2172 { 2173 nis_result *res = NULL; 2174 int retry_cnt, max_col, i; 2175 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 2176 2177 if (tTd(38, 2)) 2178 printf("nisplus_map_open(%s, %s, %d)\n", 2179 map->map_mname, map->map_file, mode); 2180 2181 mode &= O_ACCMODE; 2182 if (mode != O_RDONLY) 2183 { 2184 errno = EPERM; 2185 return FALSE; 2186 } 2187 2188 if (*map->map_file == '\0') 2189 map->map_file = "mail_aliases.org_dir"; 2190 2191 if (PARTIAL_NAME(map->map_file) && map->map_domain == NULL) 2192 { 2193 /* set default NISPLUS Domain to $m */ 2194 extern char *nisplus_default_domain __P((void)); 2195 2196 map->map_domain = newstr(nisplus_default_domain()); 2197 if (tTd(38, 2)) 2198 printf("nisplus_map_open(%s): using domain %s\n", 2199 map->map_file, map->map_domain); 2200 } 2201 if (!PARTIAL_NAME(map->map_file)) 2202 { 2203 map->map_domain = newstr(""); 2204 snprintf(qbuf, sizeof qbuf, "%s", map->map_file); 2205 } 2206 else 2207 { 2208 /* check to see if this map actually exists */ 2209 snprintf(qbuf, sizeof qbuf, "%s.%s", 2210 map->map_file, map->map_domain); 2211 } 2212 2213 retry_cnt = 0; 2214 while (res == NULL || res->status != NIS_SUCCESS) 2215 { 2216 res = nis_lookup(qbuf, FOLLOW_LINKS); 2217 switch (res->status) 2218 { 2219 case NIS_SUCCESS: 2220 break; 2221 2222 case NIS_TRYAGAIN: 2223 case NIS_RPCERROR: 2224 case NIS_NAMEUNREACHABLE: 2225 if (retry_cnt++ > 4) 2226 { 2227 errno = EAGAIN; 2228 return FALSE; 2229 } 2230 /* try not to overwhelm hosed server */ 2231 sleep(2); 2232 break; 2233 2234 default: /* all other nisplus errors */ 2235 #if 0 2236 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2237 syserr("421 Cannot find table %s.%s: %s", 2238 map->map_file, map->map_domain, 2239 nis_sperrno(res->status)); 2240 #endif 2241 errno = EAGAIN; 2242 return FALSE; 2243 } 2244 } 2245 2246 if (NIS_RES_NUMOBJ(res) != 1 || 2247 (NIS_RES_OBJECT(res)->zo_data.zo_type != TABLE_OBJ)) 2248 { 2249 if (tTd(38, 10)) 2250 printf("nisplus_map_open: %s is not a table\n", qbuf); 2251 #if 0 2252 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2253 syserr("421 %s.%s: %s is not a table", 2254 map->map_file, map->map_domain, 2255 nis_sperrno(res->status)); 2256 #endif 2257 errno = EBADF; 2258 return FALSE; 2259 } 2260 /* default key column is column 0 */ 2261 if (map->map_keycolnm == NULL) 2262 map->map_keycolnm = newstr(COL_NAME(res,0)); 2263 2264 max_col = COL_MAX(res); 2265 2266 /* verify the key column exist */ 2267 for (i=0; i< max_col; i++) 2268 { 2269 if (!strcmp(map->map_keycolnm, COL_NAME(res,i))) 2270 break; 2271 } 2272 if (i == max_col) 2273 { 2274 if (tTd(38, 2)) 2275 printf("nisplus_map_open(%s): can not find key column %s\n", 2276 map->map_file, map->map_keycolnm); 2277 errno = ENOENT; 2278 return FALSE; 2279 } 2280 2281 /* default value column is the last column */ 2282 if (map->map_valcolnm == NULL) 2283 { 2284 map->map_valcolno = max_col - 1; 2285 return TRUE; 2286 } 2287 2288 for (i=0; i< max_col; i++) 2289 { 2290 if (strcmp(map->map_valcolnm, COL_NAME(res,i)) == 0) 2291 { 2292 map->map_valcolno = i; 2293 return TRUE; 2294 } 2295 } 2296 2297 if (tTd(38, 2)) 2298 printf("nisplus_map_open(%s): can not find column %s\n", 2299 map->map_file, map->map_keycolnm); 2300 errno = ENOENT; 2301 return FALSE; 2302 } 2303 2304 2305 /* 2306 ** NISPLUS_MAP_LOOKUP -- look up a datum in a NISPLUS table 2307 */ 2308 2309 char * 2310 nisplus_map_lookup(map, name, av, statp) 2311 MAP *map; 2312 char *name; 2313 char **av; 2314 int *statp; 2315 { 2316 char *p; 2317 auto int vsize; 2318 char *skp; 2319 int skleft; 2320 char search_key[MAXNAME + 4]; 2321 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 2322 nis_result *result; 2323 2324 if (tTd(38, 20)) 2325 printf("nisplus_map_lookup(%s, %s)\n", 2326 map->map_mname, name); 2327 2328 if (!bitset(MF_OPEN, map->map_mflags)) 2329 { 2330 if (nisplus_map_open(map, O_RDONLY)) 2331 { 2332 map->map_mflags |= MF_OPEN; 2333 map->map_pid = getpid(); 2334 } 2335 else 2336 { 2337 *statp = EX_UNAVAILABLE; 2338 return NULL; 2339 } 2340 } 2341 2342 /* 2343 ** Copy the name to the key buffer, escaping double quote characters 2344 ** by doubling them and quoting "]" and "," to avoid having the 2345 ** NIS+ parser choke on them. 2346 */ 2347 2348 skleft = sizeof search_key - 4; 2349 skp = search_key; 2350 for (p = name; *p != '\0' && skleft > 0; p++) 2351 { 2352 switch (*p) 2353 { 2354 case ']': 2355 case ',': 2356 /* quote the character */ 2357 *skp++ = '"'; 2358 *skp++ = *p; 2359 *skp++ = '"'; 2360 skleft -= 3; 2361 break; 2362 2363 case '"': 2364 /* double the quote */ 2365 *skp++ = '"'; 2366 skleft--; 2367 /* fall through... */ 2368 2369 default: 2370 *skp++ = *p; 2371 skleft--; 2372 break; 2373 } 2374 } 2375 *skp = '\0'; 2376 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2377 makelower(search_key); 2378 2379 /* construct the query */ 2380 if (PARTIAL_NAME(map->map_file)) 2381 snprintf(qbuf, sizeof qbuf, "[%s=%s],%s.%s", 2382 map->map_keycolnm, search_key, map->map_file, 2383 map->map_domain); 2384 else 2385 snprintf(qbuf, sizeof qbuf, "[%s=%s],%s", 2386 map->map_keycolnm, search_key, map->map_file); 2387 2388 if (tTd(38, 20)) 2389 printf("qbuf=%s\n", qbuf); 2390 result = nis_list(qbuf, FOLLOW_LINKS | FOLLOW_PATH, NULL, NULL); 2391 if (result->status == NIS_SUCCESS) 2392 { 2393 int count; 2394 char *str; 2395 2396 if ((count = NIS_RES_NUMOBJ(result)) != 1) 2397 { 2398 if (LogLevel > 10) 2399 sm_syslog(LOG_WARNING, CurEnv->e_id, 2400 "%s: lookup error, expected 1 entry, got %d", 2401 map->map_file, count); 2402 2403 /* ignore second entry */ 2404 if (tTd(38, 20)) 2405 printf("nisplus_map_lookup(%s), got %d entries, additional entries ignored\n", 2406 name, count); 2407 } 2408 2409 p = ((NIS_RES_OBJECT(result))->EN_col(map->map_valcolno)); 2410 /* set the length of the result */ 2411 if (p == NULL) 2412 p = ""; 2413 vsize = strlen(p); 2414 if (tTd(38, 20)) 2415 printf("nisplus_map_lookup(%s), found %s\n", 2416 name, p); 2417 if (bitset(MF_MATCHONLY, map->map_mflags)) 2418 str = map_rewrite(map, name, strlen(name), NULL); 2419 else 2420 str = map_rewrite(map, p, vsize, av); 2421 nis_freeresult(result); 2422 *statp = EX_OK; 2423 return str; 2424 } 2425 else 2426 { 2427 if (result->status == NIS_NOTFOUND) 2428 *statp = EX_NOTFOUND; 2429 else if (result->status == NIS_TRYAGAIN) 2430 *statp = EX_TEMPFAIL; 2431 else 2432 { 2433 *statp = EX_UNAVAILABLE; 2434 map->map_mflags &= ~(MF_VALID|MF_OPEN); 2435 } 2436 } 2437 if (tTd(38, 20)) 2438 printf("nisplus_map_lookup(%s), failed\n", name); 2439 nis_freeresult(result); 2440 return NULL; 2441 } 2442 2443 2444 2445 /* 2446 ** NISPLUS_GETCANONNAME -- look up canonical name in NIS+ 2447 */ 2448 2449 bool 2450 nisplus_getcanonname(name, hbsize, statp) 2451 char *name; 2452 int hbsize; 2453 int *statp; 2454 { 2455 char *vp; 2456 auto int vsize; 2457 nis_result *result; 2458 char *p; 2459 char nbuf[MAXNAME + 1]; 2460 char qbuf[MAXLINE + NIS_MAXNAMELEN]; 2461 2462 if (strlen(name) >= sizeof nbuf) 2463 { 2464 *statp = EX_UNAVAILABLE; 2465 return FALSE; 2466 } 2467 (void) strcpy(nbuf, name); 2468 shorten_hostname(nbuf); 2469 2470 p = strchr(nbuf, '.'); 2471 if (p == NULL) 2472 { 2473 /* single token */ 2474 snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir", nbuf); 2475 } 2476 else if (p[1] != '\0') 2477 { 2478 /* multi token -- take only first token in nbuf */ 2479 *p = '\0'; 2480 snprintf(qbuf, sizeof qbuf, "[name=%s],hosts.org_dir.%s", 2481 nbuf, &p[1]); 2482 } 2483 else 2484 { 2485 *statp = EX_NOHOST; 2486 return FALSE; 2487 } 2488 2489 if (tTd(38, 20)) 2490 printf("\nnisplus_getcanoname(%s), qbuf=%s\n", 2491 name, qbuf); 2492 2493 result = nis_list(qbuf, EXPAND_NAME|FOLLOW_LINKS|FOLLOW_PATH, 2494 NULL, NULL); 2495 2496 if (result->status == NIS_SUCCESS) 2497 { 2498 int count; 2499 char *domain; 2500 2501 if ((count = NIS_RES_NUMOBJ(result)) != 1) 2502 { 2503 if (LogLevel > 10) 2504 sm_syslog(LOG_WARNING, CurEnv->e_id, 2505 "nisplus_getcanonname: lookup error, expected 1 entry, got %d", 2506 count); 2507 2508 /* ignore second entry */ 2509 if (tTd(38, 20)) 2510 printf("nisplus_getcanoname(%s), got %d entries, all but first ignored\n", 2511 name, count); 2512 } 2513 2514 if (tTd(38, 20)) 2515 printf("nisplus_getcanoname(%s), found in directory \"%s\"\n", 2516 name, (NIS_RES_OBJECT(result))->zo_domain); 2517 2518 2519 vp = ((NIS_RES_OBJECT(result))->EN_col(0)); 2520 vsize = strlen(vp); 2521 if (tTd(38, 20)) 2522 printf("nisplus_getcanonname(%s), found %s\n", 2523 name, vp); 2524 if (strchr(vp, '.') != NULL) 2525 { 2526 domain = ""; 2527 } 2528 else 2529 { 2530 domain = macvalue('m', CurEnv); 2531 if (domain == NULL) 2532 domain = ""; 2533 } 2534 if (hbsize > vsize + (int) strlen(domain) + 1) 2535 { 2536 if (domain[0] == '\0') 2537 strcpy(name, vp); 2538 else 2539 snprintf(name, hbsize, "%s.%s", vp, domain); 2540 *statp = EX_OK; 2541 } 2542 else 2543 *statp = EX_NOHOST; 2544 nis_freeresult(result); 2545 return TRUE; 2546 } 2547 else 2548 { 2549 if (result->status == NIS_NOTFOUND) 2550 *statp = EX_NOHOST; 2551 else if (result->status == NIS_TRYAGAIN) 2552 *statp = EX_TEMPFAIL; 2553 else 2554 *statp = EX_UNAVAILABLE; 2555 } 2556 if (tTd(38, 20)) 2557 printf("nisplus_getcanonname(%s), failed, status=%d, nsw_stat=%d\n", 2558 name, result->status, *statp); 2559 nis_freeresult(result); 2560 return FALSE; 2561 } 2562 2563 2564 char * 2565 nisplus_default_domain() 2566 { 2567 static char default_domain[MAXNAME + 1] = ""; 2568 char *p; 2569 2570 if (default_domain[0] != '\0') 2571 return(default_domain); 2572 2573 p = nis_local_directory(); 2574 snprintf(default_domain, sizeof default_domain, "%s", p); 2575 return default_domain; 2576 } 2577 2578 #endif /* NISPLUS */ 2579 /* 2580 ** LDAP Modules 2581 ** 2582 ** Contributed by Booker C. Bense <bbense@networking.stanford.edu>. 2583 ** Get your support from him. 2584 */ 2585 2586 #ifdef LDAPMAP 2587 2588 # undef NEEDGETOPT /* used for something else in LDAP */ 2589 2590 # include <lber.h> 2591 # include <ldap.h> 2592 # include "ldap_map.h" 2593 2594 /* 2595 ** LDAP_MAP_OPEN -- open LDAP map 2596 ** 2597 ** Since LDAP is TCP-based there is not much we can or should do 2598 ** here. It might be a good idea to attempt an open/close here. 2599 */ 2600 2601 bool 2602 ldap_map_open(map, mode) 2603 MAP *map; 2604 int mode; 2605 { 2606 if (tTd(38, 2)) 2607 printf("ldap_map_open(%s, %d)\n", map->map_mname, mode); 2608 2609 mode &= O_ACCMODE; 2610 if (mode != O_RDONLY) 2611 { 2612 /* issue a pseudo-error message */ 2613 #ifdef ENOSYS 2614 errno = ENOSYS; 2615 #else 2616 # ifdef EFTYPE 2617 errno = EFTYPE; 2618 # else 2619 errno = ENXIO; 2620 # endif 2621 #endif 2622 return FALSE; 2623 } 2624 return TRUE; 2625 } 2626 2627 2628 /* 2629 ** LDAP_MAP_START -- actually open LDAP map 2630 ** 2631 ** Caching should be investigated. 2632 */ 2633 2634 static jmp_buf LDAPTimeout; 2635 2636 static void 2637 ldaptimeout(sig_no) 2638 int sig_no; 2639 { 2640 longjmp(LDAPTimeout, 1); 2641 } 2642 2643 bool 2644 ldap_map_start(map) 2645 MAP *map; 2646 { 2647 LDAP_MAP_STRUCT *lmap; 2648 LDAP *ld; 2649 register EVENT *ev = NULL; 2650 2651 if (tTd(38, 2)) 2652 printf("ldap_map_start(%s)\n", map->map_mname); 2653 2654 lmap = (LDAP_MAP_STRUCT *) map->map_db1; 2655 2656 if (tTd(38,9)) 2657 printf("ldap_open(%s, %d)\n", lmap->ldaphost, lmap->ldapport); 2658 2659 /* Need to set an alarm here, ldap_open is hopelessly broken. */ 2660 2661 /* set the timeout */ 2662 if (lmap->timeout.tv_sec != 0) 2663 { 2664 if (setjmp(LDAPTimeout) != 0) 2665 { 2666 if (LogLevel > 1) 2667 sm_syslog(LOG_NOTICE, CurEnv->e_id, 2668 "timeout waiting for ldap_open to %.100s", 2669 lmap->ldaphost); 2670 return (FALSE); 2671 } 2672 ev = setevent(lmap->timeout.tv_sec, ldaptimeout, 0); 2673 } 2674 2675 #ifdef USE_LDAP_INIT 2676 ld = ldap_init(lmap->ldaphost,lmap->ldapport); 2677 #else 2678 ld = ldap_open(lmap->ldaphost,lmap->ldapport); 2679 #endif 2680 2681 /* clear the event if it has not sprung */ 2682 if (lmap->timeout.tv_sec != 0) 2683 clrevent(ev); 2684 2685 if (ld == NULL) 2686 { 2687 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2688 { 2689 syserr("%sldapopen failed to %s in map %s", 2690 bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ", 2691 lmap->ldaphost, map->map_mname); 2692 } 2693 return FALSE; 2694 } 2695 2696 #ifdef USE_LDAP_SET_OPTION 2697 ldap_set_option(ld, LDAP_OPT_DEREF, &lmap->deref); 2698 ldap_set_option(ld, LDAP_OPT_TIMELIMIT, &lmap->timelimit); 2699 ldap_set_option(ld, LDAP_OPT_SIZELIMIT, &lmap->sizelimit); 2700 ldap_set_option(ld, LDAP_OPT_REFERRALS, 2701 bitset(LDAP_OPT_REFERRALS, lmap->ldap_options) ? 2702 LDAP_OPT_ON : LDAP_OPT_OFF); 2703 #else 2704 /* From here on in we can use ldap internal timelimits */ 2705 ld->ld_deref = lmap->deref; 2706 ld->ld_timelimit = lmap->timelimit; 2707 ld->ld_sizelimit = lmap->sizelimit; 2708 ld->ld_options = lmap->ldap_options; 2709 #endif 2710 2711 #ifdef USE_LDAP_INIT 2712 /* ld needs to be cast into the map struct */ 2713 lmap->ld = ld; 2714 return TRUE; 2715 #else 2716 if (ldap_bind_s(ld, lmap->binddn,lmap->passwd,lmap->method) != LDAP_SUCCESS) 2717 { 2718 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2719 { 2720 syserr("421 Cannot bind to map %s in ldap server %s", 2721 map->map_mname, lmap->ldaphost); 2722 } 2723 } 2724 else 2725 { 2726 /* We need to cast ld into the map structure */ 2727 lmap->ld = ld; 2728 return TRUE; 2729 } 2730 2731 return FALSE; 2732 #endif 2733 } 2734 2735 2736 /* 2737 ** LDAP_MAP_STOP -- close the ldap connection 2738 */ 2739 2740 void 2741 ldap_map_stop(map) 2742 MAP *map; 2743 { 2744 LDAP_MAP_STRUCT *lmap; 2745 2746 lmap = (LDAP_MAP_STRUCT *) map->map_db1; 2747 if (lmap->ld != NULL) 2748 { 2749 ldap_unbind(lmap->ld); 2750 lmap->ld = NULL; 2751 } 2752 } 2753 2754 /* 2755 ** LDAP_MAP_CLOSE -- close ldap map 2756 */ 2757 2758 void 2759 ldap_map_close(map) 2760 MAP *map; 2761 { 2762 ldap_map_stop(map); 2763 } 2764 2765 #ifdef SUNET_ID 2766 /* 2767 ** SUNET_ID_HASH -- Convert a string to it's Sunet_id canonical form 2768 ** This only makes sense at Stanford University. 2769 */ 2770 2771 char * 2772 sunet_id_hash(str) 2773 char *str; 2774 { 2775 char *p, *p_last; 2776 2777 p = str; 2778 p_last = p; 2779 while (*p != '\0') 2780 { 2781 if (islower(*p) || isdigit(*p)) 2782 { 2783 *p_last = *p; 2784 p_last++; 2785 } 2786 else if (isupper(*p)) 2787 { 2788 *p_last = tolower(*p); 2789 p_last++; 2790 } 2791 ++p; 2792 } 2793 if (*p_last != '\0') 2794 *p_last = '\0'; 2795 return (str); 2796 } 2797 2798 2799 2800 #endif /* SUNET_ID */ 2801 /* 2802 ** LDAP_MAP_LOOKUP -- look up a datum in a LDAP map 2803 */ 2804 2805 char * 2806 ldap_map_lookup(map, name, av, statp) 2807 MAP *map; 2808 char *name; 2809 char **av; 2810 int *statp; 2811 { 2812 LDAP_MAP_STRUCT *lmap = NULL; 2813 LDAPMessage *entry; 2814 char *vp; 2815 auto int vsize; 2816 char keybuf[MAXNAME + 1]; 2817 char filter[LDAP_MAP_MAX_FILTER + 1]; 2818 char **attr_values = NULL; 2819 char *result; 2820 int name_len; 2821 char *fp, *p, *q; 2822 2823 if (tTd(38, 20)) 2824 printf("ldap_map_lookup(%s, %s)\n", map->map_mname, name); 2825 2826 /* actually open the map */ 2827 if (!ldap_map_start(map)) 2828 { 2829 result = NULL; 2830 *statp = EX_TEMPFAIL; 2831 goto quick_exit; 2832 } 2833 2834 /* Get ldap struct pointer from map */ 2835 lmap = (LDAP_MAP_STRUCT *) map->map_db1; 2836 2837 name_len = strlen(name); 2838 if (name_len > MAXNAME) 2839 name_len = MAXNAME; 2840 strncpy(keybuf, name, name_len); 2841 keybuf[name_len] = '\0'; 2842 2843 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 2844 #ifdef SUNET_ID 2845 sunet_id_hash(keybuf); 2846 #else 2847 makelower(keybuf); 2848 #endif /*SUNET_ID */ 2849 2850 /* substitute keybuf into filter, perhaps multiple times */ 2851 fp = filter; 2852 p = lmap->filter; 2853 while ((q = strchr(p, '%')) != NULL) 2854 { 2855 if (q[1] == 's') 2856 { 2857 snprintf(fp, SPACELEFT(filter, fp), "%.*s%s", 2858 q - p, p, keybuf); 2859 p = q + 2; 2860 } 2861 else 2862 { 2863 snprintf(fp, SPACELEFT(filter, fp), "%.*s", 2864 q - p + 1, p); 2865 p = q + (q[1] == '%' ? 2 : 1); 2866 } 2867 fp += strlen(fp); 2868 } 2869 snprintf(fp, SPACELEFT(filter, fp), "%s", p); 2870 if (tTd(38, 20)) 2871 printf("ldap search filter=%s\n", filter); 2872 2873 if (ldap_search_st(lmap->ld, lmap->base,lmap->scope,filter, 2874 lmap->attr, lmap->attrsonly, &(lmap->timeout), 2875 &(lmap->res)) != LDAP_SUCCESS) 2876 { 2877 /* try stopping/starting map */ 2878 ldap_map_stop(map); 2879 if (!ldap_map_start(map)) 2880 { 2881 result = NULL; 2882 *statp = EX_TEMPFAIL; 2883 goto quick_exit; 2884 } 2885 if (ldap_search_st(lmap->ld, lmap->base, lmap->scope, filter, 2886 lmap->attr, lmap->attrsonly, 2887 &(lmap->timeout), &(lmap->res)) 2888 != LDAP_SUCCESS) 2889 { 2890 if (!bitset(MF_OPTIONAL, map->map_mflags)) 2891 { 2892 syserr("%sError in ldap_search_st using %s in map %s", 2893 bitset(MF_NODEFER, map->map_mflags) ? "" : "421 ", 2894 filter, map->map_mname); 2895 } 2896 result = NULL; 2897 *statp = EX_TEMPFAIL; 2898 goto quick_exit; 2899 } 2900 } 2901 2902 entry = ldap_first_entry(lmap->ld,lmap->res); 2903 if (entry == NULL) 2904 { 2905 result = NULL; 2906 *statp = EX_NOTFOUND; 2907 goto quick_exit; 2908 } 2909 2910 /* Need to build the args for map_rewrite here */ 2911 attr_values = ldap_get_values(lmap->ld,entry,lmap->attr[0]); 2912 if (attr_values == NULL) 2913 { 2914 /* bad things happened */ 2915 result = NULL; 2916 *statp = EX_NOTFOUND; 2917 goto quick_exit; 2918 } 2919 2920 *statp = EX_OK; 2921 2922 /* If there is more that one use the first */ 2923 vp = attr_values[0]; 2924 vsize = strlen(vp); 2925 2926 if (LogLevel > 9) 2927 sm_syslog(LOG_INFO, CurEnv->e_id, 2928 "ldap %.100s => %s", 2929 name, vp); 2930 if (bitset(MF_MATCHONLY, map->map_mflags)) 2931 result = map_rewrite(map, name, strlen(name), NULL); 2932 else 2933 result = map_rewrite(map, vp, vsize, av); 2934 2935 quick_exit: 2936 if (attr_values != NULL) 2937 ldap_value_free(attr_values); 2938 if (lmap != NULL) 2939 ldap_msgfree(lmap->res); 2940 ldap_map_stop(map); 2941 return result ; 2942 } 2943 2944 2945 /* 2946 ** LDAP_MAP_DEQUOTE - helper routine for ldap_map_parseargs 2947 */ 2948 2949 char * 2950 ldap_map_dequote(str) 2951 char *str; 2952 { 2953 char *p; 2954 char *start; 2955 p = str; 2956 2957 if (*p == '"') 2958 { 2959 start = ++p; 2960 /* Should probably swallow initial whitespace here */ 2961 } 2962 else 2963 { 2964 return(str); 2965 } 2966 while (*p != '"' && *p != '\0') 2967 { 2968 p++; 2969 } 2970 if (*p != '\0') 2971 *p = '\0'; 2972 return start; 2973 } 2974 2975 /* 2976 ** LDAP_MAP_PARSEARGS -- parse ldap map definition args. 2977 */ 2978 2979 bool 2980 ldap_map_parseargs(map,args) 2981 MAP *map; 2982 char *args; 2983 { 2984 register char *p = args; 2985 register int done; 2986 LDAP_MAP_STRUCT *lmap; 2987 2988 /* We need to alloc an LDAP_MAP_STRUCT struct */ 2989 lmap = (LDAP_MAP_STRUCT *) xalloc(sizeof(LDAP_MAP_STRUCT)); 2990 2991 /* Set default int's here , default strings below */ 2992 lmap->ldapport = DEFAULT_LDAP_MAP_PORT; 2993 lmap->deref = DEFAULT_LDAP_MAP_DEREF; 2994 lmap->timelimit = DEFAULT_LDAP_MAP_TIMELIMIT; 2995 lmap->sizelimit = DEFAULT_LDAP_MAP_SIZELIMIT; 2996 lmap->ldap_options = DEFAULT_LDAP_MAP_LDAP_OPTIONS; 2997 lmap->method = DEFAULT_LDAP_MAP_METHOD; 2998 lmap->scope = DEFAULT_LDAP_MAP_SCOPE; 2999 lmap->attrsonly = DEFAULT_LDAP_MAP_ATTRSONLY; 3000 lmap->timeout.tv_sec = DEFAULT_LDAP_MAP_TIMELIMIT; 3001 lmap->timeout.tv_usec = 0; 3002 3003 /* Default char ptrs to NULL */ 3004 lmap->binddn = NULL; 3005 lmap->passwd = NULL; 3006 lmap->base = NULL; 3007 lmap->ldaphost = NULL; 3008 3009 /* Default general ptrs to NULL */ 3010 lmap->ld = NULL; 3011 lmap->res = NULL; 3012 3013 map->map_mflags |= MF_TRY0NULL | MF_TRY1NULL; 3014 for (;;) 3015 { 3016 while (isascii(*p) && isspace(*p)) 3017 p++; 3018 if (*p != '-') 3019 break; 3020 switch (*++p) 3021 { 3022 case 'N': 3023 map->map_mflags |= MF_INCLNULL; 3024 map->map_mflags &= ~MF_TRY0NULL; 3025 break; 3026 3027 case 'O': 3028 map->map_mflags &= ~MF_TRY1NULL; 3029 break; 3030 3031 case 'o': 3032 map->map_mflags |= MF_OPTIONAL; 3033 break; 3034 3035 case 'f': 3036 map->map_mflags |= MF_NOFOLDCASE; 3037 break; 3038 3039 case 'm': 3040 map->map_mflags |= MF_MATCHONLY; 3041 break; 3042 3043 case 'A': 3044 map->map_mflags |= MF_APPEND; 3045 break; 3046 3047 case 'q': 3048 map->map_mflags |= MF_KEEPQUOTES; 3049 break; 3050 3051 case 't': 3052 map->map_mflags |= MF_NODEFER; 3053 break; 3054 3055 case 'a': 3056 map->map_app = ++p; 3057 break; 3058 3059 case 'T': 3060 map->map_tapp = ++p; 3061 break; 3062 3063 /* Start of ldap_map specific args */ 3064 case 'k': /* search field */ 3065 while (isascii(*++p) && isspace(*p)) 3066 continue; 3067 lmap->filter = p; 3068 break; 3069 3070 case 'v': /* attr to return */ 3071 while (isascii(*++p) && isspace(*p)) 3072 continue; 3073 lmap->attr[0] = p; 3074 lmap->attr[1] = NULL; 3075 break; 3076 3077 /* args stolen from ldapsearch.c */ 3078 case 'R': /* don't auto chase referrals */ 3079 #ifdef LDAP_REFERRALS 3080 lmap->ldap_options &= ~LDAP_OPT_REFERRALS; 3081 #else /* LDAP_REFERRALS */ 3082 syserr("compile with -DLDAP_REFERRALS for referral support\n"); 3083 #endif /* LDAP_REFERRALS */ 3084 break; 3085 3086 case 'n': /* retrieve attribute names only -- no values */ 3087 lmap->attrsonly += 1; 3088 break; 3089 3090 case 's': /* search scope */ 3091 if (strncasecmp(++p, "base", 4) == 0) 3092 { 3093 lmap->scope = LDAP_SCOPE_BASE; 3094 } 3095 else if (strncasecmp(p, "one", 3) == 0) 3096 { 3097 lmap->scope = LDAP_SCOPE_ONELEVEL; 3098 } 3099 else if (strncasecmp(p, "sub", 3) == 0) 3100 { 3101 lmap->scope = LDAP_SCOPE_SUBTREE; 3102 } 3103 else 3104 { /* bad config line */ 3105 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 3106 { 3107 char *ptr; 3108 3109 if ((ptr = strchr(p, ' ')) != NULL) 3110 *ptr = '\0'; 3111 syserr("Scope must be [base|one|sub] not %s in map %s", 3112 p, map->map_mname); 3113 if (ptr != NULL) 3114 *ptr = ' '; 3115 return FALSE; 3116 } 3117 } 3118 break; 3119 3120 case 'h': /* ldap host */ 3121 while (isascii(*++p) && isspace(*p)) 3122 continue; 3123 map->map_domain = p; 3124 lmap->ldaphost = p; 3125 break; 3126 3127 case 'b': /* search base */ 3128 while (isascii(*++p) && isspace(*p)) 3129 continue; 3130 lmap->base = p; 3131 break; 3132 3133 case 'p': /* ldap port */ 3134 while (isascii(*++p) && isspace(*p)) 3135 continue; 3136 lmap->ldapport = atoi(p); 3137 break; 3138 3139 case 'l': /* time limit */ 3140 while (isascii(*++p) && isspace(*p)) 3141 continue; 3142 lmap->timelimit = atoi(p); 3143 lmap->timeout.tv_sec = lmap->timelimit; 3144 break; 3145 3146 } 3147 3148 /* need to account for quoted strings here arggg... */ 3149 done = isascii(*p) && isspace(*p); 3150 while (*p != '\0' && !done) 3151 { 3152 if (*p == '"') 3153 { 3154 while (*++p != '"' && *p != '\0') 3155 { 3156 continue; 3157 } 3158 if (*p != '\0') 3159 p++; 3160 } 3161 else 3162 { 3163 p++; 3164 } 3165 done = isascii(*p) && isspace(*p); 3166 } 3167 3168 if (*p != '\0') 3169 *p++ = '\0'; 3170 } 3171 3172 if (map->map_app != NULL) 3173 map->map_app = newstr(ldap_map_dequote(map->map_app)); 3174 if (map->map_tapp != NULL) 3175 map->map_tapp = newstr(ldap_map_dequote(map->map_tapp)); 3176 if (map->map_domain != NULL) 3177 map->map_domain = newstr(ldap_map_dequote(map->map_domain)); 3178 3179 /* 3180 ** We need to swallow up all the stuff into a struct 3181 ** and dump it into map->map_dbptr1 3182 */ 3183 3184 if (lmap->ldaphost != NULL) 3185 lmap->ldaphost = newstr(ldap_map_dequote(lmap->ldaphost)); 3186 else 3187 { 3188 syserr("LDAP map: -h flag is required"); 3189 return FALSE; 3190 } 3191 3192 if (lmap->binddn != NULL) 3193 lmap->binddn = newstr(ldap_map_dequote(lmap->binddn)); 3194 else 3195 lmap->binddn = DEFAULT_LDAP_MAP_BINDDN; 3196 3197 3198 if (lmap->passwd != NULL) 3199 lmap->passwd = newstr(ldap_map_dequote(lmap->passwd)); 3200 else 3201 lmap->passwd = DEFAULT_LDAP_MAP_PASSWD; 3202 3203 if (lmap->base != NULL) 3204 lmap->base = newstr(ldap_map_dequote(lmap->base)); 3205 else 3206 { 3207 syserr("LDAP map: -b flag is required"); 3208 return FALSE; 3209 } 3210 3211 3212 if (lmap->filter != NULL) 3213 lmap->filter = newstr(ldap_map_dequote(lmap->filter)); 3214 else 3215 { 3216 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 3217 { 3218 syserr("No filter given in map %s", map->map_mname); 3219 return FALSE; 3220 } 3221 } 3222 if (lmap->attr[0] != NULL) 3223 lmap->attr[0] = newstr(ldap_map_dequote(lmap->attr[0])); 3224 else 3225 { 3226 if (!bitset(MCF_OPTFILE, map->map_class->map_cflags)) 3227 { 3228 syserr("No return attribute in %s", map->map_mname); 3229 return FALSE; 3230 } 3231 } 3232 3233 map->map_db1 = (ARBPTR_T) lmap; 3234 return TRUE; 3235 } 3236 3237 #endif /* LDAP Modules */ 3238 /* 3239 ** syslog map 3240 */ 3241 3242 #if _FFR_MAP_SYSLOG 3243 3244 #define map_prio map_lockfd /* overload field */ 3245 3246 /* 3247 ** SYSLOG_MAP_PARSEARGS -- check for priority level to syslog messages. 3248 */ 3249 3250 bool 3251 syslog_map_parseargs(map, args) 3252 MAP *map; 3253 char *args; 3254 { 3255 char *p = args; 3256 char *priority = NULL; 3257 3258 for (;;) 3259 { 3260 while (isascii(*p) && isspace(*p)) 3261 p++; 3262 if (*p != '-') 3263 break; 3264 if (*++p == 'L') 3265 priority = ++p; 3266 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 3267 p++; 3268 if (*p != '\0') 3269 *p++ = '\0'; 3270 } 3271 3272 if (priority == NULL) 3273 map->map_prio = LOG_INFO; 3274 else 3275 { 3276 if (strncasecmp("LOG_", priority, 4) == 0) 3277 priority += 4; 3278 3279 #ifdef LOG_EMERG 3280 if (strcasecmp("EMERG", priority) == 0) 3281 map->map_prio = LOG_EMERG; 3282 else 3283 #endif 3284 #ifdef LOG_ALERT 3285 if (strcasecmp("ALERT", priority) == 0) 3286 map->map_prio = LOG_ALERT; 3287 else 3288 #endif 3289 #ifdef LOG_CRIT 3290 if (strcasecmp("CRIT", priority) == 0) 3291 map->map_prio = LOG_CRIT; 3292 else 3293 #endif 3294 #ifdef LOG_ERR 3295 if (strcasecmp("ERR", priority) == 0) 3296 map->map_prio = LOG_ERR; 3297 else 3298 #endif 3299 #ifdef LOG_WARNING 3300 if (strcasecmp("WARNING", priority) == 0) 3301 map->map_prio = LOG_WARNING; 3302 else 3303 #endif 3304 #ifdef LOG_NOTICE 3305 if (strcasecmp("NOTICE", priority) == 0) 3306 map->map_prio = LOG_NOTICE; 3307 else 3308 #endif 3309 #ifdef LOG_INFO 3310 if (strcasecmp("INFO", priority) == 0) 3311 map->map_prio = LOG_INFO; 3312 else 3313 #endif 3314 #ifdef LOG_DEBUG 3315 if (strcasecmp("DEBUG", priority) == 0) 3316 map->map_prio = LOG_DEBUG; 3317 else 3318 #endif 3319 { 3320 syserr("syslog_map_parseargs: Unknown priority %s\n", 3321 priority); 3322 return FALSE; 3323 } 3324 } 3325 return TRUE; 3326 } 3327 3328 /* 3329 ** SYSLOG_MAP_LOOKUP -- rewrite and syslog message. Always return empty string 3330 */ 3331 3332 char * 3333 syslog_map_lookup(map, string, args, statp) 3334 MAP *map; 3335 char *string; 3336 char **args; 3337 int *statp; 3338 { 3339 char *ptr = map_rewrite(map, string, strlen(string), args); 3340 3341 if (ptr != NULL) 3342 { 3343 if (tTd(38, 20)) 3344 printf("syslog_map_lookup(%s (priority %d): %s\n", 3345 map->map_mname, map->map_prio, ptr); 3346 3347 sm_syslog(map->map_prio, CurEnv->e_id, "%s", ptr); 3348 } 3349 3350 *statp = EX_OK; 3351 return ""; 3352 } 3353 3354 #endif /* _FFR_MAP_SYSLOG */ 3355 /* 3356 ** HESIOD Modules 3357 */ 3358 3359 #ifdef HESIOD 3360 3361 bool 3362 hes_map_open(map, mode) 3363 MAP *map; 3364 int mode; 3365 { 3366 if (tTd(38, 2)) 3367 printf("hes_map_open(%s, %s, %d)\n", 3368 map->map_mname, map->map_file, mode); 3369 3370 if (mode != O_RDONLY) 3371 { 3372 /* issue a pseudo-error message */ 3373 #ifdef ENOSYS 3374 errno = ENOSYS; 3375 #else 3376 # ifdef EFTYPE 3377 errno = EFTYPE; 3378 # else 3379 errno = ENXIO; 3380 # endif 3381 #endif 3382 return FALSE; 3383 } 3384 3385 #ifdef HESIOD_INIT 3386 if (HesiodContext != NULL || hesiod_init(&HesiodContext) == 0) 3387 return TRUE; 3388 3389 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3390 syserr("421 cannot initialize Hesiod map (%s)", 3391 errstring(errno)); 3392 return FALSE; 3393 #else 3394 if (hes_error() == HES_ER_UNINIT) 3395 hes_init(); 3396 switch (hes_error()) 3397 { 3398 case HES_ER_OK: 3399 case HES_ER_NOTFOUND: 3400 return TRUE; 3401 } 3402 3403 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3404 syserr("421 cannot initialize Hesiod map (%d)", hes_error()); 3405 3406 return FALSE; 3407 #endif /* HESIOD_INIT */ 3408 } 3409 3410 char * 3411 hes_map_lookup(map, name, av, statp) 3412 MAP *map; 3413 char *name; 3414 char **av; 3415 int *statp; 3416 { 3417 char **hp; 3418 3419 if (tTd(38, 20)) 3420 printf("hes_map_lookup(%s, %s)\n", map->map_file, name); 3421 3422 if (name[0] == '\\') 3423 { 3424 char *np; 3425 int nl; 3426 char nbuf[MAXNAME]; 3427 3428 nl = strlen(name); 3429 if (nl < sizeof nbuf - 1) 3430 np = nbuf; 3431 else 3432 np = xalloc(strlen(name) + 2); 3433 np[0] = '\\'; 3434 strcpy(&np[1], name); 3435 #ifdef HESIOD_INIT 3436 hp = hesiod_resolve(HesiodContext, np, map->map_file); 3437 #else 3438 hp = hes_resolve(np, map->map_file); 3439 #endif /* HESIOD_INIT */ 3440 if (np != nbuf) 3441 free(np); 3442 } 3443 else 3444 { 3445 #ifdef HESIOD_INIT 3446 hp = hesiod_resolve(HesiodContext, name, map->map_file); 3447 #else 3448 hp = hes_resolve(name, map->map_file); 3449 #endif /* HESIOD_INIT */ 3450 } 3451 #ifdef HESIOD_INIT 3452 if (hp == NULL) 3453 return NULL; 3454 if (*hp == NULL) 3455 { 3456 hesiod_free_list(HesiodContext, hp); 3457 switch (errno) 3458 { 3459 case ENOENT: 3460 *statp = EX_NOTFOUND; 3461 break; 3462 case ECONNREFUSED: 3463 case EMSGSIZE: 3464 *statp = EX_TEMPFAIL; 3465 break; 3466 case ENOMEM: 3467 default: 3468 *statp = EX_UNAVAILABLE; 3469 break; 3470 } 3471 return NULL; 3472 } 3473 #else 3474 if (hp == NULL || hp[0] == NULL) 3475 { 3476 switch (hes_error()) 3477 { 3478 case HES_ER_OK: 3479 *statp = EX_OK; 3480 break; 3481 3482 case HES_ER_NOTFOUND: 3483 *statp = EX_NOTFOUND; 3484 break; 3485 3486 case HES_ER_CONFIG: 3487 *statp = EX_UNAVAILABLE; 3488 break; 3489 3490 case HES_ER_NET: 3491 *statp = EX_TEMPFAIL; 3492 break; 3493 } 3494 return NULL; 3495 } 3496 #endif /* HESIOD_INIT */ 3497 3498 if (bitset(MF_MATCHONLY, map->map_mflags)) 3499 return map_rewrite(map, name, strlen(name), NULL); 3500 else 3501 return map_rewrite(map, hp[0], strlen(hp[0]), av); 3502 } 3503 3504 #endif 3505 /* 3506 ** NeXT NETINFO Modules 3507 */ 3508 3509 #if NETINFO 3510 3511 # define NETINFO_DEFAULT_DIR "/aliases" 3512 # define NETINFO_DEFAULT_PROPERTY "members" 3513 3514 extern char *ni_propval __P((char *, char *, char *, char *, int)); 3515 3516 3517 /* 3518 ** NI_MAP_OPEN -- open NetInfo Aliases 3519 */ 3520 3521 bool 3522 ni_map_open(map, mode) 3523 MAP *map; 3524 int mode; 3525 { 3526 if (tTd(38, 2)) 3527 printf("ni_map_open(%s, %s, %d)\n", 3528 map->map_mname, map->map_file, mode); 3529 mode &= O_ACCMODE; 3530 3531 if (*map->map_file == '\0') 3532 map->map_file = NETINFO_DEFAULT_DIR; 3533 3534 if (map->map_valcolnm == NULL) 3535 map->map_valcolnm = NETINFO_DEFAULT_PROPERTY; 3536 3537 if (map->map_coldelim == '\0' && bitset(MF_ALIAS, map->map_mflags)) 3538 map->map_coldelim = ','; 3539 3540 return TRUE; 3541 } 3542 3543 3544 /* 3545 ** NI_MAP_LOOKUP -- look up a datum in NetInfo 3546 */ 3547 3548 char * 3549 ni_map_lookup(map, name, av, statp) 3550 MAP *map; 3551 char *name; 3552 char **av; 3553 int *statp; 3554 { 3555 char *res; 3556 char *propval; 3557 3558 if (tTd(38, 20)) 3559 printf("ni_map_lookup(%s, %s)\n", map->map_mname, name); 3560 3561 propval = ni_propval(map->map_file, map->map_keycolnm, name, 3562 map->map_valcolnm, map->map_coldelim); 3563 3564 if (propval == NULL) 3565 return NULL; 3566 3567 if (bitset(MF_MATCHONLY, map->map_mflags)) 3568 res = map_rewrite(map, name, strlen(name), NULL); 3569 else 3570 res = map_rewrite(map, propval, strlen(propval), av); 3571 free(propval); 3572 return res; 3573 } 3574 3575 3576 bool 3577 ni_getcanonname(name, hbsize, statp) 3578 char *name; 3579 int hbsize; 3580 int *statp; 3581 { 3582 char *vptr; 3583 char *ptr; 3584 char nbuf[MAXNAME + 1]; 3585 3586 if (tTd(38, 20)) 3587 printf("ni_getcanonname(%s)\n", name); 3588 3589 if (strlen(name) >= sizeof nbuf) 3590 { 3591 *statp = EX_UNAVAILABLE; 3592 return FALSE; 3593 } 3594 (void) strcpy(nbuf, name); 3595 shorten_hostname(nbuf); 3596 3597 /* we only accept single token search key */ 3598 if (strchr(nbuf, '.')) 3599 { 3600 *statp = EX_NOHOST; 3601 return FALSE; 3602 } 3603 3604 /* Do the search */ 3605 vptr = ni_propval("/machines", NULL, nbuf, "name", '\n'); 3606 3607 if (vptr == NULL) 3608 { 3609 *statp = EX_NOHOST; 3610 return FALSE; 3611 } 3612 3613 /* Only want the first machine name */ 3614 if ((ptr = strchr(vptr, '\n')) != NULL) 3615 *ptr = '\0'; 3616 3617 if (hbsize >= strlen(vptr)) 3618 { 3619 strcpy(name, vptr); 3620 *statp = EX_OK; 3621 return TRUE; 3622 } 3623 *statp = EX_UNAVAILABLE; 3624 free(vptr); 3625 return FALSE; 3626 } 3627 3628 3629 /* 3630 ** NI_PROPVAL -- NetInfo property value lookup routine 3631 ** 3632 ** Parameters: 3633 ** keydir -- the NetInfo directory name in which to search 3634 ** for the key. 3635 ** keyprop -- the name of the property in which to find the 3636 ** property we are interested. Defaults to "name". 3637 ** keyval -- the value for which we are really searching. 3638 ** valprop -- the property name for the value in which we 3639 ** are interested. 3640 ** sepchar -- if non-nil, this can be multiple-valued, and 3641 ** we should return a string separated by this 3642 ** character. 3643 ** 3644 ** Returns: 3645 ** NULL -- if: 3646 ** 1. the directory is not found 3647 ** 2. the property name is not found 3648 ** 3. the property contains multiple values 3649 ** 4. some error occured 3650 ** else -- the value of the lookup. 3651 ** 3652 ** Example: 3653 ** To search for an alias value, use: 3654 ** ni_propval("/aliases", "name", aliasname, "members", ',') 3655 ** 3656 ** Notes: 3657 ** Caller should free the return value of ni_proval 3658 */ 3659 3660 # include <netinfo/ni.h> 3661 3662 # define LOCAL_NETINFO_DOMAIN "." 3663 # define PARENT_NETINFO_DOMAIN ".." 3664 # define MAX_NI_LEVELS 256 3665 3666 char * 3667 ni_propval(keydir, keyprop, keyval, valprop, sepchar) 3668 char *keydir; 3669 char *keyprop; 3670 char *keyval; 3671 char *valprop; 3672 int sepchar; 3673 { 3674 char *propval = NULL; 3675 int i; 3676 int j, alen; 3677 void *ni = NULL; 3678 void *lastni = NULL; 3679 ni_status nis; 3680 ni_id nid; 3681 ni_namelist ninl; 3682 register char *p; 3683 char keybuf[1024]; 3684 3685 /* 3686 ** Create the full key from the two parts. 3687 ** 3688 ** Note that directory can end with, e.g., "name=" to specify 3689 ** an alternate search property. 3690 */ 3691 3692 i = strlen(keydir) + strlen(keyval) + 2; 3693 if (keyprop != NULL) 3694 i += strlen(keyprop) + 1; 3695 if (i > sizeof keybuf) 3696 return NULL; 3697 strcpy(keybuf, keydir); 3698 strcat(keybuf, "/"); 3699 if (keyprop != NULL) 3700 { 3701 strcat(keybuf, keyprop); 3702 strcat(keybuf, "="); 3703 } 3704 strcat(keybuf, keyval); 3705 3706 if (tTd(38, 21)) 3707 printf("ni_propval(%s, %s, %s, %s, %d) keybuf='%s'\n", 3708 keydir, keyprop, keyval, valprop, sepchar, keybuf); 3709 /* 3710 ** If the passed directory and property name are found 3711 ** in one of netinfo domains we need to search (starting 3712 ** from the local domain moving all the way back to the 3713 ** root domain) set propval to the property's value 3714 ** and return it. 3715 */ 3716 3717 for (i = 0; i < MAX_NI_LEVELS && propval == NULL; i++) 3718 { 3719 if (i == 0) 3720 { 3721 nis = ni_open(NULL, LOCAL_NETINFO_DOMAIN, &ni); 3722 if (tTd(38, 20)) 3723 printf("ni_open(LOCAL) = %d\n", nis); 3724 } 3725 else 3726 { 3727 if (lastni != NULL) 3728 ni_free(lastni); 3729 lastni = ni; 3730 nis = ni_open(lastni, PARENT_NETINFO_DOMAIN, &ni); 3731 if (tTd(38, 20)) 3732 printf("ni_open(PARENT) = %d\n", nis); 3733 } 3734 3735 /* 3736 ** Don't bother if we didn't get a handle on a 3737 ** proper domain. This is not necessarily an error. 3738 ** We would get a positive ni_status if, for instance 3739 ** we never found the directory or property and tried 3740 ** to open the parent of the root domain! 3741 */ 3742 3743 if (nis != 0) 3744 break; 3745 3746 /* 3747 ** Find the path to the server information. 3748 */ 3749 3750 if (ni_pathsearch(ni, &nid, keybuf) != 0) 3751 continue; 3752 3753 /* 3754 ** Find associated value information. 3755 */ 3756 3757 if (ni_lookupprop(ni, &nid, valprop, &ninl) != 0) 3758 continue; 3759 3760 if (tTd(38, 20)) 3761 printf("ni_lookupprop: len=%d\n", ninl.ni_namelist_len); 3762 /* 3763 ** See if we have an acceptable number of values. 3764 */ 3765 3766 if (ninl.ni_namelist_len <= 0) 3767 continue; 3768 3769 if (sepchar == '\0' && ninl.ni_namelist_len > 1) 3770 { 3771 ni_namelist_free(&ninl); 3772 continue; 3773 } 3774 3775 /* 3776 ** Calculate number of bytes needed and build result 3777 */ 3778 3779 alen = 1; 3780 for (j = 0; j < ninl.ni_namelist_len; j++) 3781 alen += strlen(ninl.ni_namelist_val[j]) + 1; 3782 propval = p = xalloc(alen); 3783 for (j = 0; j < ninl.ni_namelist_len; j++) 3784 { 3785 strcpy(p, ninl.ni_namelist_val[j]); 3786 p += strlen(p); 3787 *p++ = sepchar; 3788 } 3789 *--p = '\0'; 3790 3791 ni_namelist_free(&ninl); 3792 } 3793 3794 /* 3795 ** Clean up. 3796 */ 3797 3798 if (ni != NULL) 3799 ni_free(ni); 3800 if (lastni != NULL && ni != lastni) 3801 ni_free(lastni); 3802 if (tTd(38, 20)) 3803 printf("ni_propval returns: '%s'\n", propval); 3804 3805 return propval; 3806 } 3807 3808 #endif /* NETINFO */ 3809 /* 3810 ** TEXT (unindexed text file) Modules 3811 ** 3812 ** This code donated by Sun Microsystems. 3813 */ 3814 3815 #define map_sff map_lockfd /* overload field */ 3816 3817 3818 /* 3819 ** TEXT_MAP_OPEN -- open text table 3820 */ 3821 3822 bool 3823 text_map_open(map, mode) 3824 MAP *map; 3825 int mode; 3826 { 3827 int sff; 3828 int i; 3829 3830 if (tTd(38, 2)) 3831 printf("text_map_open(%s, %s, %d)\n", 3832 map->map_mname, map->map_file, mode); 3833 3834 mode &= O_ACCMODE; 3835 if (mode != O_RDONLY) 3836 { 3837 errno = EPERM; 3838 return FALSE; 3839 } 3840 3841 if (*map->map_file == '\0') 3842 { 3843 syserr("text map \"%s\": file name required", 3844 map->map_mname); 3845 return FALSE; 3846 } 3847 3848 if (map->map_file[0] != '/') 3849 { 3850 syserr("text map \"%s\": file name must be fully qualified", 3851 map->map_mname); 3852 return FALSE; 3853 } 3854 3855 sff = SFF_ROOTOK|SFF_REGONLY; 3856 if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 3857 sff |= SFF_NOWLINK; 3858 if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 3859 sff |= SFF_SAFEDIRPATH; 3860 if ((i = safefile(map->map_file, RunAsUid, RunAsGid, RunAsUserName, 3861 sff, S_IRUSR, NULL)) != 0) 3862 { 3863 /* cannot open this map */ 3864 if (tTd(38, 2)) 3865 printf("\tunsafe map file: %d\n", i); 3866 if (!bitset(MF_OPTIONAL, map->map_mflags)) 3867 syserr("text map \"%s\": unsafe map file %s", 3868 map->map_mname, map->map_file); 3869 return FALSE; 3870 } 3871 3872 if (map->map_keycolnm == NULL) 3873 map->map_keycolno = 0; 3874 else 3875 { 3876 if (!(isascii(*map->map_keycolnm) && isdigit(*map->map_keycolnm))) 3877 { 3878 syserr("text map \"%s\", file %s: -k should specify a number, not %s", 3879 map->map_mname, map->map_file, 3880 map->map_keycolnm); 3881 return FALSE; 3882 } 3883 map->map_keycolno = atoi(map->map_keycolnm); 3884 } 3885 3886 if (map->map_valcolnm == NULL) 3887 map->map_valcolno = 0; 3888 else 3889 { 3890 if (!(isascii(*map->map_valcolnm) && isdigit(*map->map_valcolnm))) 3891 { 3892 syserr("text map \"%s\", file %s: -v should specify a number, not %s", 3893 map->map_mname, map->map_file, 3894 map->map_valcolnm); 3895 return FALSE; 3896 } 3897 map->map_valcolno = atoi(map->map_valcolnm); 3898 } 3899 3900 if (tTd(38, 2)) 3901 { 3902 printf("text_map_open(%s, %s): delimiter = ", 3903 map->map_mname, map->map_file); 3904 if (map->map_coldelim == '\0') 3905 printf("(white space)\n"); 3906 else 3907 printf("%c\n", map->map_coldelim); 3908 } 3909 3910 map->map_sff = sff; 3911 return TRUE; 3912 } 3913 3914 3915 /* 3916 ** TEXT_MAP_LOOKUP -- look up a datum in a TEXT table 3917 */ 3918 3919 char * 3920 text_map_lookup(map, name, av, statp) 3921 MAP *map; 3922 char *name; 3923 char **av; 3924 int *statp; 3925 { 3926 char *vp; 3927 auto int vsize; 3928 int buflen; 3929 FILE *f; 3930 char delim; 3931 int key_idx; 3932 bool found_it; 3933 int sff = map->map_sff; 3934 char search_key[MAXNAME + 1]; 3935 char linebuf[MAXLINE]; 3936 char buf[MAXNAME + 1]; 3937 extern char *get_column __P((char *, int, char, char *, int)); 3938 3939 found_it = FALSE; 3940 if (tTd(38, 20)) 3941 printf("text_map_lookup(%s, %s)\n", map->map_mname, name); 3942 3943 buflen = strlen(name); 3944 if (buflen > sizeof search_key - 1) 3945 buflen = sizeof search_key - 1; 3946 bcopy(name, search_key, buflen); 3947 search_key[buflen] = '\0'; 3948 if (!bitset(MF_NOFOLDCASE, map->map_mflags)) 3949 makelower(search_key); 3950 3951 f = safefopen(map->map_file, O_RDONLY, FileMode, sff); 3952 if (f == NULL) 3953 { 3954 map->map_mflags &= ~(MF_VALID|MF_OPEN); 3955 *statp = EX_UNAVAILABLE; 3956 return NULL; 3957 } 3958 key_idx = map->map_keycolno; 3959 delim = map->map_coldelim; 3960 while (fgets(linebuf, MAXLINE, f) != NULL) 3961 { 3962 char *p; 3963 3964 /* skip comment line */ 3965 if (linebuf[0] == '#') 3966 continue; 3967 p = strchr(linebuf, '\n'); 3968 if (p != NULL) 3969 *p = '\0'; 3970 p = get_column(linebuf, key_idx, delim, buf, sizeof buf); 3971 if (p != NULL && strcasecmp(search_key, p) == 0) 3972 { 3973 found_it = TRUE; 3974 break; 3975 } 3976 } 3977 fclose(f); 3978 if (!found_it) 3979 { 3980 *statp = EX_NOTFOUND; 3981 return NULL; 3982 } 3983 vp = get_column(linebuf, map->map_valcolno, delim, buf, sizeof buf); 3984 if (vp == NULL) 3985 { 3986 *statp = EX_NOTFOUND; 3987 return NULL; 3988 } 3989 vsize = strlen(vp); 3990 *statp = EX_OK; 3991 if (bitset(MF_MATCHONLY, map->map_mflags)) 3992 return map_rewrite(map, name, strlen(name), NULL); 3993 else 3994 return map_rewrite(map, vp, vsize, av); 3995 } 3996 3997 3998 /* 3999 ** TEXT_GETCANONNAME -- look up canonical name in hosts file 4000 */ 4001 4002 bool 4003 text_getcanonname(name, hbsize, statp) 4004 char *name; 4005 int hbsize; 4006 int *statp; 4007 { 4008 bool found; 4009 FILE *f; 4010 char linebuf[MAXLINE]; 4011 char cbuf[MAXNAME + 1]; 4012 char nbuf[MAXNAME + 1]; 4013 4014 if (tTd(38, 20)) 4015 printf("text_getcanonname(%s)\n", name); 4016 4017 if (strlen(name) >= (SIZE_T) sizeof nbuf) 4018 { 4019 *statp = EX_UNAVAILABLE; 4020 return FALSE; 4021 } 4022 (void) strcpy(nbuf, name); 4023 shorten_hostname(nbuf); 4024 4025 f = fopen(HostsFile, "r"); 4026 if (f == NULL) 4027 { 4028 *statp = EX_UNAVAILABLE; 4029 return FALSE; 4030 } 4031 found = FALSE; 4032 while (!found && fgets(linebuf, MAXLINE, f) != NULL) 4033 { 4034 char *p = strpbrk(linebuf, "#\n"); 4035 4036 if (p != NULL) 4037 *p = '\0'; 4038 if (linebuf[0] != '\0') 4039 found = extract_canonname(nbuf, linebuf, cbuf, sizeof cbuf); 4040 } 4041 fclose(f); 4042 if (!found) 4043 { 4044 *statp = EX_NOHOST; 4045 return FALSE; 4046 } 4047 4048 if ((SIZE_T) hbsize >= strlen(cbuf)) 4049 { 4050 strcpy(name, cbuf); 4051 *statp = EX_OK; 4052 return TRUE; 4053 } 4054 *statp = EX_UNAVAILABLE; 4055 return FALSE; 4056 } 4057 /* 4058 ** STAB (Symbol Table) Modules 4059 */ 4060 4061 4062 /* 4063 ** STAB_MAP_LOOKUP -- look up alias in symbol table 4064 */ 4065 4066 /* ARGSUSED2 */ 4067 char * 4068 stab_map_lookup(map, name, av, pstat) 4069 register MAP *map; 4070 char *name; 4071 char **av; 4072 int *pstat; 4073 { 4074 register STAB *s; 4075 4076 if (tTd(38, 20)) 4077 printf("stab_lookup(%s, %s)\n", 4078 map->map_mname, name); 4079 4080 s = stab(name, ST_ALIAS, ST_FIND); 4081 if (s != NULL) 4082 return (s->s_alias); 4083 return (NULL); 4084 } 4085 4086 4087 /* 4088 ** STAB_MAP_STORE -- store in symtab (actually using during init, not rebuild) 4089 */ 4090 4091 void 4092 stab_map_store(map, lhs, rhs) 4093 register MAP *map; 4094 char *lhs; 4095 char *rhs; 4096 { 4097 register STAB *s; 4098 4099 s = stab(lhs, ST_ALIAS, ST_ENTER); 4100 s->s_alias = newstr(rhs); 4101 } 4102 4103 4104 /* 4105 ** STAB_MAP_OPEN -- initialize (reads data file) 4106 ** 4107 ** This is a wierd case -- it is only intended as a fallback for 4108 ** aliases. For this reason, opens for write (only during a 4109 ** "newaliases") always fails, and opens for read open the 4110 ** actual underlying text file instead of the database. 4111 */ 4112 4113 bool 4114 stab_map_open(map, mode) 4115 register MAP *map; 4116 int mode; 4117 { 4118 FILE *af; 4119 int sff; 4120 struct stat st; 4121 4122 if (tTd(38, 2)) 4123 printf("stab_map_open(%s, %s, %d)\n", 4124 map->map_mname, map->map_file, mode); 4125 4126 mode &= O_ACCMODE; 4127 if (mode != O_RDONLY) 4128 { 4129 errno = EPERM; 4130 return FALSE; 4131 } 4132 4133 sff = SFF_ROOTOK|SFF_REGONLY; 4134 if (!bitset(DBS_LINKEDMAPINWRITABLEDIR, DontBlameSendmail)) 4135 sff |= SFF_NOWLINK; 4136 if (!bitset(DBS_MAPINUNSAFEDIRPATH, DontBlameSendmail)) 4137 sff |= SFF_SAFEDIRPATH; 4138 af = safefopen(map->map_file, O_RDONLY, 0444, sff); 4139 if (af == NULL) 4140 return FALSE; 4141 readaliases(map, af, FALSE, FALSE); 4142 4143 if (fstat(fileno(af), &st) >= 0) 4144 map->map_mtime = st.st_mtime; 4145 fclose(af); 4146 4147 return TRUE; 4148 } 4149 /* 4150 ** Implicit Modules 4151 ** 4152 ** Tries several types. For back compatibility of aliases. 4153 */ 4154 4155 4156 /* 4157 ** IMPL_MAP_LOOKUP -- lookup in best open database 4158 */ 4159 4160 char * 4161 impl_map_lookup(map, name, av, pstat) 4162 MAP *map; 4163 char *name; 4164 char **av; 4165 int *pstat; 4166 { 4167 if (tTd(38, 20)) 4168 printf("impl_map_lookup(%s, %s)\n", 4169 map->map_mname, name); 4170 4171 #ifdef NEWDB 4172 if (bitset(MF_IMPL_HASH, map->map_mflags)) 4173 return db_map_lookup(map, name, av, pstat); 4174 #endif 4175 #ifdef NDBM 4176 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 4177 return ndbm_map_lookup(map, name, av, pstat); 4178 #endif 4179 return stab_map_lookup(map, name, av, pstat); 4180 } 4181 4182 /* 4183 ** IMPL_MAP_STORE -- store in open databases 4184 */ 4185 4186 void 4187 impl_map_store(map, lhs, rhs) 4188 MAP *map; 4189 char *lhs; 4190 char *rhs; 4191 { 4192 if (tTd(38, 12)) 4193 printf("impl_map_store(%s, %s, %s)\n", 4194 map->map_mname, lhs, rhs); 4195 #ifdef NEWDB 4196 if (bitset(MF_IMPL_HASH, map->map_mflags)) 4197 db_map_store(map, lhs, rhs); 4198 #endif 4199 #ifdef NDBM 4200 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 4201 ndbm_map_store(map, lhs, rhs); 4202 #endif 4203 stab_map_store(map, lhs, rhs); 4204 } 4205 4206 /* 4207 ** IMPL_MAP_OPEN -- implicit database open 4208 */ 4209 4210 bool 4211 impl_map_open(map, mode) 4212 MAP *map; 4213 int mode; 4214 { 4215 if (tTd(38, 2)) 4216 printf("impl_map_open(%s, %s, %d)\n", 4217 map->map_mname, map->map_file, mode); 4218 4219 mode &= O_ACCMODE; 4220 #ifdef NEWDB 4221 map->map_mflags |= MF_IMPL_HASH; 4222 if (hash_map_open(map, mode)) 4223 { 4224 # ifdef NDBM_YP_COMPAT 4225 if (mode == O_RDONLY || strstr(map->map_file, "/yp/") == NULL) 4226 # endif 4227 return TRUE; 4228 } 4229 else 4230 map->map_mflags &= ~MF_IMPL_HASH; 4231 #endif 4232 #ifdef NDBM 4233 map->map_mflags |= MF_IMPL_NDBM; 4234 if (ndbm_map_open(map, mode)) 4235 { 4236 return TRUE; 4237 } 4238 else 4239 map->map_mflags &= ~MF_IMPL_NDBM; 4240 #endif 4241 4242 #if defined(NEWDB) || defined(NDBM) 4243 if (Verbose) 4244 message("WARNING: cannot open alias database %s%s", 4245 map->map_file, 4246 mode == O_RDONLY ? "; reading text version" : ""); 4247 #else 4248 if (mode != O_RDONLY) 4249 usrerr("Cannot rebuild aliases: no database format defined"); 4250 #endif 4251 4252 if (mode == O_RDONLY) 4253 return stab_map_open(map, mode); 4254 else 4255 return FALSE; 4256 } 4257 4258 4259 /* 4260 ** IMPL_MAP_CLOSE -- close any open database(s) 4261 */ 4262 4263 void 4264 impl_map_close(map) 4265 MAP *map; 4266 { 4267 if (tTd(38, 9)) 4268 printf("impl_map_close(%s, %s, %lx)\n", 4269 map->map_mname, map->map_file, map->map_mflags); 4270 #ifdef NEWDB 4271 if (bitset(MF_IMPL_HASH, map->map_mflags)) 4272 { 4273 db_map_close(map); 4274 map->map_mflags &= ~MF_IMPL_HASH; 4275 } 4276 #endif 4277 4278 #ifdef NDBM 4279 if (bitset(MF_IMPL_NDBM, map->map_mflags)) 4280 { 4281 ndbm_map_close(map); 4282 map->map_mflags &= ~MF_IMPL_NDBM; 4283 } 4284 #endif 4285 } 4286 /* 4287 ** User map class. 4288 ** 4289 ** Provides access to the system password file. 4290 */ 4291 4292 /* 4293 ** USER_MAP_OPEN -- open user map 4294 ** 4295 ** Really just binds field names to field numbers. 4296 */ 4297 4298 bool 4299 user_map_open(map, mode) 4300 MAP *map; 4301 int mode; 4302 { 4303 if (tTd(38, 2)) 4304 printf("user_map_open(%s, %d)\n", 4305 map->map_mname, mode); 4306 4307 mode &= O_ACCMODE; 4308 if (mode != O_RDONLY) 4309 { 4310 /* issue a pseudo-error message */ 4311 #ifdef ENOSYS 4312 errno = ENOSYS; 4313 #else 4314 # ifdef EFTYPE 4315 errno = EFTYPE; 4316 # else 4317 errno = ENXIO; 4318 # endif 4319 #endif 4320 return FALSE; 4321 } 4322 if (map->map_valcolnm == NULL) 4323 /* nothing */ ; 4324 else if (strcasecmp(map->map_valcolnm, "name") == 0) 4325 map->map_valcolno = 1; 4326 else if (strcasecmp(map->map_valcolnm, "passwd") == 0) 4327 map->map_valcolno = 2; 4328 else if (strcasecmp(map->map_valcolnm, "uid") == 0) 4329 map->map_valcolno = 3; 4330 else if (strcasecmp(map->map_valcolnm, "gid") == 0) 4331 map->map_valcolno = 4; 4332 else if (strcasecmp(map->map_valcolnm, "gecos") == 0) 4333 map->map_valcolno = 5; 4334 else if (strcasecmp(map->map_valcolnm, "dir") == 0) 4335 map->map_valcolno = 6; 4336 else if (strcasecmp(map->map_valcolnm, "shell") == 0) 4337 map->map_valcolno = 7; 4338 else 4339 { 4340 syserr("User map %s: unknown column name %s", 4341 map->map_mname, map->map_valcolnm); 4342 return FALSE; 4343 } 4344 return TRUE; 4345 } 4346 4347 4348 /* 4349 ** USER_MAP_LOOKUP -- look up a user in the passwd file. 4350 */ 4351 4352 /* ARGSUSED3 */ 4353 char * 4354 user_map_lookup(map, key, av, statp) 4355 MAP *map; 4356 char *key; 4357 char **av; 4358 int *statp; 4359 { 4360 struct passwd *pw; 4361 auto bool fuzzy; 4362 4363 if (tTd(38, 20)) 4364 printf("user_map_lookup(%s, %s)\n", 4365 map->map_mname, key); 4366 4367 pw = finduser(key, &fuzzy); 4368 if (pw == NULL) 4369 return NULL; 4370 if (bitset(MF_MATCHONLY, map->map_mflags)) 4371 return map_rewrite(map, key, strlen(key), NULL); 4372 else 4373 { 4374 char *rwval = NULL; 4375 char buf[30]; 4376 4377 switch (map->map_valcolno) 4378 { 4379 case 0: 4380 case 1: 4381 rwval = pw->pw_name; 4382 break; 4383 4384 case 2: 4385 rwval = pw->pw_passwd; 4386 break; 4387 4388 case 3: 4389 snprintf(buf, sizeof buf, "%d", pw->pw_uid); 4390 rwval = buf; 4391 break; 4392 4393 case 4: 4394 snprintf(buf, sizeof buf, "%d", pw->pw_gid); 4395 rwval = buf; 4396 break; 4397 4398 case 5: 4399 rwval = pw->pw_gecos; 4400 break; 4401 4402 case 6: 4403 rwval = pw->pw_dir; 4404 break; 4405 4406 case 7: 4407 rwval = pw->pw_shell; 4408 break; 4409 } 4410 return map_rewrite(map, rwval, strlen(rwval), av); 4411 } 4412 } 4413 /* 4414 ** Program map type. 4415 ** 4416 ** This provides access to arbitrary programs. It should be used 4417 ** only very sparingly, since there is no way to bound the cost 4418 ** of invoking an arbitrary program. 4419 */ 4420 4421 char * 4422 prog_map_lookup(map, name, av, statp) 4423 MAP *map; 4424 char *name; 4425 char **av; 4426 int *statp; 4427 { 4428 int i; 4429 register char *p; 4430 int fd; 4431 auto pid_t pid; 4432 char *rval; 4433 int stat; 4434 char *argv[MAXPV + 1]; 4435 char buf[MAXLINE]; 4436 4437 if (tTd(38, 20)) 4438 printf("prog_map_lookup(%s, %s) %s\n", 4439 map->map_mname, name, map->map_file); 4440 4441 i = 0; 4442 argv[i++] = map->map_file; 4443 if (map->map_rebuild != NULL) 4444 { 4445 snprintf(buf, sizeof buf, "%s", map->map_rebuild); 4446 for (p = strtok(buf, " \t"); p != NULL; p = strtok(NULL, " \t")) 4447 { 4448 if (i >= MAXPV - 1) 4449 break; 4450 argv[i++] = p; 4451 } 4452 } 4453 argv[i++] = name; 4454 argv[i] = NULL; 4455 if (tTd(38, 21)) 4456 { 4457 printf("prog_open:"); 4458 for (i = 0; argv[i] != NULL; i++) 4459 printf(" %s", argv[i]); 4460 printf("\n"); 4461 } 4462 (void) blocksignal(SIGCHLD); 4463 pid = prog_open(argv, &fd, CurEnv); 4464 if (pid < 0) 4465 { 4466 if (!bitset(MF_OPTIONAL, map->map_mflags)) 4467 syserr("prog_map_lookup(%s) failed (%s) -- closing", 4468 map->map_mname, errstring(errno)); 4469 else if (tTd(38, 9)) 4470 printf("prog_map_lookup(%s) failed (%s) -- closing", 4471 map->map_mname, errstring(errno)); 4472 map->map_mflags &= ~(MF_VALID|MF_OPEN); 4473 *statp = EX_OSFILE; 4474 return NULL; 4475 } 4476 i = read(fd, buf, sizeof buf - 1); 4477 if (i < 0) 4478 { 4479 syserr("prog_map_lookup(%s): read error %s\n", 4480 map->map_mname, errstring(errno)); 4481 rval = NULL; 4482 } 4483 else if (i == 0) 4484 { 4485 if (tTd(38, 20)) 4486 printf("prog_map_lookup(%s): empty answer\n", 4487 map->map_mname); 4488 rval = NULL; 4489 } 4490 else 4491 { 4492 buf[i] = '\0'; 4493 p = strchr(buf, '\n'); 4494 if (p != NULL) 4495 *p = '\0'; 4496 4497 /* collect the return value */ 4498 if (bitset(MF_MATCHONLY, map->map_mflags)) 4499 rval = map_rewrite(map, name, strlen(name), NULL); 4500 else 4501 rval = map_rewrite(map, buf, strlen(buf), NULL); 4502 4503 /* now flush any additional output */ 4504 while ((i = read(fd, buf, sizeof buf)) > 0) 4505 continue; 4506 } 4507 4508 /* wait for the process to terminate */ 4509 close(fd); 4510 stat = waitfor(pid); 4511 (void) releasesignal(SIGCHLD); 4512 4513 if (stat == -1) 4514 { 4515 syserr("prog_map_lookup(%s): wait error %s\n", 4516 map->map_mname, errstring(errno)); 4517 *statp = EX_SOFTWARE; 4518 rval = NULL; 4519 } 4520 else if (WIFEXITED(stat)) 4521 { 4522 if ((*statp = WEXITSTATUS(stat)) != EX_OK) 4523 rval = NULL; 4524 } 4525 else 4526 { 4527 syserr("prog_map_lookup(%s): child died on signal %d", 4528 map->map_mname, stat); 4529 *statp = EX_UNAVAILABLE; 4530 rval = NULL; 4531 } 4532 return rval; 4533 } 4534 /* 4535 ** Sequenced map type. 4536 ** 4537 ** Tries each map in order until something matches, much like 4538 ** implicit. Stores go to the first map in the list that can 4539 ** support storing. 4540 ** 4541 ** This is slightly unusual in that there are two interfaces. 4542 ** The "sequence" interface lets you stack maps arbitrarily. 4543 ** The "switch" interface builds a sequence map by looking 4544 ** at a system-dependent configuration file such as 4545 ** /etc/nsswitch.conf on Solaris or /etc/svc.conf on Ultrix. 4546 ** 4547 ** We don't need an explicit open, since all maps are 4548 ** opened during startup, including underlying maps. 4549 */ 4550 4551 /* 4552 ** SEQ_MAP_PARSE -- Sequenced map parsing 4553 */ 4554 4555 bool 4556 seq_map_parse(map, ap) 4557 MAP *map; 4558 char *ap; 4559 { 4560 int maxmap; 4561 4562 if (tTd(38, 2)) 4563 printf("seq_map_parse(%s, %s)\n", map->map_mname, ap); 4564 maxmap = 0; 4565 while (*ap != '\0') 4566 { 4567 register char *p; 4568 STAB *s; 4569 4570 /* find beginning of map name */ 4571 while (isascii(*ap) && isspace(*ap)) 4572 ap++; 4573 for (p = ap; isascii(*p) && isalnum(*p); p++) 4574 continue; 4575 if (*p != '\0') 4576 *p++ = '\0'; 4577 while (*p != '\0' && (!isascii(*p) || !isalnum(*p))) 4578 p++; 4579 if (*ap == '\0') 4580 { 4581 ap = p; 4582 continue; 4583 } 4584 s = stab(ap, ST_MAP, ST_FIND); 4585 if (s == NULL) 4586 { 4587 syserr("Sequence map %s: unknown member map %s", 4588 map->map_mname, ap); 4589 } 4590 else if (maxmap == MAXMAPSTACK) 4591 { 4592 syserr("Sequence map %s: too many member maps (%d max)", 4593 map->map_mname, MAXMAPSTACK); 4594 maxmap++; 4595 } 4596 else if (maxmap < MAXMAPSTACK) 4597 { 4598 map->map_stack[maxmap++] = &s->s_map; 4599 } 4600 ap = p; 4601 } 4602 return TRUE; 4603 } 4604 4605 4606 /* 4607 ** SWITCH_MAP_OPEN -- open a switched map 4608 ** 4609 ** This looks at the system-dependent configuration and builds 4610 ** a sequence map that does the same thing. 4611 ** 4612 ** Every system must define a switch_map_find routine in conf.c 4613 ** that will return the list of service types associated with a 4614 ** given service class. 4615 */ 4616 4617 bool 4618 switch_map_open(map, mode) 4619 MAP *map; 4620 int mode; 4621 { 4622 int mapno; 4623 int nmaps; 4624 char *maptype[MAXMAPSTACK]; 4625 4626 if (tTd(38, 2)) 4627 printf("switch_map_open(%s, %s, %d)\n", 4628 map->map_mname, map->map_file, mode); 4629 4630 mode &= O_ACCMODE; 4631 nmaps = switch_map_find(map->map_file, maptype, map->map_return); 4632 if (tTd(38, 19)) 4633 { 4634 printf("\tswitch_map_find => %d\n", nmaps); 4635 for (mapno = 0; mapno < nmaps; mapno++) 4636 printf("\t\t%s\n", maptype[mapno]); 4637 } 4638 if (nmaps <= 0 || nmaps > MAXMAPSTACK) 4639 return FALSE; 4640 4641 for (mapno = 0; mapno < nmaps; mapno++) 4642 { 4643 register STAB *s; 4644 char nbuf[MAXNAME + 1]; 4645 4646 if (maptype[mapno] == NULL) 4647 continue; 4648 (void) snprintf(nbuf, sizeof nbuf, "%s.%s", 4649 map->map_mname, maptype[mapno]); 4650 s = stab(nbuf, ST_MAP, ST_FIND); 4651 if (s == NULL) 4652 { 4653 syserr("Switch map %s: unknown member map %s", 4654 map->map_mname, nbuf); 4655 } 4656 else 4657 { 4658 map->map_stack[mapno] = &s->s_map; 4659 if (tTd(38, 4)) 4660 printf("\tmap_stack[%d] = %s:%s\n", 4661 mapno, s->s_map.map_class->map_cname, 4662 nbuf); 4663 } 4664 } 4665 return TRUE; 4666 } 4667 4668 4669 /* 4670 ** SEQ_MAP_CLOSE -- close all underlying maps 4671 */ 4672 4673 void 4674 seq_map_close(map) 4675 MAP *map; 4676 { 4677 int mapno; 4678 4679 if (tTd(38, 9)) 4680 printf("seq_map_close(%s)\n", map->map_mname); 4681 4682 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 4683 { 4684 MAP *mm = map->map_stack[mapno]; 4685 4686 if (mm == NULL || !bitset(MF_OPEN, mm->map_mflags)) 4687 continue; 4688 mm->map_class->map_close(mm); 4689 mm->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 4690 } 4691 } 4692 4693 4694 /* 4695 ** SEQ_MAP_LOOKUP -- sequenced map lookup 4696 */ 4697 4698 char * 4699 seq_map_lookup(map, key, args, pstat) 4700 MAP *map; 4701 char *key; 4702 char **args; 4703 int *pstat; 4704 { 4705 int mapno; 4706 int mapbit = 0x01; 4707 bool tempfail = FALSE; 4708 4709 if (tTd(38, 20)) 4710 printf("seq_map_lookup(%s, %s)\n", map->map_mname, key); 4711 4712 for (mapno = 0; mapno < MAXMAPSTACK; mapbit <<= 1, mapno++) 4713 { 4714 MAP *mm = map->map_stack[mapno]; 4715 char *rv; 4716 4717 if (mm == NULL) 4718 continue; 4719 if (!bitset(MF_OPEN, mm->map_mflags)) 4720 { 4721 if (bitset(mapbit, map->map_return[MA_UNAVAIL])) 4722 { 4723 *pstat = EX_UNAVAILABLE; 4724 return NULL; 4725 } 4726 continue; 4727 } 4728 *pstat = EX_OK; 4729 rv = mm->map_class->map_lookup(mm, key, args, pstat); 4730 if (rv != NULL) 4731 return rv; 4732 if (*pstat == EX_TEMPFAIL) 4733 { 4734 if (bitset(mapbit, map->map_return[MA_TRYAGAIN])) 4735 return NULL; 4736 tempfail = TRUE; 4737 } 4738 else if (bitset(mapbit, map->map_return[MA_NOTFOUND])) 4739 break; 4740 } 4741 if (tempfail) 4742 *pstat = EX_TEMPFAIL; 4743 else if (*pstat == EX_OK) 4744 *pstat = EX_NOTFOUND; 4745 return NULL; 4746 } 4747 4748 4749 /* 4750 ** SEQ_MAP_STORE -- sequenced map store 4751 */ 4752 4753 void 4754 seq_map_store(map, key, val) 4755 MAP *map; 4756 char *key; 4757 char *val; 4758 { 4759 int mapno; 4760 4761 if (tTd(38, 12)) 4762 printf("seq_map_store(%s, %s, %s)\n", 4763 map->map_mname, key, val); 4764 4765 for (mapno = 0; mapno < MAXMAPSTACK; mapno++) 4766 { 4767 MAP *mm = map->map_stack[mapno]; 4768 4769 if (mm == NULL || !bitset(MF_WRITABLE, mm->map_mflags)) 4770 continue; 4771 4772 mm->map_class->map_store(mm, key, val); 4773 return; 4774 } 4775 syserr("seq_map_store(%s, %s, %s): no writable map", 4776 map->map_mname, key, val); 4777 } 4778 /* 4779 ** NULL stubs 4780 */ 4781 4782 /* ARGSUSED */ 4783 bool 4784 null_map_open(map, mode) 4785 MAP *map; 4786 int mode; 4787 { 4788 return TRUE; 4789 } 4790 4791 /* ARGSUSED */ 4792 void 4793 null_map_close(map) 4794 MAP *map; 4795 { 4796 return; 4797 } 4798 4799 char * 4800 null_map_lookup(map, key, args, pstat) 4801 MAP *map; 4802 char *key; 4803 char **args; 4804 int *pstat; 4805 { 4806 *pstat = EX_NOTFOUND; 4807 return NULL; 4808 } 4809 4810 /* ARGSUSED */ 4811 void 4812 null_map_store(map, key, val) 4813 MAP *map; 4814 char *key; 4815 char *val; 4816 { 4817 return; 4818 } 4819 4820 4821 /* 4822 ** BOGUS stubs 4823 */ 4824 4825 char * 4826 bogus_map_lookup(map, key, args, pstat) 4827 MAP *map; 4828 char *key; 4829 char **args; 4830 int *pstat; 4831 { 4832 *pstat = EX_TEMPFAIL; 4833 return NULL; 4834 } 4835 4836 MAPCLASS BogusMapClass = 4837 { 4838 "bogus-map", NULL, 0, 4839 NULL, bogus_map_lookup, null_map_store, 4840 null_map_open, null_map_close, 4841 }; 4842 /* 4843 ** REGEX modules 4844 */ 4845 4846 #ifdef MAP_REGEX 4847 4848 # include <regex.h> 4849 4850 # define DEFAULT_DELIM CONDELSE 4851 4852 # define END_OF_FIELDS -1 4853 4854 # define ERRBUF_SIZE 80 4855 # define MAX_MATCH 32 4856 4857 # define xnalloc(s) memset(xalloc(s), 0, s); 4858 4859 struct regex_map 4860 { 4861 regex_t pattern_buf; /* xalloc it */ 4862 int *regex_subfields; /* move to type MAP */ 4863 char *delim; /* move to type MAP */ 4864 }; 4865 4866 static int 4867 parse_fields(s, ibuf, blen, nr_substrings) 4868 char *s; 4869 int *ibuf; /* array */ 4870 int blen; /* number of elements in ibuf */ 4871 int nr_substrings; /* number of substrings in the pattern */ 4872 { 4873 register char *cp; 4874 int i = 0; 4875 bool lastone = FALSE; 4876 4877 blen--; /* for terminating END_OF_FIELDS */ 4878 cp = s; 4879 do 4880 { 4881 for (;; cp++) 4882 { 4883 if (*cp == ',') 4884 { 4885 *cp = '\0'; 4886 break; 4887 } 4888 if (*cp == '\0') 4889 { 4890 lastone = TRUE; 4891 break; 4892 } 4893 } 4894 if (i < blen) 4895 { 4896 int val = atoi(s); 4897 4898 if (val < 0 || val >= nr_substrings) 4899 { 4900 syserr("field (%d) out of range, only %d substrings in pattern", 4901 val, nr_substrings); 4902 return -1; 4903 } 4904 ibuf[i++] = val; 4905 } 4906 else 4907 { 4908 syserr("too many fields, %d max\n", blen); 4909 return -1; 4910 } 4911 s = ++cp; 4912 } while (!lastone); 4913 ibuf[i] = END_OF_FIELDS; 4914 return i; 4915 } 4916 4917 bool 4918 regex_map_init(map, ap) 4919 MAP *map; 4920 char *ap; 4921 { 4922 int regerr; 4923 struct regex_map *map_p; 4924 register char *p; 4925 char *sub_param = NULL; 4926 int pflags; 4927 static char defdstr[] = { (char)DEFAULT_DELIM, '\0' }; 4928 4929 if (tTd(38, 2)) 4930 printf("regex_map_init: mapname '%s', args '%s'\n", 4931 map->map_mname, ap); 4932 4933 pflags = REG_ICASE | REG_EXTENDED | REG_NOSUB; 4934 4935 p = ap; 4936 4937 map_p = (struct regex_map *) xnalloc(sizeof(struct regex_map)); 4938 4939 for (;;) 4940 { 4941 while (isascii(*p) && isspace(*p)) 4942 p++; 4943 if (*p != '-') 4944 break; 4945 switch (*++p) 4946 { 4947 case 'n': /* not */ 4948 map->map_mflags |= MF_REGEX_NOT; 4949 break; 4950 4951 case 'f': /* case sensitive */ 4952 map->map_mflags |= MF_NOFOLDCASE; 4953 pflags &= ~REG_ICASE; 4954 break; 4955 4956 case 'b': /* basic regular expressions */ 4957 pflags &= ~REG_EXTENDED; 4958 break; 4959 4960 case 's': /* substring match () syntax */ 4961 sub_param = ++p; 4962 pflags &= ~REG_NOSUB; 4963 break; 4964 4965 case 'd': /* delimiter */ 4966 map_p->delim = ++p; 4967 break; 4968 4969 case 'a': /* map append */ 4970 map->map_app = ++p; 4971 break; 4972 4973 case 'm': /* matchonly */ 4974 map->map_mflags |= MF_MATCHONLY; 4975 break; 4976 4977 } 4978 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 4979 p++; 4980 if (*p != '\0') 4981 *p++ = '\0'; 4982 } 4983 if (tTd(38, 3)) 4984 printf("regex_map_init: compile '%s' 0x%x\n", p, pflags); 4985 4986 if ((regerr = regcomp(&(map_p->pattern_buf), p, pflags)) != 0) 4987 { 4988 /* Errorhandling */ 4989 char errbuf[ERRBUF_SIZE]; 4990 4991 regerror(regerr, &(map_p->pattern_buf), errbuf, ERRBUF_SIZE); 4992 syserr("pattern-compile-error: %s\n", errbuf); 4993 free(map_p); 4994 return FALSE; 4995 } 4996 4997 if (map->map_app != NULL) 4998 map->map_app = newstr(map->map_app); 4999 if (map_p->delim != NULL) 5000 map_p->delim = newstr(map_p->delim); 5001 else 5002 map_p->delim = defdstr; 5003 5004 if (!bitset(REG_NOSUB, pflags)) 5005 { 5006 /* substring matching */ 5007 int substrings; 5008 int *fields = (int *)xalloc(sizeof(int) * (MAX_MATCH + 1)); 5009 5010 substrings = map_p->pattern_buf.re_nsub + 1; 5011 5012 if (tTd(38, 3)) 5013 printf("regex_map_init: nr of substrings %d\n", substrings); 5014 5015 if (substrings >= MAX_MATCH) 5016 { 5017 syserr("too many substrings, %d max\n", MAX_MATCH); 5018 free(map_p); 5019 return FALSE; 5020 } 5021 if (sub_param != NULL && sub_param[0] != '\0') 5022 { 5023 /* optional parameter -sfields */ 5024 if (parse_fields(sub_param, fields, 5025 MAX_MATCH + 1, substrings) == -1) 5026 return FALSE; 5027 } 5028 else 5029 { 5030 /* set default fields */ 5031 int i; 5032 5033 for (i = 0; i < substrings; i++) 5034 fields[i] = i; 5035 fields[i] = END_OF_FIELDS; 5036 } 5037 map_p->regex_subfields = fields; 5038 if (tTd(38, 3)) 5039 { 5040 int *ip; 5041 5042 printf("regex_map_init: subfields"); 5043 for (ip = fields; *ip != END_OF_FIELDS; ip++) 5044 printf(" %d", *ip); 5045 printf("\n"); 5046 } 5047 } 5048 map->map_db1 = (ARBPTR_T)map_p; /* dirty hack */ 5049 5050 return TRUE; 5051 } 5052 5053 static char * 5054 regex_map_rewrite(map, s, slen, av) 5055 MAP *map; 5056 const char *s; 5057 size_t slen; 5058 char **av; 5059 { 5060 if (bitset(MF_MATCHONLY, map->map_mflags)) 5061 return map_rewrite(map, av[0], strlen(av[0]), NULL); 5062 else 5063 return map_rewrite(map, s, slen, NULL); 5064 } 5065 5066 char * 5067 regex_map_lookup(map, name, av, statp) 5068 MAP *map; 5069 char *name; 5070 char **av; 5071 int *statp; 5072 { 5073 int reg_res; 5074 struct regex_map *map_p; 5075 regmatch_t pmatch[MAX_MATCH]; 5076 5077 if (tTd(38, 20)) 5078 { 5079 char **cpp; 5080 5081 printf("regex_map_lookup: key '%s'\n", name); 5082 for (cpp = av; cpp && *cpp; cpp++) 5083 printf("regex_map_lookup: arg '%s'\n", *cpp); 5084 } 5085 5086 map_p = (struct regex_map *)(map->map_db1); 5087 reg_res = regexec(&(map_p->pattern_buf), name, MAX_MATCH, pmatch, 0); 5088 5089 if (bitset(MF_REGEX_NOT, map->map_mflags)) 5090 { 5091 /* option -n */ 5092 if (reg_res == REG_NOMATCH) 5093 return regex_map_rewrite(map, "", (size_t)0, av); 5094 else 5095 return NULL; 5096 } 5097 if (reg_res == REG_NOMATCH) 5098 return NULL; 5099 5100 if (map_p->regex_subfields != NULL) 5101 { 5102 /* option -s */ 5103 static char retbuf[MAXNAME]; 5104 int fields[MAX_MATCH + 1]; 5105 bool first = TRUE; 5106 int anglecnt = 0, cmntcnt = 0, spacecnt = 0; 5107 bool quotemode = FALSE, bslashmode = FALSE; 5108 register char *dp, *sp; 5109 char *endp, *ldp; 5110 int *ip; 5111 5112 dp = retbuf; 5113 ldp = retbuf + sizeof(retbuf) - 1; 5114 5115 if (av[1] != NULL) 5116 { 5117 if (parse_fields(av[1], fields, MAX_MATCH + 1, 5118 (int) map_p->pattern_buf.re_nsub + 1) == -1) 5119 { 5120 *statp = EX_CONFIG; 5121 return NULL; 5122 } 5123 ip = fields; 5124 } 5125 else 5126 ip = map_p->regex_subfields; 5127 5128 for ( ; *ip != END_OF_FIELDS; ip++) 5129 { 5130 if (!first) 5131 { 5132 for (sp = map_p->delim; *sp; sp++) 5133 { 5134 if (dp < ldp) 5135 *dp++ = *sp; 5136 } 5137 } 5138 else 5139 first = FALSE; 5140 5141 5142 if (pmatch[*ip].rm_so < 0 || pmatch[*ip].rm_eo < 0) 5143 continue; 5144 5145 sp = name + pmatch[*ip].rm_so; 5146 endp = name + pmatch[*ip].rm_eo; 5147 for (; endp > sp; sp++) 5148 { 5149 if (dp < ldp) 5150 { 5151 if(bslashmode) 5152 { 5153 *dp++ = *sp; 5154 bslashmode = FALSE; 5155 } 5156 else if(quotemode && *sp != '"' && 5157 *sp != '\\') 5158 { 5159 *dp++ = *sp; 5160 } 5161 else switch(*dp++ = *sp) 5162 { 5163 case '\\': 5164 bslashmode = TRUE; 5165 break; 5166 5167 case '(': 5168 cmntcnt++; 5169 break; 5170 5171 case ')': 5172 cmntcnt--; 5173 break; 5174 5175 case '<': 5176 anglecnt++; 5177 break; 5178 5179 case '>': 5180 anglecnt--; 5181 break; 5182 5183 case ' ': 5184 spacecnt++; 5185 break; 5186 5187 case '"': 5188 quotemode = !quotemode; 5189 break; 5190 } 5191 } 5192 } 5193 } 5194 if (anglecnt != 0 || cmntcnt != 0 || quotemode || 5195 bslashmode || spacecnt != 0) 5196 { 5197 sm_syslog(LOG_WARNING, NOQID, 5198 "Warning: regex may cause prescan() failure map=%s lookup=%s", 5199 map->map_mname, name); 5200 return NULL; 5201 } 5202 5203 *dp = '\0'; 5204 5205 return regex_map_rewrite(map, retbuf, strlen(retbuf), av); 5206 } 5207 return regex_map_rewrite(map, "", (size_t)0, av); 5208 } 5209 #endif /* MAP_REGEX */ 5210