122694Sdist /* 235073Sbostic * Copyright (c) 1983 Eric P. Allman 333728Sbostic * Copyright (c) 1988 Regents of the University of California. 433728Sbostic * All rights reserved. 533728Sbostic * 642824Sbostic * %sccs.include.redist.c% 733728Sbostic */ 822694Sdist 933728Sbostic #ifndef lint 1033728Sbostic #ifdef DBM 11*46928Sbostic static char sccsid[] = "@(#)alias.c 5.22 (Berkeley) 03/02/91 (with DBM)"; 1233728Sbostic #else 13*46928Sbostic static char sccsid[] = "@(#)alias.c 5.22 (Berkeley) 03/02/91 (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; 172*46928Sbostic static int readaliases(); 1734322Seric 17427176Seric if (initialized) 17527176Seric return; 17627176Seric initialized = TRUE; 17727176Seric 17817984Seric if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 1798437Seric { 18025522Seric if (aliasfile != NULL && init) 18125522Seric syserr("Cannot open %s", aliasfile); 1828437Seric NoAlias = TRUE; 18311937Seric errno = 0; 1848437Seric return; 1858437Seric } 1868437Seric 1878928Seric # ifdef DBM 1884322Seric /* 1898437Seric ** Check to see that the alias file is complete. 1908437Seric ** If not, we will assume that someone died, and it is up 1918437Seric ** to us to rebuild it. 1928437Seric */ 1938437Seric 19425689Seric if (!init) 19525689Seric dbminit(aliasfile); 19617471Seric atcnt = SafeAlias * 2; 19717471Seric if (atcnt > 0) 19817471Seric { 19917471Seric while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 20025689Seric { 20125689Seric /* 20225689Seric ** Reinitialize alias file in case the new 20325689Seric ** one is mv'ed in instead of cp'ed in. 20425689Seric ** 20525689Seric ** Only works with new DBM -- old one will 20625689Seric ** just consume file descriptors forever. 20725689Seric ** If you have a dbmclose() it can be 20825689Seric ** added before the sleep(30). 20925689Seric */ 21025689Seric 21117471Seric sleep(30); 21225689Seric # ifdef NDBM 21325689Seric dbminit(aliasfile); 21425689Seric # endif NDBM 21525689Seric } 21617471Seric } 21717471Seric else 21817471Seric atcnt = 1; 2198437Seric 2208437Seric /* 2214322Seric ** See if the DBM version of the file is out of date with 2224322Seric ** the text version. If so, go into 'init' mode automatically. 22340559Sbostic ** This only happens if our effective userid owns the DBM. 22440559Sbostic ** Note the unpalatable hack to see if the stat succeeded. 2254322Seric */ 2264322Seric 2274322Seric modtime = stb.st_mtime; 2284322Seric (void) strcpy(buf, aliasfile); 2294322Seric (void) strcat(buf, ".pag"); 2304322Seric stb.st_ino = 0; 23119039Seric if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 2324322Seric { 23311937Seric errno = 0; 23440559Sbostic if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 2354322Seric { 2364322Seric init = TRUE; 23725522Seric automatic = TRUE; 2387051Seric message(Arpa_Info, "rebuilding alias database"); 23924944Seric #ifdef LOG 24024944Seric if (LogLevel >= 7) 24124944Seric syslog(LOG_INFO, "rebuilding alias database"); 24224944Seric #endif LOG 2434322Seric } 2444322Seric else 2454322Seric { 24619039Seric #ifdef LOG 24724944Seric if (LogLevel >= 7) 24824944Seric syslog(LOG_INFO, "alias database out of date"); 24919039Seric #endif LOG 2504322Seric message(Arpa_Info, "Warning: alias database out of date"); 2514322Seric } 2524322Seric } 2534322Seric 2544322Seric 2554322Seric /* 2568437Seric ** If necessary, load the DBM file. 2574322Seric ** If running without DBM, load the symbol table. 2584322Seric */ 2594322Seric 2604157Seric if (init) 2618437Seric { 26225522Seric #ifdef LOG 26325522Seric if (LogLevel >= 6) 26425522Seric { 26525522Seric extern char *username(); 26625522Seric 26725522Seric syslog(LOG_NOTICE, "alias database %srebuilt by %s", 26825522Seric automatic ? "auto" : "", username()); 26925522Seric } 27025522Seric #endif LOG 2714157Seric readaliases(aliasfile, TRUE); 2728437Seric } 2734098Seric # else DBM 2744157Seric readaliases(aliasfile, init); 2754157Seric # endif DBM 2764157Seric } 2774157Seric /* 2784157Seric ** READALIASES -- read and process the alias file. 2794157Seric ** 2804157Seric ** This routine implements the part of initaliases that occurs 2814157Seric ** when we are not going to use the DBM stuff. 2824157Seric ** 2834157Seric ** Parameters: 2844157Seric ** aliasfile -- the pathname of the alias file master. 2854157Seric ** init -- if set, initialize the DBM stuff. 2864157Seric ** 2874157Seric ** Returns: 2884157Seric ** none. 2894157Seric ** 2904157Seric ** Side Effects: 2914157Seric ** Reads aliasfile into the symbol table. 2924157Seric ** Optionally, builds the .dir & .pag files. 2934157Seric */ 2944157Seric 2954157Seric static 2964157Seric readaliases(aliasfile, init) 2974157Seric char *aliasfile; 2984157Seric bool init; 2994157Seric { 3004098Seric register char *p; 3014098Seric char *rhs; 3024098Seric bool skipping; 3039368Seric int naliases, bytes, longest; 3049368Seric FILE *af; 30540970Sbostic void (*oldsigint)(); 3064098Seric ADDRESS al, bl; 3074106Seric register STAB *s; 3089368Seric char line[BUFSIZ]; 3094098Seric 3104098Seric if ((af = fopen(aliasfile, "r")) == NULL) 3111515Seric { 3127671Seric if (tTd(27, 1)) 3134106Seric printf("Can't open %s\n", aliasfile); 3144098Seric errno = 0; 3154098Seric NoAlias++; 3164098Seric return; 3174098Seric } 3184314Seric 31919784Seric # ifdef DBM 32019784Seric /* see if someone else is rebuilding the alias file already */ 32119784Seric if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 32219784Seric { 32319784Seric /* yes, they are -- wait until done and then return */ 32419784Seric message(Arpa_Info, "Alias file is already being rebuilt"); 32519784Seric if (OpMode != MD_INITALIAS) 32619784Seric { 32719784Seric /* wait for other rebuild to complete */ 32819784Seric (void) flock(fileno(af), LOCK_EX); 32919784Seric } 33023108Seric (void) fclose(af); 33119784Seric errno = 0; 33219784Seric return; 33319784Seric } 33419784Seric # endif DBM 33519784Seric 3364314Seric /* 33719784Seric ** If initializing, create the new DBM files. 33819784Seric */ 33919784Seric 34019784Seric if (init) 34119784Seric { 34219784Seric oldsigint = signal(SIGINT, SIG_IGN); 34319784Seric (void) strcpy(line, aliasfile); 34419784Seric (void) strcat(line, ".dir"); 34519784Seric if (close(creat(line, DBMMODE)) < 0) 34619784Seric { 34719784Seric syserr("cannot make %s", line); 34819784Seric (void) signal(SIGINT, oldsigint); 34919784Seric return; 35019784Seric } 35119784Seric (void) strcpy(line, aliasfile); 35219784Seric (void) strcat(line, ".pag"); 35319784Seric if (close(creat(line, DBMMODE)) < 0) 35419784Seric { 35519784Seric syserr("cannot make %s", line); 35619784Seric (void) signal(SIGINT, oldsigint); 35719784Seric return; 35819784Seric } 35926501Seric dbminit(aliasfile); 36019784Seric } 36119784Seric 36219784Seric /* 3634314Seric ** Read and interpret lines 3644314Seric */ 3654314Seric 3669368Seric FileName = aliasfile; 3679368Seric LineNumber = 0; 3684322Seric naliases = bytes = longest = 0; 3694098Seric skipping = FALSE; 3704098Seric while (fgets(line, sizeof (line), af) != NULL) 3714098Seric { 3724322Seric int lhssize, rhssize; 3734322Seric 3749368Seric LineNumber++; 37525278Seric p = index(line, '\n'); 37625278Seric if (p != NULL) 37725278Seric *p = '\0'; 3784098Seric switch (line[0]) 3794098Seric { 3804098Seric case '#': 3814098Seric case '\0': 3824098Seric skipping = FALSE; 3834098Seric continue; 3844065Seric 3854098Seric case ' ': 3864098Seric case '\t': 3874098Seric if (!skipping) 3889368Seric syserr("Non-continuation line starts with space"); 3894098Seric skipping = TRUE; 3904097Seric continue; 3914098Seric } 3924098Seric skipping = FALSE; 3931874Seric 3944314Seric /* 3954314Seric ** Process the LHS 3964314Seric ** Find the final colon, and parse the address. 39716898Seric ** It should resolve to a local name -- this will 39816898Seric ** be checked later (we want to optionally do 39916898Seric ** parsing of the RHS first to maximize error 40016898Seric ** detection). 4014314Seric */ 4024314Seric 4034098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 4044097Seric continue; 40516898Seric if (*p++ != ':') 4064098Seric { 4079368Seric syserr("missing colon"); 4084097Seric continue; 4094098Seric } 41016898Seric if (parseaddr(line, &al, 1, ':') == NULL) 4114098Seric { 41216898Seric syserr("illegal alias name"); 41316898Seric continue; 4144098Seric } 41516898Seric loweraddr(&al); 4164314Seric 4174314Seric /* 4184314Seric ** Process the RHS. 4194314Seric ** 'al' is the internal form of the LHS address. 4204314Seric ** 'p' points to the text of the RHS. 4214314Seric */ 4224314Seric 4234098Seric rhs = p; 4244098Seric for (;;) 4254098Seric { 4264098Seric register char c; 4271515Seric 42825821Seric if (init && CheckAliases) 4294098Seric { 4304157Seric /* do parsing & compression of addresses */ 43125278Seric while (*p != '\0') 4324098Seric { 43325278Seric extern char *DelimChar; 43425278Seric 43525278Seric while (isspace(*p) || *p == ',') 4364157Seric p++; 43725278Seric if (*p == '\0') 43825278Seric break; 43925278Seric if (parseaddr(p, &bl, -1, ',') == NULL) 44025278Seric usrerr("%s... bad address", p); 44125278Seric p = DelimChar; 4424098Seric } 4434098Seric } 4444157Seric else 44515769Seric { 44616898Seric p = &p[strlen(p)]; 44716898Seric if (p[-1] == '\n') 44816898Seric *--p = '\0'; 44915769Seric } 4501515Seric 4514098Seric /* see if there should be a continuation line */ 4524106Seric c = fgetc(af); 4534106Seric if (!feof(af)) 4544314Seric (void) ungetc(c, af); 4554106Seric if (c != ' ' && c != '\t') 4564098Seric break; 4574098Seric 4584098Seric /* read continuation line */ 4594098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 4604098Seric break; 4619368Seric LineNumber++; 4624098Seric } 46316898Seric if (al.q_mailer != LocalMailer) 46416898Seric { 46516898Seric syserr("cannot alias non-local names"); 46616898Seric continue; 46716898Seric } 4684314Seric 4694314Seric /* 4704314Seric ** Insert alias into symbol table or DBM file 4714314Seric */ 4724314Seric 47316898Seric lhssize = strlen(al.q_user) + 1; 4744322Seric rhssize = strlen(rhs) + 1; 4754322Seric 4764157Seric # ifdef DBM 4774157Seric if (init) 4784157Seric { 4794157Seric DATUM key, content; 4804157Seric 4814322Seric key.dsize = lhssize; 4824157Seric key.dptr = al.q_user; 4834322Seric content.dsize = rhssize; 4844157Seric content.dptr = rhs; 4854157Seric store(key, content); 4864157Seric } 4874157Seric else 4884157Seric # endif DBM 4894157Seric { 4904157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 4914157Seric s->s_alias = newstr(rhs); 4924157Seric } 4934322Seric 4944322Seric /* statistics */ 4954322Seric naliases++; 4964322Seric bytes += lhssize + rhssize; 4974322Seric if (rhssize > longest) 4984322Seric longest = rhssize; 4991515Seric } 50019784Seric 50119784Seric # ifdef DBM 50219784Seric if (init) 50319784Seric { 50419784Seric /* add the distinquished alias "@" */ 50519784Seric DATUM key; 50619784Seric 50719784Seric key.dsize = 2; 50819784Seric key.dptr = "@"; 50919784Seric store(key, key); 51019784Seric 51119784Seric /* restore the old signal */ 51219784Seric (void) signal(SIGINT, oldsigint); 51319784Seric } 51419784Seric # endif DBM 51519784Seric 51619784Seric /* closing the alias file drops the lock */ 5174098Seric (void) fclose(af); 5186898Seric CurEnv->e_to = NULL; 5199368Seric FileName = NULL; 5207051Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 5214322Seric naliases, longest, bytes); 52224944Seric # ifdef LOG 52324944Seric if (LogLevel >= 8) 52424944Seric syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 52524944Seric naliases, longest, bytes); 52624944Seric # endif LOG 527292Seric } 528292Seric /* 529292Seric ** FORWARD -- Try to forward mail 530292Seric ** 531292Seric ** This is similar but not identical to aliasing. 532292Seric ** 533292Seric ** Parameters: 5344314Seric ** user -- the name of the user who's mail we would like 5354314Seric ** to forward to. It must have been verified -- 5364314Seric ** i.e., the q_home field must have been filled 5374314Seric ** in. 5384999Seric ** sendq -- a pointer to the head of the send queue to 5394999Seric ** put this user's aliases in. 540292Seric ** 541292Seric ** Returns: 5424098Seric ** none. 543292Seric ** 544292Seric ** Side Effects: 5453185Seric ** New names are added to send queues. 546292Seric */ 547292Seric 5484999Seric forward(user, sendq) 5492966Seric ADDRESS *user; 5504999Seric ADDRESS **sendq; 551292Seric { 5524078Seric char buf[60]; 5534536Seric extern bool safefile(); 5544069Seric 5557671Seric if (tTd(27, 1)) 5564098Seric printf("forward(%s)\n", user->q_paddr); 5574098Seric 5584594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 5594098Seric return; 5604314Seric if (user->q_home == NULL) 5614314Seric syserr("forward: no home"); 5624069Seric 5634069Seric /* good address -- look for .forward file in home */ 5649368Seric define('z', user->q_home, CurEnv); 56516154Seric expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 5664536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 5674098Seric return; 5684069Seric 5694069Seric /* we do have an address to forward to -- do it */ 5704999Seric include(buf, "forwarding", user, sendq); 571292Seric } 572