122694Sdist /* 222694Sdist ** Sendmail 322694Sdist ** Copyright (c) 1983 Eric P. Allman 422694Sdist ** Berkeley, California 522694Sdist ** 622694Sdist ** Copyright (c) 1983 Regents of the University of California. 722694Sdist ** All rights reserved. The Berkeley software License Agreement 822694Sdist ** specifies the terms and conditions for redistribution. 922694Sdist */ 1022694Sdist 1122694Sdist #ifndef lint 1222903Smiriam # ifdef DBM 13*25689Seric static char SccsId[] = "@(#)alias.c 5.9 (Berkeley) 01/05/86 (with DBM)"; 1422903Smiriam # else DBM 15*25689Seric static char SccsId[] = "@(#)alias.c 5.9 (Berkeley) 01/05/86 (without DBM)"; 1622903Smiriam # endif DBM 1722694Sdist #endif not lint 1822694Sdist 19292Seric # include <pwd.h> 204212Seric # include <sys/types.h> 214212Seric # include <sys/stat.h> 228437Seric # include <signal.h> 2319784Seric # include <errno.h> 243309Seric # include "sendmail.h" 2519784Seric # ifdef FLOCK 2619784Seric # include <sys/file.h> 2719784Seric # endif FLOCK 28292Seric 29402Seric 30292Seric /* 31292Seric ** ALIAS -- Compute aliases. 32292Seric ** 339368Seric ** Scans the alias file for an alias for the given address. 349368Seric ** If found, it arranges to deliver to the alias list instead. 359368Seric ** Uses libdbm database if -DDBM. 36292Seric ** 37292Seric ** Parameters: 384097Seric ** a -- address to alias. 394999Seric ** sendq -- a pointer to the head of the send queue 404999Seric ** to put the aliases in. 41292Seric ** 42292Seric ** Returns: 43292Seric ** none 44292Seric ** 45292Seric ** Side Effects: 463185Seric ** Aliases found are expanded. 47292Seric ** 48292Seric ** Notes: 49292Seric ** If NoAlias (the "-n" flag) is set, no aliasing is 50292Seric ** done. 51292Seric ** 52292Seric ** Deficiencies: 53292Seric ** It should complain about names that are aliased to 54292Seric ** nothing. 55292Seric */ 56292Seric 57292Seric 581503Smark #ifdef DBM 592966Seric typedef struct 602966Seric { 612966Seric char *dptr; 624157Seric int dsize; 634157Seric } DATUM; 644157Seric extern DATUM fetch(); 651503Smark #endif DBM 66292Seric 674999Seric alias(a, sendq) 684097Seric register ADDRESS *a; 694999Seric ADDRESS **sendq; 70292Seric { 714081Seric register char *p; 725701Seric extern char *aliaslookup(); 73292Seric 74292Seric # ifdef DEBUG 757671Seric if (tTd(27, 1)) 764098Seric printf("alias(%s)\n", a->q_paddr); 77292Seric # endif 78292Seric 794098Seric /* don't realias already aliased names */ 804098Seric if (bitset(QDONTSEND, a->q_flags)) 814098Seric return; 824098Seric 836898Seric CurEnv->e_to = a->q_paddr; 844098Seric 854314Seric /* 864314Seric ** Look up this name 874314Seric */ 884314Seric 8924944Seric if (NoAlias) 9024944Seric p = NULL; 9124944Seric else 9224944Seric p = aliaslookup(a->q_user); 934098Seric if (p == NULL) 944098Seric return; 95292Seric 96292Seric /* 974098Seric ** Match on Alias. 984098Seric ** Deliver to the target list. 991515Seric */ 1001515Seric 1014098Seric # ifdef DEBUG 1027671Seric if (tTd(27, 1)) 1034098Seric printf("%s (%s, %s) aliased to %s\n", 1044098Seric a->q_paddr, a->q_host, a->q_user, p); 1054098Seric # endif 1067051Seric message(Arpa_Info, "aliased to %s", p); 1074098Seric AliasLevel++; 1089614Seric sendtolist(p, a, sendq); 1094098Seric AliasLevel--; 1104098Seric } 1114098Seric /* 1125701Seric ** ALIASLOOKUP -- look up a name in the alias file. 1135701Seric ** 1145701Seric ** Parameters: 1155701Seric ** name -- the name to look up. 1165701Seric ** 1175701Seric ** Returns: 1185701Seric ** the value of name. 1195701Seric ** NULL if unknown. 1205701Seric ** 1215701Seric ** Side Effects: 1225701Seric ** none. 1235701Seric ** 1245701Seric ** Warnings: 1255701Seric ** The return value will be trashed across calls. 1265701Seric */ 1275701Seric 1285701Seric char * 1295701Seric aliaslookup(name) 1305701Seric char *name; 1315701Seric { 1325701Seric # ifdef DBM 1335701Seric DATUM rhs, lhs; 1345701Seric 1355701Seric /* create a key for fetch */ 1365701Seric lhs.dptr = name; 1375701Seric lhs.dsize = strlen(name) + 1; 1385701Seric rhs = fetch(lhs); 1395701Seric return (rhs.dptr); 1405701Seric # else DBM 1415701Seric register STAB *s; 1425701Seric 1435701Seric s = stab(name, ST_ALIAS, ST_FIND); 1445701Seric if (s == NULL) 1455701Seric return (NULL); 1465701Seric return (s->s_alias); 1475701Seric # endif DBM 1485701Seric } 1495701Seric /* 1504098Seric ** INITALIASES -- initialize for aliasing 1514098Seric ** 1524098Seric ** Very different depending on whether we are running DBM or not. 1534098Seric ** 1544098Seric ** Parameters: 1554098Seric ** aliasfile -- location of aliases. 1564157Seric ** init -- if set and if DBM, initialize the DBM files. 1574098Seric ** 1584098Seric ** Returns: 1594098Seric ** none. 1604098Seric ** 1614098Seric ** Side Effects: 1624098Seric ** initializes aliases: 1634098Seric ** if DBM: opens the database. 1644098Seric ** if ~DBM: reads the aliases into the symbol table. 1654098Seric */ 1664098Seric 1674157Seric # define DBMMODE 0666 1684157Seric 1694157Seric initaliases(aliasfile, init) 1704098Seric char *aliasfile; 1714157Seric bool init; 1724098Seric { 1739368Seric #ifdef DBM 1748437Seric int atcnt; 17525522Seric time_t modtime; 17625522Seric bool automatic = FALSE; 1774322Seric char buf[MAXNAME]; 1789368Seric #endif DBM 1799368Seric struct stat stb; 1804322Seric 18117984Seric if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 1828437Seric { 18325522Seric if (aliasfile != NULL && init) 18425522Seric syserr("Cannot open %s", aliasfile); 1858437Seric NoAlias = TRUE; 18611937Seric errno = 0; 1878437Seric return; 1888437Seric } 1898437Seric 1908928Seric # ifdef DBM 1914322Seric /* 1928437Seric ** Check to see that the alias file is complete. 1938437Seric ** If not, we will assume that someone died, and it is up 1948437Seric ** to us to rebuild it. 1958437Seric */ 1968437Seric 197*25689Seric if (!init) 198*25689Seric dbminit(aliasfile); 19917471Seric atcnt = SafeAlias * 2; 20017471Seric if (atcnt > 0) 20117471Seric { 20217471Seric while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 203*25689Seric { 204*25689Seric /* 205*25689Seric ** Reinitialize alias file in case the new 206*25689Seric ** one is mv'ed in instead of cp'ed in. 207*25689Seric ** 208*25689Seric ** Only works with new DBM -- old one will 209*25689Seric ** just consume file descriptors forever. 210*25689Seric ** If you have a dbmclose() it can be 211*25689Seric ** added before the sleep(30). 212*25689Seric */ 213*25689Seric 21417471Seric sleep(30); 215*25689Seric # ifdef NDBM 216*25689Seric dbminit(aliasfile); 217*25689Seric # endif NDBM 218*25689Seric } 21917471Seric } 22017471Seric else 22117471Seric atcnt = 1; 2228437Seric 2238437Seric /* 2244322Seric ** See if the DBM version of the file is out of date with 2254322Seric ** the text version. If so, go into 'init' mode automatically. 2264322Seric ** This only happens if our effective userid owns the DBM 2274325Seric ** version or if the mode of the database is 666 -- this 2284322Seric ** is an attempt to avoid protection problems. Note the 2294322Seric ** unpalatable hack to see if the stat succeeded. 2304322Seric */ 2314322Seric 2324322Seric modtime = stb.st_mtime; 2334322Seric (void) strcpy(buf, aliasfile); 2344322Seric (void) strcat(buf, ".pag"); 2354322Seric stb.st_ino = 0; 23619039Seric if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 2374322Seric { 23811937Seric errno = 0; 2399150Seric if (AutoRebuild && stb.st_ino != 0 && 2409368Seric ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid())) 2414322Seric { 2424322Seric init = TRUE; 24325522Seric automatic = TRUE; 2447051Seric message(Arpa_Info, "rebuilding alias database"); 24524944Seric #ifdef LOG 24624944Seric if (LogLevel >= 7) 24724944Seric syslog(LOG_INFO, "rebuilding alias database"); 24824944Seric #endif LOG 2494322Seric } 2504322Seric else 2514322Seric { 25219039Seric #ifdef LOG 25324944Seric if (LogLevel >= 7) 25424944Seric syslog(LOG_INFO, "alias database out of date"); 25519039Seric #endif LOG 2564322Seric message(Arpa_Info, "Warning: alias database out of date"); 2574322Seric } 2584322Seric } 2594322Seric 2604322Seric 2614322Seric /* 2628437Seric ** If necessary, load the DBM file. 2634322Seric ** If running without DBM, load the symbol table. 2644322Seric */ 2654322Seric 2664157Seric if (init) 2678437Seric { 26825522Seric #ifdef LOG 26925522Seric if (LogLevel >= 6) 27025522Seric { 27125522Seric extern char *username(); 27225522Seric 27325522Seric syslog(LOG_NOTICE, "alias database %srebuilt by %s", 27425522Seric automatic ? "auto" : "", username()); 27525522Seric } 27625522Seric #endif LOG 2774157Seric readaliases(aliasfile, TRUE); 2788437Seric } 2794098Seric # else DBM 2804157Seric readaliases(aliasfile, init); 2814157Seric # endif DBM 2824157Seric } 2834157Seric /* 2844157Seric ** READALIASES -- read and process the alias file. 2854157Seric ** 2864157Seric ** This routine implements the part of initaliases that occurs 2874157Seric ** when we are not going to use the DBM stuff. 2884157Seric ** 2894157Seric ** Parameters: 2904157Seric ** aliasfile -- the pathname of the alias file master. 2914157Seric ** init -- if set, initialize the DBM stuff. 2924157Seric ** 2934157Seric ** Returns: 2944157Seric ** none. 2954157Seric ** 2964157Seric ** Side Effects: 2974157Seric ** Reads aliasfile into the symbol table. 2984157Seric ** Optionally, builds the .dir & .pag files. 2994157Seric */ 3004157Seric 3014157Seric static 3024157Seric readaliases(aliasfile, init) 3034157Seric char *aliasfile; 3044157Seric bool init; 3054157Seric { 3064098Seric register char *p; 3074098Seric char *rhs; 3084098Seric bool skipping; 3099368Seric int naliases, bytes, longest; 3109368Seric FILE *af; 31119784Seric int (*oldsigint)(); 3124098Seric ADDRESS al, bl; 3134106Seric register STAB *s; 3149368Seric char line[BUFSIZ]; 3154098Seric 3164098Seric if ((af = fopen(aliasfile, "r")) == NULL) 3171515Seric { 3184098Seric # ifdef DEBUG 3197671Seric if (tTd(27, 1)) 3204106Seric printf("Can't open %s\n", aliasfile); 3214098Seric # endif 3224098Seric errno = 0; 3234098Seric NoAlias++; 3244098Seric return; 3254098Seric } 3264314Seric 32719784Seric # ifdef DBM 32819784Seric # ifdef FLOCK 32919784Seric /* see if someone else is rebuilding the alias file already */ 33019784Seric if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 33119784Seric { 33219784Seric /* yes, they are -- wait until done and then return */ 33319784Seric message(Arpa_Info, "Alias file is already being rebuilt"); 33419784Seric if (OpMode != MD_INITALIAS) 33519784Seric { 33619784Seric /* wait for other rebuild to complete */ 33719784Seric (void) flock(fileno(af), LOCK_EX); 33819784Seric } 33923108Seric (void) fclose(af); 34019784Seric errno = 0; 34119784Seric return; 34219784Seric } 34319784Seric # endif FLOCK 34419784Seric # endif DBM 34519784Seric 3464314Seric /* 34719784Seric ** If initializing, create the new DBM files. 34819784Seric */ 34919784Seric 35019784Seric if (init) 35119784Seric { 35219784Seric oldsigint = signal(SIGINT, SIG_IGN); 35319784Seric (void) strcpy(line, aliasfile); 35419784Seric (void) strcat(line, ".dir"); 35519784Seric if (close(creat(line, DBMMODE)) < 0) 35619784Seric { 35719784Seric syserr("cannot make %s", line); 35819784Seric (void) signal(SIGINT, oldsigint); 35919784Seric return; 36019784Seric } 361*25689Seric dbminit(aliasfile); 36219784Seric (void) strcpy(line, aliasfile); 36319784Seric (void) strcat(line, ".pag"); 36419784Seric if (close(creat(line, DBMMODE)) < 0) 36519784Seric { 36619784Seric syserr("cannot make %s", line); 36719784Seric (void) signal(SIGINT, oldsigint); 36819784Seric return; 36919784Seric } 37019784Seric } 37119784Seric 37219784Seric /* 3734314Seric ** Read and interpret lines 3744314Seric */ 3754314Seric 3769368Seric FileName = aliasfile; 3779368Seric LineNumber = 0; 3784322Seric naliases = bytes = longest = 0; 3794098Seric skipping = FALSE; 3804098Seric while (fgets(line, sizeof (line), af) != NULL) 3814098Seric { 3824322Seric int lhssize, rhssize; 3834322Seric 3849368Seric LineNumber++; 38525278Seric p = index(line, '\n'); 38625278Seric if (p != NULL) 38725278Seric *p = '\0'; 3884098Seric switch (line[0]) 3894098Seric { 3904098Seric case '#': 3914098Seric case '\0': 3924098Seric skipping = FALSE; 3934098Seric continue; 3944065Seric 3954098Seric case ' ': 3964098Seric case '\t': 3974098Seric if (!skipping) 3989368Seric syserr("Non-continuation line starts with space"); 3994098Seric skipping = TRUE; 4004097Seric continue; 4014098Seric } 4024098Seric skipping = FALSE; 4031874Seric 4044314Seric /* 4054314Seric ** Process the LHS 4064314Seric ** Find the final colon, and parse the address. 40716898Seric ** It should resolve to a local name -- this will 40816898Seric ** be checked later (we want to optionally do 40916898Seric ** parsing of the RHS first to maximize error 41016898Seric ** detection). 4114314Seric */ 4124314Seric 4134098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 4144097Seric continue; 41516898Seric if (*p++ != ':') 4164098Seric { 4179368Seric syserr("missing colon"); 4184097Seric continue; 4194098Seric } 42016898Seric if (parseaddr(line, &al, 1, ':') == NULL) 4214098Seric { 42216898Seric syserr("illegal alias name"); 42316898Seric continue; 4244098Seric } 42516898Seric loweraddr(&al); 4264314Seric 4274314Seric /* 4284314Seric ** Process the RHS. 4294314Seric ** 'al' is the internal form of the LHS address. 4304314Seric ** 'p' points to the text of the RHS. 4314314Seric */ 4324314Seric 4334098Seric rhs = p; 4344098Seric for (;;) 4354098Seric { 4364098Seric register char c; 4371515Seric 4384157Seric if (init) 4394098Seric { 4404157Seric /* do parsing & compression of addresses */ 44125278Seric while (*p != '\0') 4424098Seric { 44325278Seric extern char *DelimChar; 44425278Seric 44525278Seric while (isspace(*p) || *p == ',') 4464157Seric p++; 44725278Seric if (*p == '\0') 44825278Seric break; 44925278Seric if (parseaddr(p, &bl, -1, ',') == NULL) 45025278Seric usrerr("%s... bad address", p); 45125278Seric p = DelimChar; 4524098Seric } 4534098Seric } 4544157Seric else 45515769Seric { 45616898Seric p = &p[strlen(p)]; 45716898Seric if (p[-1] == '\n') 45816898Seric *--p = '\0'; 45915769Seric } 4601515Seric 4614098Seric /* see if there should be a continuation line */ 4624106Seric c = fgetc(af); 4634106Seric if (!feof(af)) 4644314Seric (void) ungetc(c, af); 4654106Seric if (c != ' ' && c != '\t') 4664098Seric break; 4674098Seric 4684098Seric /* read continuation line */ 4694098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 4704098Seric break; 4719368Seric LineNumber++; 4724098Seric } 47316898Seric if (al.q_mailer != LocalMailer) 47416898Seric { 47516898Seric syserr("cannot alias non-local names"); 47616898Seric continue; 47716898Seric } 4784314Seric 4794314Seric /* 4804314Seric ** Insert alias into symbol table or DBM file 4814314Seric */ 4824314Seric 48316898Seric lhssize = strlen(al.q_user) + 1; 4844322Seric rhssize = strlen(rhs) + 1; 4854322Seric 4864157Seric # ifdef DBM 4874157Seric if (init) 4884157Seric { 4894157Seric DATUM key, content; 4904157Seric 4914322Seric key.dsize = lhssize; 4924157Seric key.dptr = al.q_user; 4934322Seric content.dsize = rhssize; 4944157Seric content.dptr = rhs; 4954157Seric store(key, content); 4964157Seric } 4974157Seric else 4984157Seric # endif DBM 4994157Seric { 5004157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 5014157Seric s->s_alias = newstr(rhs); 5024157Seric } 5034322Seric 5044322Seric /* statistics */ 5054322Seric naliases++; 5064322Seric bytes += lhssize + rhssize; 5074322Seric if (rhssize > longest) 5084322Seric longest = rhssize; 5091515Seric } 51019784Seric 51119784Seric # ifdef DBM 51219784Seric if (init) 51319784Seric { 51419784Seric /* add the distinquished alias "@" */ 51519784Seric DATUM key; 51619784Seric 51719784Seric key.dsize = 2; 51819784Seric key.dptr = "@"; 51919784Seric store(key, key); 52019784Seric 52119784Seric /* restore the old signal */ 52219784Seric (void) signal(SIGINT, oldsigint); 52319784Seric } 52419784Seric # endif DBM 52519784Seric 52619784Seric /* closing the alias file drops the lock */ 5274098Seric (void) fclose(af); 5286898Seric CurEnv->e_to = NULL; 5299368Seric FileName = NULL; 5307051Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 5314322Seric naliases, longest, bytes); 53224944Seric # ifdef LOG 53324944Seric if (LogLevel >= 8) 53424944Seric syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 53524944Seric naliases, longest, bytes); 53624944Seric # endif LOG 537292Seric } 538292Seric /* 539292Seric ** FORWARD -- Try to forward mail 540292Seric ** 541292Seric ** This is similar but not identical to aliasing. 542292Seric ** 543292Seric ** Parameters: 5444314Seric ** user -- the name of the user who's mail we would like 5454314Seric ** to forward to. It must have been verified -- 5464314Seric ** i.e., the q_home field must have been filled 5474314Seric ** in. 5484999Seric ** sendq -- a pointer to the head of the send queue to 5494999Seric ** put this user's aliases in. 550292Seric ** 551292Seric ** Returns: 5524098Seric ** none. 553292Seric ** 554292Seric ** Side Effects: 5553185Seric ** New names are added to send queues. 556292Seric */ 557292Seric 5584999Seric forward(user, sendq) 5592966Seric ADDRESS *user; 5604999Seric ADDRESS **sendq; 561292Seric { 5624078Seric char buf[60]; 5634536Seric extern bool safefile(); 5644069Seric 5654098Seric # ifdef DEBUG 5667671Seric if (tTd(27, 1)) 5674098Seric printf("forward(%s)\n", user->q_paddr); 5684098Seric # endif DEBUG 5694098Seric 5704594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 5714098Seric return; 5724314Seric # ifdef DEBUG 5734314Seric if (user->q_home == NULL) 5744314Seric syserr("forward: no home"); 5754314Seric # endif DEBUG 5764069Seric 5774069Seric /* good address -- look for .forward file in home */ 5789368Seric define('z', user->q_home, CurEnv); 57916154Seric expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 5804536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 5814098Seric return; 5824069Seric 5834069Seric /* we do have an address to forward to -- do it */ 5844999Seric include(buf, "forwarding", user, sendq); 585292Seric } 586