122694Sdist /* 235073Sbostic * Copyright (c) 1983 Eric P. Allman 333728Sbostic * Copyright (c) 1988 Regents of the University of California. 433728Sbostic * All rights reserved. 533728Sbostic * 6*42824Sbostic * %sccs.include.redist.c% 733728Sbostic */ 822694Sdist 933728Sbostic #ifndef lint 1033728Sbostic #ifdef DBM 11*42824Sbostic static char sccsid[] = "@(#)alias.c 5.21 (Berkeley) 06/01/90 (with DBM)"; 1233728Sbostic #else 13*42824Sbostic static char sccsid[] = "@(#)alias.c 5.21 (Berkeley) 06/01/90 (without DBM)"; 1433728Sbostic #endif 1533728Sbostic #endif /* not lint */ 1633728Sbostic 174212Seric # include <sys/types.h> 184212Seric # include <sys/stat.h> 198437Seric # include <signal.h> 2019784Seric # include <errno.h> 213309Seric # include "sendmail.h" 2219784Seric # include <sys/file.h> 2336928Sbostic # include <pwd.h> 24292Seric 25292Seric /* 26292Seric ** ALIAS -- Compute aliases. 27292Seric ** 289368Seric ** Scans the alias file for an alias for the given address. 299368Seric ** If found, it arranges to deliver to the alias list instead. 309368Seric ** Uses libdbm database if -DDBM. 31292Seric ** 32292Seric ** Parameters: 334097Seric ** a -- address to alias. 344999Seric ** sendq -- a pointer to the head of the send queue 354999Seric ** to put the aliases in. 36292Seric ** 37292Seric ** Returns: 38292Seric ** none 39292Seric ** 40292Seric ** Side Effects: 413185Seric ** Aliases found are expanded. 42292Seric ** 43292Seric ** Notes: 44292Seric ** If NoAlias (the "-n" flag) is set, no aliasing is 45292Seric ** done. 46292Seric ** 47292Seric ** Deficiencies: 48292Seric ** It should complain about names that are aliased to 49292Seric ** nothing. 50292Seric */ 51292Seric 52292Seric 531503Smark #ifdef DBM 542966Seric typedef struct 552966Seric { 562966Seric char *dptr; 574157Seric int dsize; 584157Seric } DATUM; 594157Seric extern DATUM fetch(); 601503Smark #endif DBM 61292Seric 624999Seric alias(a, sendq) 634097Seric register ADDRESS *a; 644999Seric ADDRESS **sendq; 65292Seric { 664081Seric register char *p; 675701Seric extern char *aliaslookup(); 68292Seric 697671Seric if (tTd(27, 1)) 704098Seric printf("alias(%s)\n", a->q_paddr); 71292Seric 724098Seric /* don't realias already aliased names */ 734098Seric if (bitset(QDONTSEND, a->q_flags)) 744098Seric return; 754098Seric 766898Seric CurEnv->e_to = a->q_paddr; 774098Seric 784314Seric /* 794314Seric ** Look up this name 804314Seric */ 814314Seric 8224944Seric if (NoAlias) 8324944Seric p = NULL; 8424944Seric else 8524944Seric p = aliaslookup(a->q_user); 864098Seric if (p == NULL) 874098Seric return; 88292Seric 89292Seric /* 904098Seric ** Match on Alias. 914098Seric ** Deliver to the target list. 921515Seric */ 931515Seric 947671Seric if (tTd(27, 1)) 954098Seric printf("%s (%s, %s) aliased to %s\n", 964098Seric a->q_paddr, a->q_host, a->q_user, p); 977051Seric message(Arpa_Info, "aliased to %s", p); 984098Seric AliasLevel++; 999614Seric sendtolist(p, a, sendq); 1004098Seric AliasLevel--; 1014098Seric } 1024098Seric /* 1035701Seric ** ALIASLOOKUP -- look up a name in the alias file. 1045701Seric ** 1055701Seric ** Parameters: 1065701Seric ** name -- the name to look up. 1075701Seric ** 1085701Seric ** Returns: 1095701Seric ** the value of name. 1105701Seric ** NULL if unknown. 1115701Seric ** 1125701Seric ** Side Effects: 1135701Seric ** none. 1145701Seric ** 1155701Seric ** Warnings: 1165701Seric ** The return value will be trashed across calls. 1175701Seric */ 1185701Seric 1195701Seric char * 1205701Seric aliaslookup(name) 1215701Seric char *name; 1225701Seric { 1235701Seric # ifdef DBM 1245701Seric DATUM rhs, lhs; 1255701Seric 1265701Seric /* create a key for fetch */ 1275701Seric lhs.dptr = name; 1285701Seric lhs.dsize = strlen(name) + 1; 1295701Seric rhs = fetch(lhs); 1305701Seric return (rhs.dptr); 1315701Seric # else DBM 1325701Seric register STAB *s; 1335701Seric 1345701Seric s = stab(name, ST_ALIAS, ST_FIND); 1355701Seric if (s == NULL) 1365701Seric return (NULL); 1375701Seric return (s->s_alias); 1385701Seric # endif DBM 1395701Seric } 1405701Seric /* 1414098Seric ** INITALIASES -- initialize for aliasing 1424098Seric ** 1434098Seric ** Very different depending on whether we are running DBM or not. 1444098Seric ** 1454098Seric ** Parameters: 1464098Seric ** aliasfile -- location of aliases. 1474157Seric ** init -- if set and if DBM, initialize the DBM files. 1484098Seric ** 1494098Seric ** Returns: 1504098Seric ** none. 1514098Seric ** 1524098Seric ** Side Effects: 1534098Seric ** initializes aliases: 1544098Seric ** if DBM: opens the database. 1554098Seric ** if ~DBM: reads the aliases into the symbol table. 1564098Seric */ 1574098Seric 15840559Sbostic # define DBMMODE 0644 1594157Seric 1604157Seric initaliases(aliasfile, init) 1614098Seric char *aliasfile; 1624157Seric bool init; 1634098Seric { 1649368Seric #ifdef DBM 1658437Seric int atcnt; 16625522Seric time_t modtime; 16725522Seric bool automatic = FALSE; 1684322Seric char buf[MAXNAME]; 1699368Seric #endif DBM 1709368Seric struct stat stb; 17127176Seric static bool initialized = FALSE; 1724322Seric 17327176Seric if (initialized) 17427176Seric return; 17527176Seric initialized = TRUE; 17627176Seric 17717984Seric if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 1788437Seric { 17925522Seric if (aliasfile != NULL && init) 18025522Seric syserr("Cannot open %s", aliasfile); 1818437Seric NoAlias = TRUE; 18211937Seric errno = 0; 1838437Seric return; 1848437Seric } 1858437Seric 1868928Seric # ifdef DBM 1874322Seric /* 1888437Seric ** Check to see that the alias file is complete. 1898437Seric ** If not, we will assume that someone died, and it is up 1908437Seric ** to us to rebuild it. 1918437Seric */ 1928437Seric 19325689Seric if (!init) 19425689Seric dbminit(aliasfile); 19517471Seric atcnt = SafeAlias * 2; 19617471Seric if (atcnt > 0) 19717471Seric { 19817471Seric while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 19925689Seric { 20025689Seric /* 20125689Seric ** Reinitialize alias file in case the new 20225689Seric ** one is mv'ed in instead of cp'ed in. 20325689Seric ** 20425689Seric ** Only works with new DBM -- old one will 20525689Seric ** just consume file descriptors forever. 20625689Seric ** If you have a dbmclose() it can be 20725689Seric ** added before the sleep(30). 20825689Seric */ 20925689Seric 21017471Seric sleep(30); 21125689Seric # ifdef NDBM 21225689Seric dbminit(aliasfile); 21325689Seric # endif NDBM 21425689Seric } 21517471Seric } 21617471Seric else 21717471Seric atcnt = 1; 2188437Seric 2198437Seric /* 2204322Seric ** See if the DBM version of the file is out of date with 2214322Seric ** the text version. If so, go into 'init' mode automatically. 22240559Sbostic ** This only happens if our effective userid owns the DBM. 22340559Sbostic ** Note the unpalatable hack to see if the stat succeeded. 2244322Seric */ 2254322Seric 2264322Seric modtime = stb.st_mtime; 2274322Seric (void) strcpy(buf, aliasfile); 2284322Seric (void) strcat(buf, ".pag"); 2294322Seric stb.st_ino = 0; 23019039Seric if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 2314322Seric { 23211937Seric errno = 0; 23340559Sbostic if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 2344322Seric { 2354322Seric init = TRUE; 23625522Seric automatic = TRUE; 2377051Seric message(Arpa_Info, "rebuilding alias database"); 23824944Seric #ifdef LOG 23924944Seric if (LogLevel >= 7) 24024944Seric syslog(LOG_INFO, "rebuilding alias database"); 24124944Seric #endif LOG 2424322Seric } 2434322Seric else 2444322Seric { 24519039Seric #ifdef LOG 24624944Seric if (LogLevel >= 7) 24724944Seric syslog(LOG_INFO, "alias database out of date"); 24819039Seric #endif LOG 2494322Seric message(Arpa_Info, "Warning: alias database out of date"); 2504322Seric } 2514322Seric } 2524322Seric 2534322Seric 2544322Seric /* 2558437Seric ** If necessary, load the DBM file. 2564322Seric ** If running without DBM, load the symbol table. 2574322Seric */ 2584322Seric 2594157Seric if (init) 2608437Seric { 26125522Seric #ifdef LOG 26225522Seric if (LogLevel >= 6) 26325522Seric { 26425522Seric extern char *username(); 26525522Seric 26625522Seric syslog(LOG_NOTICE, "alias database %srebuilt by %s", 26725522Seric automatic ? "auto" : "", username()); 26825522Seric } 26925522Seric #endif LOG 2704157Seric readaliases(aliasfile, TRUE); 2718437Seric } 2724098Seric # else DBM 2734157Seric readaliases(aliasfile, init); 2744157Seric # endif DBM 2754157Seric } 2764157Seric /* 2774157Seric ** READALIASES -- read and process the alias file. 2784157Seric ** 2794157Seric ** This routine implements the part of initaliases that occurs 2804157Seric ** when we are not going to use the DBM stuff. 2814157Seric ** 2824157Seric ** Parameters: 2834157Seric ** aliasfile -- the pathname of the alias file master. 2844157Seric ** init -- if set, initialize the DBM stuff. 2854157Seric ** 2864157Seric ** Returns: 2874157Seric ** none. 2884157Seric ** 2894157Seric ** Side Effects: 2904157Seric ** Reads aliasfile into the symbol table. 2914157Seric ** Optionally, builds the .dir & .pag files. 2924157Seric */ 2934157Seric 2944157Seric static 2954157Seric readaliases(aliasfile, init) 2964157Seric char *aliasfile; 2974157Seric bool init; 2984157Seric { 2994098Seric register char *p; 3004098Seric char *rhs; 3014098Seric bool skipping; 3029368Seric int naliases, bytes, longest; 3039368Seric FILE *af; 30440970Sbostic void (*oldsigint)(); 3054098Seric ADDRESS al, bl; 3064106Seric register STAB *s; 3079368Seric char line[BUFSIZ]; 3084098Seric 3094098Seric if ((af = fopen(aliasfile, "r")) == NULL) 3101515Seric { 3117671Seric if (tTd(27, 1)) 3124106Seric printf("Can't open %s\n", aliasfile); 3134098Seric errno = 0; 3144098Seric NoAlias++; 3154098Seric return; 3164098Seric } 3174314Seric 31819784Seric # ifdef DBM 31919784Seric /* see if someone else is rebuilding the alias file already */ 32019784Seric if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 32119784Seric { 32219784Seric /* yes, they are -- wait until done and then return */ 32319784Seric message(Arpa_Info, "Alias file is already being rebuilt"); 32419784Seric if (OpMode != MD_INITALIAS) 32519784Seric { 32619784Seric /* wait for other rebuild to complete */ 32719784Seric (void) flock(fileno(af), LOCK_EX); 32819784Seric } 32923108Seric (void) fclose(af); 33019784Seric errno = 0; 33119784Seric return; 33219784Seric } 33319784Seric # endif DBM 33419784Seric 3354314Seric /* 33619784Seric ** If initializing, create the new DBM files. 33719784Seric */ 33819784Seric 33919784Seric if (init) 34019784Seric { 34119784Seric oldsigint = signal(SIGINT, SIG_IGN); 34219784Seric (void) strcpy(line, aliasfile); 34319784Seric (void) strcat(line, ".dir"); 34419784Seric if (close(creat(line, DBMMODE)) < 0) 34519784Seric { 34619784Seric syserr("cannot make %s", line); 34719784Seric (void) signal(SIGINT, oldsigint); 34819784Seric return; 34919784Seric } 35019784Seric (void) strcpy(line, aliasfile); 35119784Seric (void) strcat(line, ".pag"); 35219784Seric if (close(creat(line, DBMMODE)) < 0) 35319784Seric { 35419784Seric syserr("cannot make %s", line); 35519784Seric (void) signal(SIGINT, oldsigint); 35619784Seric return; 35719784Seric } 35826501Seric dbminit(aliasfile); 35919784Seric } 36019784Seric 36119784Seric /* 3624314Seric ** Read and interpret lines 3634314Seric */ 3644314Seric 3659368Seric FileName = aliasfile; 3669368Seric LineNumber = 0; 3674322Seric naliases = bytes = longest = 0; 3684098Seric skipping = FALSE; 3694098Seric while (fgets(line, sizeof (line), af) != NULL) 3704098Seric { 3714322Seric int lhssize, rhssize; 3724322Seric 3739368Seric LineNumber++; 37425278Seric p = index(line, '\n'); 37525278Seric if (p != NULL) 37625278Seric *p = '\0'; 3774098Seric switch (line[0]) 3784098Seric { 3794098Seric case '#': 3804098Seric case '\0': 3814098Seric skipping = FALSE; 3824098Seric continue; 3834065Seric 3844098Seric case ' ': 3854098Seric case '\t': 3864098Seric if (!skipping) 3879368Seric syserr("Non-continuation line starts with space"); 3884098Seric skipping = TRUE; 3894097Seric continue; 3904098Seric } 3914098Seric skipping = FALSE; 3921874Seric 3934314Seric /* 3944314Seric ** Process the LHS 3954314Seric ** Find the final colon, and parse the address. 39616898Seric ** It should resolve to a local name -- this will 39716898Seric ** be checked later (we want to optionally do 39816898Seric ** parsing of the RHS first to maximize error 39916898Seric ** detection). 4004314Seric */ 4014314Seric 4024098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 4034097Seric continue; 40416898Seric if (*p++ != ':') 4054098Seric { 4069368Seric syserr("missing colon"); 4074097Seric continue; 4084098Seric } 40916898Seric if (parseaddr(line, &al, 1, ':') == NULL) 4104098Seric { 41116898Seric syserr("illegal alias name"); 41216898Seric continue; 4134098Seric } 41416898Seric loweraddr(&al); 4154314Seric 4164314Seric /* 4174314Seric ** Process the RHS. 4184314Seric ** 'al' is the internal form of the LHS address. 4194314Seric ** 'p' points to the text of the RHS. 4204314Seric */ 4214314Seric 4224098Seric rhs = p; 4234098Seric for (;;) 4244098Seric { 4254098Seric register char c; 4261515Seric 42725821Seric if (init && CheckAliases) 4284098Seric { 4294157Seric /* do parsing & compression of addresses */ 43025278Seric while (*p != '\0') 4314098Seric { 43225278Seric extern char *DelimChar; 43325278Seric 43425278Seric while (isspace(*p) || *p == ',') 4354157Seric p++; 43625278Seric if (*p == '\0') 43725278Seric break; 43825278Seric if (parseaddr(p, &bl, -1, ',') == NULL) 43925278Seric usrerr("%s... bad address", p); 44025278Seric p = DelimChar; 4414098Seric } 4424098Seric } 4434157Seric else 44415769Seric { 44516898Seric p = &p[strlen(p)]; 44616898Seric if (p[-1] == '\n') 44716898Seric *--p = '\0'; 44815769Seric } 4491515Seric 4504098Seric /* see if there should be a continuation line */ 4514106Seric c = fgetc(af); 4524106Seric if (!feof(af)) 4534314Seric (void) ungetc(c, af); 4544106Seric if (c != ' ' && c != '\t') 4554098Seric break; 4564098Seric 4574098Seric /* read continuation line */ 4584098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 4594098Seric break; 4609368Seric LineNumber++; 4614098Seric } 46216898Seric if (al.q_mailer != LocalMailer) 46316898Seric { 46416898Seric syserr("cannot alias non-local names"); 46516898Seric continue; 46616898Seric } 4674314Seric 4684314Seric /* 4694314Seric ** Insert alias into symbol table or DBM file 4704314Seric */ 4714314Seric 47216898Seric lhssize = strlen(al.q_user) + 1; 4734322Seric rhssize = strlen(rhs) + 1; 4744322Seric 4754157Seric # ifdef DBM 4764157Seric if (init) 4774157Seric { 4784157Seric DATUM key, content; 4794157Seric 4804322Seric key.dsize = lhssize; 4814157Seric key.dptr = al.q_user; 4824322Seric content.dsize = rhssize; 4834157Seric content.dptr = rhs; 4844157Seric store(key, content); 4854157Seric } 4864157Seric else 4874157Seric # endif DBM 4884157Seric { 4894157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 4904157Seric s->s_alias = newstr(rhs); 4914157Seric } 4924322Seric 4934322Seric /* statistics */ 4944322Seric naliases++; 4954322Seric bytes += lhssize + rhssize; 4964322Seric if (rhssize > longest) 4974322Seric longest = rhssize; 4981515Seric } 49919784Seric 50019784Seric # ifdef DBM 50119784Seric if (init) 50219784Seric { 50319784Seric /* add the distinquished alias "@" */ 50419784Seric DATUM key; 50519784Seric 50619784Seric key.dsize = 2; 50719784Seric key.dptr = "@"; 50819784Seric store(key, key); 50919784Seric 51019784Seric /* restore the old signal */ 51119784Seric (void) signal(SIGINT, oldsigint); 51219784Seric } 51319784Seric # endif DBM 51419784Seric 51519784Seric /* closing the alias file drops the lock */ 5164098Seric (void) fclose(af); 5176898Seric CurEnv->e_to = NULL; 5189368Seric FileName = NULL; 5197051Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 5204322Seric naliases, longest, bytes); 52124944Seric # ifdef LOG 52224944Seric if (LogLevel >= 8) 52324944Seric syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 52424944Seric naliases, longest, bytes); 52524944Seric # endif LOG 526292Seric } 527292Seric /* 528292Seric ** FORWARD -- Try to forward mail 529292Seric ** 530292Seric ** This is similar but not identical to aliasing. 531292Seric ** 532292Seric ** Parameters: 5334314Seric ** user -- the name of the user who's mail we would like 5344314Seric ** to forward to. It must have been verified -- 5354314Seric ** i.e., the q_home field must have been filled 5364314Seric ** in. 5374999Seric ** sendq -- a pointer to the head of the send queue to 5384999Seric ** put this user's aliases in. 539292Seric ** 540292Seric ** Returns: 5414098Seric ** none. 542292Seric ** 543292Seric ** Side Effects: 5443185Seric ** New names are added to send queues. 545292Seric */ 546292Seric 5474999Seric forward(user, sendq) 5482966Seric ADDRESS *user; 5494999Seric ADDRESS **sendq; 550292Seric { 5514078Seric char buf[60]; 5524536Seric extern bool safefile(); 5534069Seric 5547671Seric if (tTd(27, 1)) 5554098Seric printf("forward(%s)\n", user->q_paddr); 5564098Seric 5574594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 5584098Seric return; 5594314Seric if (user->q_home == NULL) 5604314Seric syserr("forward: no home"); 5614069Seric 5624069Seric /* good address -- look for .forward file in home */ 5639368Seric define('z', user->q_home, CurEnv); 56416154Seric expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 5654536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 5664098Seric return; 5674069Seric 5684069Seric /* we do have an address to forward to -- do it */ 5694999Seric include(buf, "forwarding", user, sendq); 570292Seric } 571