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.50 (Berkeley) 05/27/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 == '\0') 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 syslog(LOG_NOTICE, "alias database %s %srebuilt by %s", 420 map->map_file, automatic ? "auto" : "", username()); 421 } 422 #endif /* LOG */ 423 424 /* try to lock the source file */ 425 if ((af = fopen(map->map_file, "r+")) == NULL) 426 { 427 if (tTd(27, 1)) 428 printf("Can't open %s: %s\n", 429 map->map_file, errstring(errno)); 430 errno = 0; 431 return; 432 } 433 434 /* see if someone else is rebuilding the alias file */ 435 if (!lockfile(fileno(af), map->map_file, LOCK_EX|LOCK_NB)) 436 { 437 /* yes, they are -- wait until done */ 438 message("Alias file %s is already being rebuilt", 439 map->map_file); 440 if (OpMode != MD_INITALIAS) 441 { 442 /* wait for other rebuild to complete */ 443 (void) lockfile(fileno(af), map->map_file, 444 LOCK_EX); 445 } 446 (void) fclose(af); 447 errno = 0; 448 return; 449 } 450 451 oldsigint = signal(SIGINT, SIG_IGN); 452 453 if (map->map_class->map_open(map, O_RDWR)) 454 { 455 map->map_mflags |= MF_OPEN|MF_WRITABLE; 456 readaliases(map, af, automatic); 457 } 458 else 459 { 460 if (tTd(27, 1)) 461 printf("Can't create database for %s: %s\n", 462 map->map_file, errstring(errno)); 463 if (!automatic) 464 syserr("Cannot create database for alias file %s", 465 map->map_file); 466 } 467 468 /* close the file, thus releasing locks */ 469 fclose(af); 470 471 /* add distinguished entries and close the database */ 472 if (bitset(MF_OPEN, map->map_mflags)) 473 map->map_class->map_close(map); 474 475 /* restore the old signal */ 476 (void) signal(SIGINT, oldsigint); 477 } 478 /* 479 ** READALIASES -- read and process the alias file. 480 ** 481 ** This routine implements the part of initaliases that occurs 482 ** when we are not going to use the DBM stuff. 483 ** 484 ** Parameters: 485 ** map -- the alias database descriptor. 486 ** af -- file to read the aliases from. 487 ** automatic -- set if this was an automatic rebuild. 488 ** 489 ** Returns: 490 ** none. 491 ** 492 ** Side Effects: 493 ** Reads aliasfile into the symbol table. 494 ** Optionally, builds the .dir & .pag files. 495 */ 496 497 readaliases(map, af, automatic) 498 register MAP *map; 499 FILE *af; 500 int automatic; 501 { 502 register char *p; 503 char *rhs; 504 bool skipping; 505 long naliases, bytes, longest; 506 ADDRESS al, bl; 507 register STAB *s; 508 char line[BUFSIZ]; 509 510 /* 511 ** Read and interpret lines 512 */ 513 514 FileName = map->map_file; 515 LineNumber = 0; 516 naliases = bytes = longest = 0; 517 skipping = FALSE; 518 while (fgets(line, sizeof (line), af) != NULL) 519 { 520 int lhssize, rhssize; 521 522 LineNumber++; 523 p = strchr(line, '\n'); 524 if (p != NULL) 525 *p = '\0'; 526 switch (line[0]) 527 { 528 case '#': 529 case '\0': 530 skipping = FALSE; 531 continue; 532 533 case ' ': 534 case '\t': 535 if (!skipping) 536 syserr("554 Non-continuation line starts with space"); 537 skipping = TRUE; 538 continue; 539 } 540 skipping = FALSE; 541 542 /* 543 ** Process the LHS 544 ** Find the colon separator, and parse the address. 545 ** It should resolve to a local name -- this will 546 ** be checked later (we want to optionally do 547 ** parsing of the RHS first to maximize error 548 ** detection). 549 */ 550 551 for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 552 continue; 553 if (*p++ != ':') 554 { 555 syserr("554 missing colon"); 556 continue; 557 } 558 if (parseaddr(line, &al, 1, ':', NULL, CurEnv) == NULL) 559 { 560 syserr("554 illegal alias name"); 561 continue; 562 } 563 564 /* 565 ** Process the RHS. 566 ** 'al' is the internal form of the LHS address. 567 ** 'p' points to the text of the RHS. 568 */ 569 570 while (isascii(*p) && isspace(*p)) 571 p++; 572 rhs = p; 573 for (;;) 574 { 575 register char c; 576 register char *nlp; 577 578 nlp = &p[strlen(p)]; 579 if (nlp[-1] == '\n') 580 *--nlp = '\0'; 581 582 if (CheckAliases) 583 { 584 /* do parsing & compression of addresses */ 585 while (*p != '\0') 586 { 587 auto char *delimptr; 588 589 while ((isascii(*p) && isspace(*p)) || 590 *p == ',') 591 p++; 592 if (*p == '\0') 593 break; 594 if (parseaddr(p, &bl, -1, ',', &delimptr, CurEnv) == NULL) 595 usrerr("553 %s... bad address", p); 596 p = delimptr; 597 } 598 } 599 else 600 { 601 p = nlp; 602 } 603 604 /* see if there should be a continuation line */ 605 c = fgetc(af); 606 if (!feof(af)) 607 (void) ungetc(c, af); 608 if (c != ' ' && c != '\t') 609 break; 610 611 /* read continuation line */ 612 if (fgets(p, sizeof line - (p - line), af) == NULL) 613 break; 614 LineNumber++; 615 616 /* check for line overflow */ 617 if (strchr(p, '\n') == NULL) 618 { 619 usrerr("554 alias too long"); 620 break; 621 } 622 } 623 if (al.q_mailer != LocalMailer) 624 { 625 syserr("554 cannot alias non-local names"); 626 continue; 627 } 628 629 /* 630 ** Insert alias into symbol table or DBM file 631 */ 632 633 if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags)) 634 makelower(al.q_user); 635 636 lhssize = strlen(al.q_user); 637 rhssize = strlen(rhs); 638 map->map_class->map_store(map, al.q_user, rhs); 639 640 if (al.q_paddr != NULL) 641 free(al.q_paddr); 642 if (al.q_host != NULL) 643 free(al.q_host); 644 if (al.q_user != NULL) 645 free(al.q_user); 646 647 /* statistics */ 648 naliases++; 649 bytes += lhssize + rhssize; 650 if (rhssize > longest) 651 longest = rhssize; 652 } 653 654 CurEnv->e_to = NULL; 655 FileName = NULL; 656 if (Verbose || !automatic) 657 message("%s: %d aliases, longest %d bytes, %d bytes total", 658 map->map_file, naliases, longest, bytes); 659 # ifdef LOG 660 if (LogLevel > 7) 661 syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total", 662 map->map_file, naliases, longest, bytes); 663 # endif /* LOG */ 664 } 665 /* 666 ** FORWARD -- Try to forward mail 667 ** 668 ** This is similar but not identical to aliasing. 669 ** 670 ** Parameters: 671 ** user -- the name of the user who's mail we would like 672 ** to forward to. It must have been verified -- 673 ** i.e., the q_home field must have been filled 674 ** in. 675 ** sendq -- a pointer to the head of the send queue to 676 ** put this user's aliases in. 677 ** 678 ** Returns: 679 ** none. 680 ** 681 ** Side Effects: 682 ** New names are added to send queues. 683 */ 684 685 forward(user, sendq, e) 686 ADDRESS *user; 687 ADDRESS **sendq; 688 register ENVELOPE *e; 689 { 690 char *pp; 691 char *ep; 692 693 if (tTd(27, 1)) 694 printf("forward(%s)\n", user->q_paddr); 695 696 if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 697 return; 698 if (user->q_home == NULL) 699 { 700 syserr("554 forward: no home"); 701 user->q_home = "/nosuchdirectory"; 702 } 703 704 /* good address -- look for .forward file in home */ 705 define('z', user->q_home, e); 706 define('u', user->q_user, e); 707 define('h', user->q_host, e); 708 if (ForwardPath == NULL) 709 ForwardPath = newstr("\201z/.forward"); 710 711 for (pp = ForwardPath; pp != NULL; pp = ep) 712 { 713 int err; 714 char buf[MAXPATHLEN+1]; 715 716 ep = strchr(pp, ':'); 717 if (ep != NULL) 718 *ep = '\0'; 719 expand(pp, buf, &buf[sizeof buf - 1], e); 720 if (ep != NULL) 721 *ep++ = ':'; 722 if (tTd(27, 3)) 723 printf("forward: trying %s\n", buf); 724 err = include(buf, TRUE, user, sendq, e); 725 if (err == 0) 726 break; 727 if (transienterror(err)) 728 { 729 /* we have to suspend this message */ 730 if (tTd(27, 2)) 731 printf("forward: transient error on %s\n", buf); 732 #ifdef LOG 733 if (LogLevel > 2) 734 syslog(LOG_ERR, "%s: forward %s: transient error: %s", 735 e->e_id, buf, errstring(err)); 736 #endif 737 message("%s: %s: message queued", buf, errstring(err)); 738 user->q_flags |= QQUEUEUP|QDONTSEND; 739 return; 740 } 741 } 742 } 743