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*50575Seric static char sccsid[] = "@(#)alias.c 5.23 (Berkeley) 07/26/91 (with DBM)"; 1233728Sbostic #else 13*50575Seric #ifdef NEWDB 14*50575Seric static char sccsid[] = "@(#)alias.c 5.23 (Berkeley) 07/26/91 (with NEWDB)"; 15*50575Seric #else 16*50575Seric static char sccsid[] = "@(#)alias.c 5.23 (Berkeley) 07/26/91 (without DBM)"; 1733728Sbostic #endif 18*50575Seric #endif 1933728Sbostic #endif /* not lint */ 2033728Sbostic 21*50575Seric # ifdef DBM 22*50575Seric # ifdef NEWDB 23*50575Seric # ERROR: must choose one of DBM or NEWDB compilation flags 24*50575Seric # endif 25*50575Seric # endif 26*50575Seric 274212Seric # include <sys/types.h> 284212Seric # include <sys/stat.h> 298437Seric # include <signal.h> 3019784Seric # include <errno.h> 313309Seric # include "sendmail.h" 3219784Seric # include <sys/file.h> 3336928Sbostic # include <pwd.h> 34292Seric 35*50575Seric # ifdef NEWDB 36*50575Seric # include <db.h> 37*50575Seric # endif 38*50575Seric 39292Seric /* 40292Seric ** ALIAS -- Compute aliases. 41292Seric ** 429368Seric ** Scans the alias file for an alias for the given address. 439368Seric ** If found, it arranges to deliver to the alias list instead. 449368Seric ** Uses libdbm database if -DDBM. 45292Seric ** 46292Seric ** Parameters: 474097Seric ** a -- address to alias. 484999Seric ** sendq -- a pointer to the head of the send queue 494999Seric ** to put the aliases in. 50292Seric ** 51292Seric ** Returns: 52292Seric ** none 53292Seric ** 54292Seric ** Side Effects: 553185Seric ** Aliases found are expanded. 56292Seric ** 57292Seric ** Notes: 58292Seric ** If NoAlias (the "-n" flag) is set, no aliasing is 59292Seric ** done. 60292Seric ** 61292Seric ** Deficiencies: 62292Seric ** It should complain about names that are aliased to 63292Seric ** nothing. 64292Seric */ 65292Seric 66292Seric 671503Smark #ifdef DBM 682966Seric typedef struct 692966Seric { 70*50575Seric char *data; 71*50575Seric int size; 72*50575Seric } DBT; 73*50575Seric extern DBT fetch(); 74*50575Seric #endif /* DBM */ 75292Seric 76*50575Seric #ifdef NEWDB 77*50575Seric static DB *AliasDBptr; 78*50575Seric #endif 79*50575Seric 804999Seric alias(a, sendq) 814097Seric register ADDRESS *a; 824999Seric ADDRESS **sendq; 83292Seric { 844081Seric register char *p; 855701Seric extern char *aliaslookup(); 86292Seric 877671Seric if (tTd(27, 1)) 884098Seric printf("alias(%s)\n", a->q_paddr); 89292Seric 904098Seric /* don't realias already aliased names */ 914098Seric if (bitset(QDONTSEND, a->q_flags)) 924098Seric return; 934098Seric 946898Seric CurEnv->e_to = a->q_paddr; 954098Seric 964314Seric /* 974314Seric ** Look up this name 984314Seric */ 994314Seric 10024944Seric if (NoAlias) 10124944Seric p = NULL; 10224944Seric else 10324944Seric p = aliaslookup(a->q_user); 1044098Seric if (p == NULL) 1054098Seric return; 106292Seric 107292Seric /* 1084098Seric ** Match on Alias. 1094098Seric ** Deliver to the target list. 1101515Seric */ 1111515Seric 1127671Seric if (tTd(27, 1)) 1134098Seric printf("%s (%s, %s) aliased to %s\n", 1144098Seric a->q_paddr, a->q_host, a->q_user, p); 1157051Seric message(Arpa_Info, "aliased to %s", p); 1164098Seric AliasLevel++; 1179614Seric sendtolist(p, a, sendq); 1184098Seric AliasLevel--; 1194098Seric } 1204098Seric /* 1215701Seric ** ALIASLOOKUP -- look up a name in the alias file. 1225701Seric ** 1235701Seric ** Parameters: 1245701Seric ** name -- the name to look up. 1255701Seric ** 1265701Seric ** Returns: 1275701Seric ** the value of name. 1285701Seric ** NULL if unknown. 1295701Seric ** 1305701Seric ** Side Effects: 1315701Seric ** none. 1325701Seric ** 1335701Seric ** Warnings: 1345701Seric ** The return value will be trashed across calls. 1355701Seric */ 1365701Seric 1375701Seric char * 1385701Seric aliaslookup(name) 1395701Seric char *name; 1405701Seric { 141*50575Seric # if defined(NEWDB) || defined(DBM) 142*50575Seric DBT rhs, lhs; 143*50575Seric int s; 1445701Seric 1455701Seric /* create a key for fetch */ 146*50575Seric lhs.data = name; 147*50575Seric lhs.size = strlen(name) + 1; 148*50575Seric # ifdef NEWDB 149*50575Seric s = AliasDBptr->get(AliasDBptr, &lhs, &rhs, 0); 150*50575Seric if (s != 0) 151*50575Seric return (NULL); 152*50575Seric # else 1535701Seric rhs = fetch(lhs); 154*50575Seric # endif 155*50575Seric return (rhs.data); 1565701Seric # else DBM 1575701Seric register STAB *s; 1585701Seric 1595701Seric s = stab(name, ST_ALIAS, ST_FIND); 1605701Seric if (s == NULL) 1615701Seric return (NULL); 1625701Seric return (s->s_alias); 1635701Seric # endif DBM 1645701Seric } 1655701Seric /* 1664098Seric ** INITALIASES -- initialize for aliasing 1674098Seric ** 1684098Seric ** Very different depending on whether we are running DBM or not. 1694098Seric ** 1704098Seric ** Parameters: 1714098Seric ** aliasfile -- location of aliases. 1724157Seric ** init -- if set and if DBM, initialize the DBM files. 1734098Seric ** 1744098Seric ** Returns: 1754098Seric ** none. 1764098Seric ** 1774098Seric ** Side Effects: 1784098Seric ** initializes aliases: 1794098Seric ** if DBM: opens the database. 1804098Seric ** if ~DBM: reads the aliases into the symbol table. 1814098Seric */ 1824098Seric 18340559Sbostic # define DBMMODE 0644 1844157Seric 1854157Seric initaliases(aliasfile, init) 1864098Seric char *aliasfile; 1874157Seric bool init; 1884098Seric { 189*50575Seric #if defined(DBM) || defined(NEWDB) 1908437Seric int atcnt; 19125522Seric time_t modtime; 19225522Seric bool automatic = FALSE; 1934322Seric char buf[MAXNAME]; 194*50575Seric #endif 1959368Seric struct stat stb; 19627176Seric static bool initialized = FALSE; 19746928Sbostic static int readaliases(); 1984322Seric 19927176Seric if (initialized) 20027176Seric return; 20127176Seric initialized = TRUE; 20227176Seric 20317984Seric if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 2048437Seric { 20525522Seric if (aliasfile != NULL && init) 20625522Seric syserr("Cannot open %s", aliasfile); 2078437Seric NoAlias = TRUE; 20811937Seric errno = 0; 2098437Seric return; 2108437Seric } 2118437Seric 212*50575Seric # if defined(DBM) || defined(NEWDB) 2134322Seric /* 2148437Seric ** Check to see that the alias file is complete. 2158437Seric ** If not, we will assume that someone died, and it is up 2168437Seric ** to us to rebuild it. 2178437Seric */ 2188437Seric 21925689Seric if (!init) 220*50575Seric { 221*50575Seric # ifdef NEWDB 222*50575Seric (void) strcpy(buf, aliasfile); 223*50575Seric (void) strcat(buf, ".db"); 224*50575Seric AliasDBptr = hash_open(buf, O_RDONLY, DBMMODE, NULL); 225*50575Seric # else 22625689Seric dbminit(aliasfile); 227*50575Seric # endif 228*50575Seric } 22917471Seric atcnt = SafeAlias * 2; 23017471Seric if (atcnt > 0) 23117471Seric { 23217471Seric while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 23325689Seric { 23425689Seric /* 23525689Seric ** Reinitialize alias file in case the new 23625689Seric ** one is mv'ed in instead of cp'ed in. 23725689Seric ** 23825689Seric ** Only works with new DBM -- old one will 23925689Seric ** just consume file descriptors forever. 24025689Seric ** If you have a dbmclose() it can be 24125689Seric ** added before the sleep(30). 24225689Seric */ 24325689Seric 244*50575Seric # ifdef NEWDB 245*50575Seric AliasDBptr->close(AliasDBptr); 246*50575Seric # endif 247*50575Seric 24817471Seric sleep(30); 249*50575Seric # ifdef NEWDB 250*50575Seric (void) strcpy(buf, aliasfile); 251*50575Seric (void) strcat(buf, ".db"); 252*50575Seric AliasDBptr = hash_open(buf, O_RDONLY, DBMMODE, NULL); 253*50575Seric # else 25425689Seric # ifdef NDBM 25525689Seric dbminit(aliasfile); 256*50575Seric # endif 257*50575Seric # endif 25825689Seric } 25917471Seric } 26017471Seric else 26117471Seric atcnt = 1; 2628437Seric 2638437Seric /* 2644322Seric ** See if the DBM version of the file is out of date with 2654322Seric ** the text version. If so, go into 'init' mode automatically. 26640559Sbostic ** This only happens if our effective userid owns the DBM. 26740559Sbostic ** Note the unpalatable hack to see if the stat succeeded. 2684322Seric */ 2694322Seric 2704322Seric modtime = stb.st_mtime; 2714322Seric (void) strcpy(buf, aliasfile); 272*50575Seric # ifdef NEWDB 273*50575Seric (void) strcat(buf, ".db"); 274*50575Seric # else 2754322Seric (void) strcat(buf, ".pag"); 276*50575Seric # endif 2774322Seric stb.st_ino = 0; 27819039Seric if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 2794322Seric { 28011937Seric errno = 0; 28140559Sbostic if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 2824322Seric { 2834322Seric init = TRUE; 28425522Seric automatic = TRUE; 2857051Seric message(Arpa_Info, "rebuilding alias database"); 28624944Seric #ifdef LOG 28724944Seric if (LogLevel >= 7) 28824944Seric syslog(LOG_INFO, "rebuilding alias database"); 28924944Seric #endif LOG 2904322Seric } 2914322Seric else 2924322Seric { 29319039Seric #ifdef LOG 29424944Seric if (LogLevel >= 7) 29524944Seric syslog(LOG_INFO, "alias database out of date"); 29619039Seric #endif LOG 2974322Seric message(Arpa_Info, "Warning: alias database out of date"); 2984322Seric } 2994322Seric } 3004322Seric 3014322Seric 3024322Seric /* 3038437Seric ** If necessary, load the DBM file. 3044322Seric ** If running without DBM, load the symbol table. 3054322Seric */ 3064322Seric 3074157Seric if (init) 3088437Seric { 30925522Seric #ifdef LOG 31025522Seric if (LogLevel >= 6) 31125522Seric { 31225522Seric extern char *username(); 31325522Seric 31425522Seric syslog(LOG_NOTICE, "alias database %srebuilt by %s", 31525522Seric automatic ? "auto" : "", username()); 31625522Seric } 31725522Seric #endif LOG 3184157Seric readaliases(aliasfile, TRUE); 3198437Seric } 3204098Seric # else DBM 3214157Seric readaliases(aliasfile, init); 3224157Seric # endif DBM 3234157Seric } 3244157Seric /* 3254157Seric ** READALIASES -- read and process the alias file. 3264157Seric ** 3274157Seric ** This routine implements the part of initaliases that occurs 3284157Seric ** when we are not going to use the DBM stuff. 3294157Seric ** 3304157Seric ** Parameters: 3314157Seric ** aliasfile -- the pathname of the alias file master. 3324157Seric ** init -- if set, initialize the DBM stuff. 3334157Seric ** 3344157Seric ** Returns: 3354157Seric ** none. 3364157Seric ** 3374157Seric ** Side Effects: 3384157Seric ** Reads aliasfile into the symbol table. 3394157Seric ** Optionally, builds the .dir & .pag files. 3404157Seric */ 3414157Seric 3424157Seric static 3434157Seric readaliases(aliasfile, init) 3444157Seric char *aliasfile; 3454157Seric bool init; 3464157Seric { 3474098Seric register char *p; 3484098Seric char *rhs; 3494098Seric bool skipping; 3509368Seric int naliases, bytes, longest; 3519368Seric FILE *af; 35240970Sbostic void (*oldsigint)(); 3534098Seric ADDRESS al, bl; 3544106Seric register STAB *s; 355*50575Seric # ifdef NEWDB 356*50575Seric DB *dbp; 357*50575Seric # endif 3589368Seric char line[BUFSIZ]; 3594098Seric 3604098Seric if ((af = fopen(aliasfile, "r")) == NULL) 3611515Seric { 3627671Seric if (tTd(27, 1)) 3634106Seric printf("Can't open %s\n", aliasfile); 3644098Seric errno = 0; 3654098Seric NoAlias++; 3664098Seric return; 3674098Seric } 3684314Seric 369*50575Seric # if defined(DBM) || defined(NEWDB) 37019784Seric /* see if someone else is rebuilding the alias file already */ 37119784Seric if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 37219784Seric { 37319784Seric /* yes, they are -- wait until done and then return */ 37419784Seric message(Arpa_Info, "Alias file is already being rebuilt"); 37519784Seric if (OpMode != MD_INITALIAS) 37619784Seric { 37719784Seric /* wait for other rebuild to complete */ 37819784Seric (void) flock(fileno(af), LOCK_EX); 37919784Seric } 38023108Seric (void) fclose(af); 38119784Seric errno = 0; 38219784Seric return; 38319784Seric } 38419784Seric # endif DBM 38519784Seric 3864314Seric /* 38719784Seric ** If initializing, create the new DBM files. 38819784Seric */ 38919784Seric 39019784Seric if (init) 39119784Seric { 39219784Seric oldsigint = signal(SIGINT, SIG_IGN); 393*50575Seric # ifdef DBM 39419784Seric (void) strcpy(line, aliasfile); 39519784Seric (void) strcat(line, ".dir"); 39619784Seric if (close(creat(line, DBMMODE)) < 0) 39719784Seric { 39819784Seric syserr("cannot make %s", line); 39919784Seric (void) signal(SIGINT, oldsigint); 40019784Seric return; 40119784Seric } 40219784Seric (void) strcpy(line, aliasfile); 40319784Seric (void) strcat(line, ".pag"); 40419784Seric if (close(creat(line, DBMMODE)) < 0) 40519784Seric { 40619784Seric syserr("cannot make %s", line); 40719784Seric (void) signal(SIGINT, oldsigint); 40819784Seric return; 40919784Seric } 41026501Seric dbminit(aliasfile); 411*50575Seric # endif 412*50575Seric # ifdef NEWDB 413*50575Seric (void) strcpy(line, aliasfile); 414*50575Seric (void) strcat(line, ".db"); 415*50575Seric dbp = hash_open(line, O_RDWR|O_CREAT|O_TRUNC, DBMMODE, NULL); 416*50575Seric # endif 41719784Seric } 41819784Seric 41919784Seric /* 4204314Seric ** Read and interpret lines 4214314Seric */ 4224314Seric 4239368Seric FileName = aliasfile; 4249368Seric LineNumber = 0; 4254322Seric naliases = bytes = longest = 0; 4264098Seric skipping = FALSE; 4274098Seric while (fgets(line, sizeof (line), af) != NULL) 4284098Seric { 4294322Seric int lhssize, rhssize; 4304322Seric 4319368Seric LineNumber++; 43225278Seric p = index(line, '\n'); 43325278Seric if (p != NULL) 43425278Seric *p = '\0'; 4354098Seric switch (line[0]) 4364098Seric { 4374098Seric case '#': 4384098Seric case '\0': 4394098Seric skipping = FALSE; 4404098Seric continue; 4414065Seric 4424098Seric case ' ': 4434098Seric case '\t': 4444098Seric if (!skipping) 4459368Seric syserr("Non-continuation line starts with space"); 4464098Seric skipping = TRUE; 4474097Seric continue; 4484098Seric } 4494098Seric skipping = FALSE; 4501874Seric 4514314Seric /* 4524314Seric ** Process the LHS 4534314Seric ** Find the final colon, and parse the address. 45416898Seric ** It should resolve to a local name -- this will 45516898Seric ** be checked later (we want to optionally do 45616898Seric ** parsing of the RHS first to maximize error 45716898Seric ** detection). 4584314Seric */ 4594314Seric 4604098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 4614097Seric continue; 46216898Seric if (*p++ != ':') 4634098Seric { 4649368Seric syserr("missing colon"); 4654097Seric continue; 4664098Seric } 46716898Seric if (parseaddr(line, &al, 1, ':') == NULL) 4684098Seric { 46916898Seric syserr("illegal alias name"); 47016898Seric continue; 4714098Seric } 47216898Seric loweraddr(&al); 4734314Seric 4744314Seric /* 4754314Seric ** Process the RHS. 4764314Seric ** 'al' is the internal form of the LHS address. 4774314Seric ** 'p' points to the text of the RHS. 4784314Seric */ 4794314Seric 4804098Seric rhs = p; 4814098Seric for (;;) 4824098Seric { 4834098Seric register char c; 4841515Seric 48525821Seric if (init && CheckAliases) 4864098Seric { 4874157Seric /* do parsing & compression of addresses */ 48825278Seric while (*p != '\0') 4894098Seric { 49025278Seric extern char *DelimChar; 49125278Seric 49225278Seric while (isspace(*p) || *p == ',') 4934157Seric p++; 49425278Seric if (*p == '\0') 49525278Seric break; 49625278Seric if (parseaddr(p, &bl, -1, ',') == NULL) 49725278Seric usrerr("%s... bad address", p); 49825278Seric p = DelimChar; 4994098Seric } 5004098Seric } 5014157Seric else 50215769Seric { 50316898Seric p = &p[strlen(p)]; 50416898Seric if (p[-1] == '\n') 50516898Seric *--p = '\0'; 50615769Seric } 5071515Seric 5084098Seric /* see if there should be a continuation line */ 5094106Seric c = fgetc(af); 5104106Seric if (!feof(af)) 5114314Seric (void) ungetc(c, af); 5124106Seric if (c != ' ' && c != '\t') 5134098Seric break; 5144098Seric 5154098Seric /* read continuation line */ 5164098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 5174098Seric break; 5189368Seric LineNumber++; 5194098Seric } 52016898Seric if (al.q_mailer != LocalMailer) 52116898Seric { 52216898Seric syserr("cannot alias non-local names"); 52316898Seric continue; 52416898Seric } 5254314Seric 5264314Seric /* 5274314Seric ** Insert alias into symbol table or DBM file 5284314Seric */ 5294314Seric 53016898Seric lhssize = strlen(al.q_user) + 1; 5314322Seric rhssize = strlen(rhs) + 1; 5324322Seric 533*50575Seric # if defined(DBM) || defined(NEWDB) 5344157Seric if (init) 5354157Seric { 536*50575Seric DBT key, content; 5374157Seric 538*50575Seric key.size = lhssize; 539*50575Seric key.data = al.q_user; 540*50575Seric content.size = rhssize; 541*50575Seric content.data = rhs; 542*50575Seric # ifdef DBM 5434157Seric store(key, content); 544*50575Seric # else 545*50575Seric if (dbp->put(dbp, &key, &content, R_PUT) != 0) 546*50575Seric { 547*50575Seric syserr("cannot put alias %s", al.q_user); 548*50575Seric } 549*50575Seric # endif 5504157Seric } 5514157Seric else 5524157Seric # endif DBM 5534157Seric { 5544157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 5554157Seric s->s_alias = newstr(rhs); 5564157Seric } 5574322Seric 5584322Seric /* statistics */ 5594322Seric naliases++; 5604322Seric bytes += lhssize + rhssize; 5614322Seric if (rhssize > longest) 5624322Seric longest = rhssize; 5631515Seric } 56419784Seric 565*50575Seric # if defined(DBM) || defined(NEWDB) 56619784Seric if (init) 56719784Seric { 56819784Seric /* add the distinquished alias "@" */ 569*50575Seric DBT key; 57019784Seric 571*50575Seric key.size = 2; 572*50575Seric key.data = "@"; 573*50575Seric # ifdef NEWDB 574*50575Seric dbp->put(dbp, &key, &key, R_PUT); 575*50575Seric dbp->close(dbp); 576*50575Seric # else 57719784Seric store(key, key); 578*50575Seric # endif 57919784Seric 58019784Seric /* restore the old signal */ 58119784Seric (void) signal(SIGINT, oldsigint); 58219784Seric } 58319784Seric # endif DBM 58419784Seric 58519784Seric /* closing the alias file drops the lock */ 5864098Seric (void) fclose(af); 5876898Seric CurEnv->e_to = NULL; 5889368Seric FileName = NULL; 5897051Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 5904322Seric naliases, longest, bytes); 59124944Seric # ifdef LOG 59224944Seric if (LogLevel >= 8) 59324944Seric syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 59424944Seric naliases, longest, bytes); 59524944Seric # endif LOG 596292Seric } 597292Seric /* 598292Seric ** FORWARD -- Try to forward mail 599292Seric ** 600292Seric ** This is similar but not identical to aliasing. 601292Seric ** 602292Seric ** Parameters: 6034314Seric ** user -- the name of the user who's mail we would like 6044314Seric ** to forward to. It must have been verified -- 6054314Seric ** i.e., the q_home field must have been filled 6064314Seric ** in. 6074999Seric ** sendq -- a pointer to the head of the send queue to 6084999Seric ** put this user's aliases in. 609292Seric ** 610292Seric ** Returns: 6114098Seric ** none. 612292Seric ** 613292Seric ** Side Effects: 6143185Seric ** New names are added to send queues. 615292Seric */ 616292Seric 6174999Seric forward(user, sendq) 6182966Seric ADDRESS *user; 6194999Seric ADDRESS **sendq; 620292Seric { 6214078Seric char buf[60]; 6224536Seric extern bool safefile(); 6234069Seric 6247671Seric if (tTd(27, 1)) 6254098Seric printf("forward(%s)\n", user->q_paddr); 6264098Seric 6274594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 6284098Seric return; 6294314Seric if (user->q_home == NULL) 6304314Seric syserr("forward: no home"); 6314069Seric 6324069Seric /* good address -- look for .forward file in home */ 6339368Seric define('z', user->q_home, CurEnv); 63416154Seric expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 6354536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 6364098Seric return; 6374069Seric 6384069Seric /* we do have an address to forward to -- do it */ 6394999Seric include(buf, "forwarding", user, sendq); 640292Seric } 641