1 /* 2 * Copyright (c) 1983, 1995 Eric P. Allman 3 * Copyright (c) 1988, 1993 4 * The Regents of the University of California. All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 # include "sendmail.h" 10 11 #ifndef lint 12 static char sccsid[] = "@(#)alias.c 8.49 (Berkeley) 05/30/95"; 13 #endif /* not lint */ 14 15 16 MAP *AliasFileMap = NULL; /* the actual aliases.files map */ 17 int NAliasFileMaps; /* the number of entries in AliasFileMap */ 18 /* 19 ** ALIAS -- Compute aliases. 20 ** 21 ** Scans the alias file for an alias for the given address. 22 ** If found, it arranges to deliver to the alias list instead. 23 ** Uses libdbm database if -DDBM. 24 ** 25 ** Parameters: 26 ** a -- address to alias. 27 ** sendq -- a pointer to the head of the send queue 28 ** to put the aliases in. 29 ** aliaslevel -- the current alias nesting depth. 30 ** e -- the current envelope. 31 ** 32 ** Returns: 33 ** none 34 ** 35 ** Side Effects: 36 ** Aliases found are expanded. 37 ** 38 ** Deficiencies: 39 ** It should complain about names that are aliased to 40 ** nothing. 41 */ 42 43 void 44 alias(a, sendq, aliaslevel, e) 45 register ADDRESS *a; 46 ADDRESS **sendq; 47 int aliaslevel; 48 register ENVELOPE *e; 49 { 50 register char *p; 51 int naliases; 52 char *owner; 53 auto int stat = EX_OK; 54 char obuf[MAXNAME + 6]; 55 extern char *aliaslookup(); 56 57 if (tTd(27, 1)) 58 printf("alias(%s)\n", a->q_user); 59 60 /* don't realias already aliased names */ 61 if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags)) 62 return; 63 64 if (NoAlias) 65 return; 66 67 e->e_to = a->q_paddr; 68 69 /* 70 ** Look up this name. 71 ** 72 ** If the map was unavailable, we will queue this message 73 ** until the map becomes available; otherwise, we could 74 ** bounce messages inappropriately. 75 */ 76 77 p = aliaslookup(a->q_user, &stat, e); 78 if (stat == EX_TEMPFAIL || stat == EX_UNAVAILABLE) 79 { 80 a->q_flags |= QQUEUEUP; 81 if (e->e_message == NULL) 82 e->e_message = "alias database unavailable"; 83 return; 84 } 85 if (p == NULL) 86 return; 87 88 /* 89 ** Match on Alias. 90 ** Deliver to the target list. 91 */ 92 93 if (tTd(27, 1)) 94 printf("%s (%s, %s) aliased to %s\n", 95 a->q_paddr, a->q_host, a->q_user, p); 96 if (bitset(EF_VRFYONLY, e->e_flags)) 97 { 98 a->q_flags |= QVERIFIED; 99 e->e_nrcpts++; 100 return; 101 } 102 message("aliased to %s", p); 103 #ifdef LOG 104 if (LogLevel > 9) 105 syslog(LOG_INFO, "%s: alias %s => %s", 106 e->e_id == NULL ? "NOQUEUE" : e->e_id, 107 a->q_paddr, p); 108 #endif 109 a->q_flags &= ~QSELFREF; 110 if (tTd(27, 5)) 111 { 112 printf("alias: QDONTSEND "); 113 printaddr(a, FALSE); 114 } 115 a->q_flags |= QDONTSEND; 116 naliases = sendtolist(p, a, sendq, aliaslevel + 1, e); 117 if (bitset(QSELFREF, a->q_flags)) 118 a->q_flags &= ~QDONTSEND; 119 120 /* 121 ** Look for owner of alias 122 */ 123 124 (void) strcpy(obuf, "owner-"); 125 if (strncmp(a->q_user, "owner-", 6) == 0) 126 (void) strcat(obuf, "owner"); 127 else 128 (void) strcat(obuf, a->q_user); 129 if (!bitnset(M_USR_UPPER, a->q_mailer->m_flags)) 130 makelower(obuf); 131 owner = aliaslookup(obuf, &stat, e); 132 if (owner == NULL) 133 return; 134 135 /* reflect owner into envelope sender */ 136 if (strpbrk(owner, ",:/|\"") != NULL) 137 owner = obuf; 138 a->q_owner = newstr(owner); 139 140 /* announce delivery to this alias; NORECEIPT bit set later */ 141 if (e->e_xfp != NULL) 142 fprintf(e->e_xfp, "Message delivered to mailing list %s\n", 143 a->q_paddr); 144 e->e_flags |= EF_SENDRECEIPT; 145 a->q_flags |= QDELIVERED|QEXPANDED; 146 } 147 /* 148 ** ALIASLOOKUP -- look up a name in the alias file. 149 ** 150 ** Parameters: 151 ** name -- the name to look up. 152 ** pstat -- a pointer to a place to put the status. 153 ** e -- the current envelope. 154 ** 155 ** Returns: 156 ** the value of name. 157 ** NULL if unknown. 158 ** 159 ** Side Effects: 160 ** none. 161 ** 162 ** Warnings: 163 ** The return value will be trashed across calls. 164 */ 165 166 char * 167 aliaslookup(name, pstat, e) 168 char *name; 169 int *pstat; 170 ENVELOPE *e; 171 { 172 static MAP *map = NULL; 173 174 if (map == NULL) 175 { 176 STAB *s = stab("aliases", ST_MAP, ST_FIND); 177 178 if (s == NULL) 179 return NULL; 180 map = &s->s_map; 181 } 182 if (!bitset(MF_OPEN, map->map_mflags)) 183 return NULL; 184 return (*map->map_class->map_lookup)(map, name, NULL, pstat); 185 } 186 /* 187 ** SETALIAS -- set up an alias map 188 ** 189 ** Called when reading configuration file. 190 ** 191 ** Parameters: 192 ** spec -- the alias specification 193 ** 194 ** Returns: 195 ** none. 196 */ 197 198 void 199 setalias(spec) 200 char *spec; 201 { 202 register char *p; 203 register MAP *map; 204 char *class; 205 STAB *s; 206 207 if (tTd(27, 8)) 208 printf("setalias(%s)\n", spec); 209 210 for (p = spec; p != NULL; ) 211 { 212 char buf[50]; 213 214 while (isspace(*p)) 215 p++; 216 if (*p == '\0') 217 break; 218 spec = p; 219 220 if (NAliasFileMaps >= MAXMAPSTACK) 221 { 222 syserr("Too many alias databases defined, %d max", 223 MAXMAPSTACK); 224 return; 225 } 226 if (AliasFileMap == NULL) 227 { 228 strcpy(buf, "aliases.files sequence"); 229 AliasFileMap = makemapentry(buf); 230 if (AliasFileMap == NULL) 231 { 232 syserr("setalias: cannot create aliases.files map"); 233 return; 234 } 235 } 236 (void) sprintf(buf, "Alias%d", NAliasFileMaps); 237 s = stab(buf, ST_MAP, ST_ENTER); 238 map = &s->s_map; 239 bzero(map, sizeof *map); 240 map->map_mname = s->s_name; 241 242 p = strpbrk(p, " ,/:"); 243 if (p != NULL && *p == ':') 244 { 245 /* map name */ 246 *p++ = '\0'; 247 class = spec; 248 spec = p; 249 } 250 else 251 { 252 class = "implicit"; 253 map->map_mflags = MF_OPTIONAL|MF_INCLNULL; 254 } 255 256 /* find end of spec */ 257 if (p != NULL) 258 p = strchr(p, ','); 259 if (p != NULL) 260 *p++ = '\0'; 261 262 if (tTd(27, 20)) 263 printf(" map %s:%s %s\n", class, s->s_name, spec); 264 265 /* look up class */ 266 s = stab(class, ST_MAPCLASS, ST_FIND); 267 if (s == NULL) 268 { 269 if (tTd(27, 1)) 270 printf("Unknown alias class %s\n", class); 271 } 272 else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags)) 273 { 274 syserr("setalias: map class %s can't handle aliases", 275 class); 276 } 277 else 278 { 279 map->map_class = &s->s_mapclass; 280 if (map->map_class->map_parse(map, spec)) 281 { 282 map->map_mflags |= MF_VALID|MF_ALIAS; 283 AliasFileMap->map_stack[NAliasFileMaps++] = map; 284 } 285 } 286 } 287 } 288 /* 289 ** ALIASWAIT -- wait for distinguished @:@ token to appear. 290 ** 291 ** This can decide to reopen or rebuild the alias file 292 ** 293 ** Parameters: 294 ** map -- a pointer to the map descriptor for this alias file. 295 ** ext -- the filename extension (e.g., ".db") for the 296 ** database file. 297 ** isopen -- if set, the database is already open, and we 298 ** should check for validity; otherwise, we are 299 ** just checking to see if it should be created. 300 ** 301 ** Returns: 302 ** TRUE -- if the database is open when we return. 303 ** FALSE -- if the database is closed when we return. 304 */ 305 306 bool 307 aliaswait(map, ext, isopen) 308 MAP *map; 309 char *ext; 310 int isopen; 311 { 312 bool attimeout = FALSE; 313 time_t mtime; 314 struct stat stb; 315 char buf[MAXNAME + 1]; 316 317 if (tTd(27, 3)) 318 printf("aliaswait(%s:%s)\n", 319 map->map_class->map_cname, map->map_file); 320 if (bitset(MF_ALIASWAIT, map->map_mflags)) 321 return isopen; 322 map->map_mflags |= MF_ALIASWAIT; 323 324 if (SafeAlias > 0) 325 { 326 auto int st; 327 time_t toolong = curtime() + SafeAlias; 328 unsigned int sleeptime = 2; 329 330 while (isopen && 331 map->map_class->map_lookup(map, "@", NULL, &st) == NULL) 332 { 333 if (curtime() > toolong) 334 { 335 /* we timed out */ 336 attimeout = TRUE; 337 break; 338 } 339 340 /* 341 ** Close and re-open the alias database in case 342 ** the one is mv'ed instead of cp'ed in. 343 */ 344 345 if (tTd(27, 2)) 346 printf("aliaswait: sleeping for %d seconds\n", 347 sleeptime); 348 349 map->map_class->map_close(map); 350 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 351 sleep(sleeptime); 352 sleeptime *= 2; 353 if (sleeptime > 60) 354 sleeptime = 60; 355 isopen = map->map_class->map_open(map, O_RDONLY); 356 } 357 } 358 359 /* see if we need to go into auto-rebuild mode */ 360 if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) 361 { 362 if (tTd(27, 3)) 363 printf("aliaswait: not rebuildable\n"); 364 map->map_mflags &= ~MF_ALIASWAIT; 365 return isopen; 366 } 367 if (stat(map->map_file, &stb) < 0) 368 { 369 if (tTd(27, 3)) 370 printf("aliaswait: no source file\n"); 371 map->map_mflags &= ~MF_ALIASWAIT; 372 return isopen; 373 } 374 mtime = stb.st_mtime; 375 (void) strcpy(buf, map->map_file); 376 if (ext != NULL) 377 (void) strcat(buf, ext); 378 if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout) 379 { 380 /* database is out of date */ 381 if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 382 { 383 bool oldSuprErrs; 384 385 message("auto-rebuilding alias database %s", buf); 386 oldSuprErrs = SuprErrs; 387 SuprErrs = TRUE; 388 if (isopen) 389 { 390 map->map_class->map_close(map); 391 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 392 } 393 rebuildaliases(map, TRUE); 394 isopen = map->map_class->map_open(map, O_RDONLY); 395 SuprErrs = oldSuprErrs; 396 } 397 else 398 { 399 #ifdef LOG 400 if (LogLevel > 3) 401 syslog(LOG_INFO, "alias database %s out of date", 402 buf); 403 #endif /* LOG */ 404 message("Warning: alias database %s out of date", buf); 405 } 406 } 407 map->map_mflags &= ~MF_ALIASWAIT; 408 return isopen; 409 } 410 /* 411 ** REBUILDALIASES -- rebuild the alias database. 412 ** 413 ** Parameters: 414 ** map -- the database to rebuild. 415 ** automatic -- set if this was automatically generated. 416 ** 417 ** Returns: 418 ** none. 419 ** 420 ** Side Effects: 421 ** Reads the text version of the database, builds the 422 ** DBM or DB version. 423 */ 424 425 void 426 rebuildaliases(map, automatic) 427 register MAP *map; 428 bool automatic; 429 { 430 FILE *af; 431 bool nolock = FALSE; 432 sigfunc_t oldsigint, oldsigquit; 433 #ifdef SIGTSTP 434 sigfunc_t oldsigtstp; 435 #endif 436 437 if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags)) 438 return; 439 440 /* try to lock the source file */ 441 if ((af = fopen(map->map_file, "r+")) == NULL) 442 { 443 if ((errno != EACCES && errno != EROFS) || automatic || 444 (af = fopen(map->map_file, "r")) == NULL) 445 { 446 int saveerr = errno; 447 448 if (tTd(27, 1)) 449 printf("Can't open %s: %s\n", 450 map->map_file, errstring(saveerr)); 451 if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags)) 452 message("newaliases: cannot open %s: %s", 453 map->map_file, errstring(saveerr)); 454 errno = 0; 455 return; 456 } 457 nolock = TRUE; 458 message("warning: cannot lock %s: %s", 459 map->map_file, errstring(errno)); 460 } 461 462 /* see if someone else is rebuilding the alias file */ 463 if (!nolock && 464 !lockfile(fileno(af), map->map_file, NULL, LOCK_EX|LOCK_NB)) 465 { 466 /* yes, they are -- wait until done */ 467 message("Alias file %s is already being rebuilt", 468 map->map_file); 469 if (OpMode != MD_INITALIAS) 470 { 471 /* wait for other rebuild to complete */ 472 (void) lockfile(fileno(af), map->map_file, NULL, 473 LOCK_EX); 474 } 475 (void) xfclose(af, "rebuildaliases1", map->map_file); 476 errno = 0; 477 return; 478 } 479 480 /* avoid denial-of-service attacks */ 481 resetlimits(); 482 oldsigint = setsignal(SIGINT, SIG_IGN); 483 oldsigquit = setsignal(SIGQUIT, SIG_IGN); 484 #ifdef SIGTSTP 485 oldsigtstp = setsignal(SIGTSTP, SIG_IGN); 486 #endif 487 488 if (map->map_class->map_open(map, O_RDWR)) 489 { 490 #ifdef LOG 491 if (LogLevel > 7) 492 { 493 syslog(LOG_NOTICE, "alias database %s %srebuilt by %s", 494 map->map_file, automatic ? "auto" : "", 495 username()); 496 } 497 #endif /* LOG */ 498 map->map_mflags |= MF_OPEN|MF_WRITABLE; 499 readaliases(map, af, !automatic, TRUE); 500 } 501 else 502 { 503 if (tTd(27, 1)) 504 printf("Can't create database for %s: %s\n", 505 map->map_file, errstring(errno)); 506 if (!automatic) 507 syserr("Cannot create database for alias file %s", 508 map->map_file); 509 } 510 511 /* close the file, thus releasing locks */ 512 xfclose(af, "rebuildaliases2", map->map_file); 513 514 /* add distinguished entries and close the database */ 515 if (bitset(MF_OPEN, map->map_mflags)) 516 { 517 map->map_class->map_close(map); 518 map->map_mflags &= ~(MF_OPEN|MF_WRITABLE); 519 } 520 521 /* restore the old signals */ 522 (void) setsignal(SIGINT, oldsigint); 523 (void) setsignal(SIGQUIT, oldsigquit); 524 #ifdef SIGTSTP 525 (void) setsignal(SIGTSTP, oldsigtstp); 526 #endif 527 } 528 /* 529 ** READALIASES -- read and process the alias file. 530 ** 531 ** This routine implements the part of initaliases that occurs 532 ** when we are not going to use the DBM stuff. 533 ** 534 ** Parameters: 535 ** map -- the alias database descriptor. 536 ** af -- file to read the aliases from. 537 ** announcestats -- anounce statistics regarding number of 538 ** aliases, longest alias, etc. 539 ** logstats -- lot the same info. 540 ** 541 ** Returns: 542 ** none. 543 ** 544 ** Side Effects: 545 ** Reads aliasfile into the symbol table. 546 ** Optionally, builds the .dir & .pag files. 547 */ 548 549 void 550 readaliases(map, af, announcestats, logstats) 551 register MAP *map; 552 FILE *af; 553 bool announcestats; 554 bool logstats; 555 { 556 register char *p; 557 char *rhs; 558 bool skipping; 559 long naliases, bytes, longest; 560 ADDRESS al, bl; 561 char line[BUFSIZ]; 562 563 /* 564 ** Read and interpret lines 565 */ 566 567 FileName = map->map_file; 568 LineNumber = 0; 569 naliases = bytes = longest = 0; 570 skipping = FALSE; 571 while (fgets(line, sizeof (line), af) != NULL) 572 { 573 int lhssize, rhssize; 574 575 LineNumber++; 576 p = strchr(line, '\n'); 577 if (p != NULL) 578 *p = '\0'; 579 switch (line[0]) 580 { 581 case '#': 582 case '\0': 583 skipping = FALSE; 584 continue; 585 586 case ' ': 587 case '\t': 588 if (!skipping) 589 syserr("554 Non-continuation line starts with space"); 590 skipping = TRUE; 591 continue; 592 } 593 skipping = FALSE; 594 595 /* 596 ** Process the LHS 597 ** Find the colon separator, and parse the address. 598 ** It should resolve to a local name -- this will 599 ** be checked later (we want to optionally do 600 ** parsing of the RHS first to maximize error 601 ** detection). 602 */ 603 604 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 605 continue; 606 if (*p++ != ':') 607 { 608 syserr("554 missing colon"); 609 continue; 610 } 611 if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv) == NULL) 612 { 613 syserr("554 %.40s... illegal alias name", line); 614 continue; 615 } 616 617 /* 618 ** Process the RHS. 619 ** 'al' is the internal form of the LHS address. 620 ** 'p' points to the text of the RHS. 621 */ 622 623 while (isascii(*p) && isspace(*p)) 624 p++; 625 rhs = p; 626 for (;;) 627 { 628 register char c; 629 register char *nlp; 630 631 nlp = &p[strlen(p)]; 632 if (nlp[-1] == '\n') 633 *--nlp = '\0'; 634 635 if (CheckAliases) 636 { 637 /* do parsing & compression of addresses */ 638 while (*p != '\0') 639 { 640 auto char *delimptr; 641 642 while ((isascii(*p) && isspace(*p)) || 643 *p == ',') 644 p++; 645 if (*p == '\0') 646 break; 647 if (parseaddr(p, &bl, RF_COPYNONE, ',', 648 &delimptr, CurEnv) == NULL) 649 usrerr("553 %s... bad address", p); 650 p = delimptr; 651 } 652 } 653 else 654 { 655 p = nlp; 656 } 657 658 /* see if there should be a continuation line */ 659 c = getc(af); 660 if (!feof(af)) 661 (void) ungetc(c, af); 662 if (c != ' ' && c != '\t') 663 break; 664 665 /* read continuation line */ 666 if (fgets(p, sizeof line - (p - line), af) == NULL) 667 break; 668 LineNumber++; 669 670 /* check for line overflow */ 671 if (strchr(p, '\n') == NULL) 672 { 673 usrerr("554 alias too long"); 674 break; 675 } 676 } 677 if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags)) 678 { 679 syserr("554 %s... cannot alias non-local names", 680 al.q_paddr); 681 continue; 682 } 683 684 /* 685 ** Insert alias into symbol table or DBM file 686 */ 687 688 if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags)) 689 makelower(al.q_user); 690 691 lhssize = strlen(al.q_user); 692 rhssize = strlen(rhs); 693 map->map_class->map_store(map, al.q_user, rhs); 694 695 if (al.q_paddr != NULL) 696 free(al.q_paddr); 697 if (al.q_host != NULL) 698 free(al.q_host); 699 if (al.q_user != NULL) 700 free(al.q_user); 701 702 /* statistics */ 703 naliases++; 704 bytes += lhssize + rhssize; 705 if (rhssize > longest) 706 longest = rhssize; 707 } 708 709 CurEnv->e_to = NULL; 710 FileName = NULL; 711 if (Verbose || announcestats) 712 message("%s: %d aliases, longest %d bytes, %d bytes total", 713 map->map_file, naliases, longest, bytes); 714 # ifdef LOG 715 if (LogLevel > 7 && logstats) 716 syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total", 717 map->map_file, naliases, longest, bytes); 718 # endif /* LOG */ 719 } 720 /* 721 ** FORWARD -- Try to forward mail 722 ** 723 ** This is similar but not identical to aliasing. 724 ** 725 ** Parameters: 726 ** user -- the name of the user who's mail we would like 727 ** to forward to. It must have been verified -- 728 ** i.e., the q_home field must have been filled 729 ** in. 730 ** sendq -- a pointer to the head of the send queue to 731 ** put this user's aliases in. 732 ** aliaslevel -- the current alias nesting depth. 733 ** e -- the current envelope. 734 ** 735 ** Returns: 736 ** none. 737 ** 738 ** Side Effects: 739 ** New names are added to send queues. 740 */ 741 742 void 743 forward(user, sendq, aliaslevel, e) 744 ADDRESS *user; 745 ADDRESS **sendq; 746 int aliaslevel; 747 register ENVELOPE *e; 748 { 749 char *pp; 750 char *ep; 751 752 if (tTd(27, 1)) 753 printf("forward(%s)\n", user->q_paddr); 754 755 if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) || 756 bitset(QBADADDR, user->q_flags)) 757 return; 758 if (user->q_home == NULL) 759 { 760 syserr("554 forward: no home"); 761 user->q_home = "/nosuchdirectory"; 762 } 763 764 /* good address -- look for .forward file in home */ 765 define('z', user->q_home, e); 766 define('u', user->q_user, e); 767 define('h', user->q_host, e); 768 if (ForwardPath == NULL) 769 ForwardPath = newstr("\201z/.forward"); 770 771 for (pp = ForwardPath; pp != NULL; pp = ep) 772 { 773 int err; 774 char buf[MAXPATHLEN+1]; 775 extern int include(); 776 777 ep = strchr(pp, ':'); 778 if (ep != NULL) 779 *ep = '\0'; 780 expand(pp, buf, sizeof buf, e); 781 if (ep != NULL) 782 *ep++ = ':'; 783 if (tTd(27, 3)) 784 printf("forward: trying %s\n", buf); 785 786 err = include(buf, TRUE, user, sendq, aliaslevel, e); 787 if (err == 0) 788 break; 789 else if (transienterror(err)) 790 { 791 /* we have to suspend this message */ 792 if (tTd(27, 2)) 793 printf("forward: transient error on %s\n", buf); 794 #ifdef LOG 795 if (LogLevel > 2) 796 syslog(LOG_ERR, "%s: forward %s: transient error: %s", 797 e->e_id == NULL ? "NOQUEUE" : e->e_id, 798 buf, errstring(err)); 799 #endif 800 message("%s: %s: message queued", buf, errstring(err)); 801 user->q_flags |= QQUEUEUP; 802 return; 803 } 804 } 805 } 806