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