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