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 11292Seric # include <pwd.h> 124212Seric # include <sys/types.h> 134212Seric # include <sys/stat.h> 148437Seric # include <signal.h> 1519784Seric # include <errno.h> 163309Seric # include "sendmail.h" 1719784Seric # ifdef FLOCK 1819784Seric # include <sys/file.h> 1919784Seric # endif FLOCK 20292Seric 2126435Seric #ifndef lint 2226435Seric # ifdef DBM 23*27176Seric static char SccsId[] = "@(#)alias.c 5.13 (Berkeley) 04/17/86 (with DBM)"; 2426435Seric # else DBM 25*27176Seric static char SccsId[] = "@(#)alias.c 5.13 (Berkeley) 04/17/86 (without DBM)"; 2626435Seric # endif DBM 2726435Seric #endif not lint 28402Seric 2926435Seric 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; 180*27176Seric static bool initialized = FALSE; 1814322Seric 182*27176Seric if (initialized) 183*27176Seric return; 184*27176Seric initialized = TRUE; 185*27176Seric 18617984Seric if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 1878437Seric { 18825522Seric if (aliasfile != NULL && init) 18925522Seric syserr("Cannot open %s", aliasfile); 1908437Seric NoAlias = TRUE; 19111937Seric errno = 0; 1928437Seric return; 1938437Seric } 1948437Seric 1958928Seric # ifdef DBM 1964322Seric /* 1978437Seric ** Check to see that the alias file is complete. 1988437Seric ** If not, we will assume that someone died, and it is up 1998437Seric ** to us to rebuild it. 2008437Seric */ 2018437Seric 20225689Seric if (!init) 20325689Seric dbminit(aliasfile); 20417471Seric atcnt = SafeAlias * 2; 20517471Seric if (atcnt > 0) 20617471Seric { 20717471Seric while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 20825689Seric { 20925689Seric /* 21025689Seric ** Reinitialize alias file in case the new 21125689Seric ** one is mv'ed in instead of cp'ed in. 21225689Seric ** 21325689Seric ** Only works with new DBM -- old one will 21425689Seric ** just consume file descriptors forever. 21525689Seric ** If you have a dbmclose() it can be 21625689Seric ** added before the sleep(30). 21725689Seric */ 21825689Seric 21917471Seric sleep(30); 22025689Seric # ifdef NDBM 22125689Seric dbminit(aliasfile); 22225689Seric # endif NDBM 22325689Seric } 22417471Seric } 22517471Seric else 22617471Seric atcnt = 1; 2278437Seric 2288437Seric /* 2294322Seric ** See if the DBM version of the file is out of date with 2304322Seric ** the text version. If so, go into 'init' mode automatically. 2314322Seric ** This only happens if our effective userid owns the DBM 2324325Seric ** version or if the mode of the database is 666 -- this 2334322Seric ** is an attempt to avoid protection problems. Note the 2344322Seric ** unpalatable hack to see if the stat succeeded. 2354322Seric */ 2364322Seric 2374322Seric modtime = stb.st_mtime; 2384322Seric (void) strcpy(buf, aliasfile); 2394322Seric (void) strcat(buf, ".pag"); 2404322Seric stb.st_ino = 0; 24119039Seric if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 2424322Seric { 24311937Seric errno = 0; 2449150Seric if (AutoRebuild && stb.st_ino != 0 && 2459368Seric ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid())) 2464322Seric { 2474322Seric init = TRUE; 24825522Seric automatic = TRUE; 2497051Seric message(Arpa_Info, "rebuilding alias database"); 25024944Seric #ifdef LOG 25124944Seric if (LogLevel >= 7) 25224944Seric syslog(LOG_INFO, "rebuilding alias database"); 25324944Seric #endif LOG 2544322Seric } 2554322Seric else 2564322Seric { 25719039Seric #ifdef LOG 25824944Seric if (LogLevel >= 7) 25924944Seric syslog(LOG_INFO, "alias database out of date"); 26019039Seric #endif LOG 2614322Seric message(Arpa_Info, "Warning: alias database out of date"); 2624322Seric } 2634322Seric } 2644322Seric 2654322Seric 2664322Seric /* 2678437Seric ** If necessary, load the DBM file. 2684322Seric ** If running without DBM, load the symbol table. 2694322Seric */ 2704322Seric 2714157Seric if (init) 2728437Seric { 27325522Seric #ifdef LOG 27425522Seric if (LogLevel >= 6) 27525522Seric { 27625522Seric extern char *username(); 27725522Seric 27825522Seric syslog(LOG_NOTICE, "alias database %srebuilt by %s", 27925522Seric automatic ? "auto" : "", username()); 28025522Seric } 28125522Seric #endif LOG 2824157Seric readaliases(aliasfile, TRUE); 2838437Seric } 2844098Seric # else DBM 2854157Seric readaliases(aliasfile, init); 2864157Seric # endif DBM 2874157Seric } 2884157Seric /* 2894157Seric ** READALIASES -- read and process the alias file. 2904157Seric ** 2914157Seric ** This routine implements the part of initaliases that occurs 2924157Seric ** when we are not going to use the DBM stuff. 2934157Seric ** 2944157Seric ** Parameters: 2954157Seric ** aliasfile -- the pathname of the alias file master. 2964157Seric ** init -- if set, initialize the DBM stuff. 2974157Seric ** 2984157Seric ** Returns: 2994157Seric ** none. 3004157Seric ** 3014157Seric ** Side Effects: 3024157Seric ** Reads aliasfile into the symbol table. 3034157Seric ** Optionally, builds the .dir & .pag files. 3044157Seric */ 3054157Seric 3064157Seric static 3074157Seric readaliases(aliasfile, init) 3084157Seric char *aliasfile; 3094157Seric bool init; 3104157Seric { 3114098Seric register char *p; 3124098Seric char *rhs; 3134098Seric bool skipping; 3149368Seric int naliases, bytes, longest; 3159368Seric FILE *af; 31619784Seric int (*oldsigint)(); 3174098Seric ADDRESS al, bl; 3184106Seric register STAB *s; 3199368Seric char line[BUFSIZ]; 3204098Seric 3214098Seric if ((af = fopen(aliasfile, "r")) == NULL) 3221515Seric { 3234098Seric # ifdef DEBUG 3247671Seric if (tTd(27, 1)) 3254106Seric printf("Can't open %s\n", aliasfile); 3264098Seric # endif 3274098Seric errno = 0; 3284098Seric NoAlias++; 3294098Seric return; 3304098Seric } 3314314Seric 33219784Seric # ifdef DBM 33319784Seric # ifdef FLOCK 33419784Seric /* see if someone else is rebuilding the alias file already */ 33519784Seric if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK) 33619784Seric { 33719784Seric /* yes, they are -- wait until done and then return */ 33819784Seric message(Arpa_Info, "Alias file is already being rebuilt"); 33919784Seric if (OpMode != MD_INITALIAS) 34019784Seric { 34119784Seric /* wait for other rebuild to complete */ 34219784Seric (void) flock(fileno(af), LOCK_EX); 34319784Seric } 34423108Seric (void) fclose(af); 34519784Seric errno = 0; 34619784Seric return; 34719784Seric } 34819784Seric # endif FLOCK 34919784Seric # endif DBM 35019784Seric 3514314Seric /* 35219784Seric ** If initializing, create the new DBM files. 35319784Seric */ 35419784Seric 35519784Seric if (init) 35619784Seric { 35719784Seric oldsigint = signal(SIGINT, SIG_IGN); 35819784Seric (void) strcpy(line, aliasfile); 35919784Seric (void) strcat(line, ".dir"); 36019784Seric if (close(creat(line, DBMMODE)) < 0) 36119784Seric { 36219784Seric syserr("cannot make %s", line); 36319784Seric (void) signal(SIGINT, oldsigint); 36419784Seric return; 36519784Seric } 36619784Seric (void) strcpy(line, aliasfile); 36719784Seric (void) strcat(line, ".pag"); 36819784Seric if (close(creat(line, DBMMODE)) < 0) 36919784Seric { 37019784Seric syserr("cannot make %s", line); 37119784Seric (void) signal(SIGINT, oldsigint); 37219784Seric return; 37319784Seric } 37426501Seric dbminit(aliasfile); 37519784Seric } 37619784Seric 37719784Seric /* 3784314Seric ** Read and interpret lines 3794314Seric */ 3804314Seric 3819368Seric FileName = aliasfile; 3829368Seric LineNumber = 0; 3834322Seric naliases = bytes = longest = 0; 3844098Seric skipping = FALSE; 3854098Seric while (fgets(line, sizeof (line), af) != NULL) 3864098Seric { 3874322Seric int lhssize, rhssize; 3884322Seric 3899368Seric LineNumber++; 39025278Seric p = index(line, '\n'); 39125278Seric if (p != NULL) 39225278Seric *p = '\0'; 3934098Seric switch (line[0]) 3944098Seric { 3954098Seric case '#': 3964098Seric case '\0': 3974098Seric skipping = FALSE; 3984098Seric continue; 3994065Seric 4004098Seric case ' ': 4014098Seric case '\t': 4024098Seric if (!skipping) 4039368Seric syserr("Non-continuation line starts with space"); 4044098Seric skipping = TRUE; 4054097Seric continue; 4064098Seric } 4074098Seric skipping = FALSE; 4081874Seric 4094314Seric /* 4104314Seric ** Process the LHS 4114314Seric ** Find the final colon, and parse the address. 41216898Seric ** It should resolve to a local name -- this will 41316898Seric ** be checked later (we want to optionally do 41416898Seric ** parsing of the RHS first to maximize error 41516898Seric ** detection). 4164314Seric */ 4174314Seric 4184098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 4194097Seric continue; 42016898Seric if (*p++ != ':') 4214098Seric { 4229368Seric syserr("missing colon"); 4234097Seric continue; 4244098Seric } 42516898Seric if (parseaddr(line, &al, 1, ':') == NULL) 4264098Seric { 42716898Seric syserr("illegal alias name"); 42816898Seric continue; 4294098Seric } 43016898Seric loweraddr(&al); 4314314Seric 4324314Seric /* 4334314Seric ** Process the RHS. 4344314Seric ** 'al' is the internal form of the LHS address. 4354314Seric ** 'p' points to the text of the RHS. 4364314Seric */ 4374314Seric 4384098Seric rhs = p; 4394098Seric for (;;) 4404098Seric { 4414098Seric register char c; 4421515Seric 44325821Seric if (init && CheckAliases) 4444098Seric { 4454157Seric /* do parsing & compression of addresses */ 44625278Seric while (*p != '\0') 4474098Seric { 44825278Seric extern char *DelimChar; 44925278Seric 45025278Seric while (isspace(*p) || *p == ',') 4514157Seric p++; 45225278Seric if (*p == '\0') 45325278Seric break; 45425278Seric if (parseaddr(p, &bl, -1, ',') == NULL) 45525278Seric usrerr("%s... bad address", p); 45625278Seric p = DelimChar; 4574098Seric } 4584098Seric } 4594157Seric else 46015769Seric { 46116898Seric p = &p[strlen(p)]; 46216898Seric if (p[-1] == '\n') 46316898Seric *--p = '\0'; 46415769Seric } 4651515Seric 4664098Seric /* see if there should be a continuation line */ 4674106Seric c = fgetc(af); 4684106Seric if (!feof(af)) 4694314Seric (void) ungetc(c, af); 4704106Seric if (c != ' ' && c != '\t') 4714098Seric break; 4724098Seric 4734098Seric /* read continuation line */ 4744098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 4754098Seric break; 4769368Seric LineNumber++; 4774098Seric } 47816898Seric if (al.q_mailer != LocalMailer) 47916898Seric { 48016898Seric syserr("cannot alias non-local names"); 48116898Seric continue; 48216898Seric } 4834314Seric 4844314Seric /* 4854314Seric ** Insert alias into symbol table or DBM file 4864314Seric */ 4874314Seric 48816898Seric lhssize = strlen(al.q_user) + 1; 4894322Seric rhssize = strlen(rhs) + 1; 4904322Seric 4914157Seric # ifdef DBM 4924157Seric if (init) 4934157Seric { 4944157Seric DATUM key, content; 4954157Seric 4964322Seric key.dsize = lhssize; 4974157Seric key.dptr = al.q_user; 4984322Seric content.dsize = rhssize; 4994157Seric content.dptr = rhs; 5004157Seric store(key, content); 5014157Seric } 5024157Seric else 5034157Seric # endif DBM 5044157Seric { 5054157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 5064157Seric s->s_alias = newstr(rhs); 5074157Seric } 5084322Seric 5094322Seric /* statistics */ 5104322Seric naliases++; 5114322Seric bytes += lhssize + rhssize; 5124322Seric if (rhssize > longest) 5134322Seric longest = rhssize; 5141515Seric } 51519784Seric 51619784Seric # ifdef DBM 51719784Seric if (init) 51819784Seric { 51919784Seric /* add the distinquished alias "@" */ 52019784Seric DATUM key; 52119784Seric 52219784Seric key.dsize = 2; 52319784Seric key.dptr = "@"; 52419784Seric store(key, key); 52519784Seric 52619784Seric /* restore the old signal */ 52719784Seric (void) signal(SIGINT, oldsigint); 52819784Seric } 52919784Seric # endif DBM 53019784Seric 53119784Seric /* closing the alias file drops the lock */ 5324098Seric (void) fclose(af); 5336898Seric CurEnv->e_to = NULL; 5349368Seric FileName = NULL; 5357051Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 5364322Seric naliases, longest, bytes); 53724944Seric # ifdef LOG 53824944Seric if (LogLevel >= 8) 53924944Seric syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 54024944Seric naliases, longest, bytes); 54124944Seric # endif LOG 542292Seric } 543292Seric /* 544292Seric ** FORWARD -- Try to forward mail 545292Seric ** 546292Seric ** This is similar but not identical to aliasing. 547292Seric ** 548292Seric ** Parameters: 5494314Seric ** user -- the name of the user who's mail we would like 5504314Seric ** to forward to. It must have been verified -- 5514314Seric ** i.e., the q_home field must have been filled 5524314Seric ** in. 5534999Seric ** sendq -- a pointer to the head of the send queue to 5544999Seric ** put this user's aliases in. 555292Seric ** 556292Seric ** Returns: 5574098Seric ** none. 558292Seric ** 559292Seric ** Side Effects: 5603185Seric ** New names are added to send queues. 561292Seric */ 562292Seric 5634999Seric forward(user, sendq) 5642966Seric ADDRESS *user; 5654999Seric ADDRESS **sendq; 566292Seric { 5674078Seric char buf[60]; 5684536Seric extern bool safefile(); 5694069Seric 5704098Seric # ifdef DEBUG 5717671Seric if (tTd(27, 1)) 5724098Seric printf("forward(%s)\n", user->q_paddr); 5734098Seric # endif DEBUG 5744098Seric 5754594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 5764098Seric return; 5774314Seric # ifdef DEBUG 5784314Seric if (user->q_home == NULL) 5794314Seric syserr("forward: no home"); 5804314Seric # endif DEBUG 5814069Seric 5824069Seric /* good address -- look for .forward file in home */ 5839368Seric define('z', user->q_home, CurEnv); 58416154Seric expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 5854536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 5864098Seric return; 5874069Seric 5884069Seric /* we do have an address to forward to -- do it */ 5894999Seric include(buf, "forwarding", user, sendq); 590292Seric } 591