1292Seric # include <pwd.h> 24212Seric # include <sys/types.h> 34212Seric # include <sys/stat.h> 43309Seric # include "sendmail.h" 5292Seric 64106Seric # ifdef DBM 7*4999Seric static char SccsId[] = "@(#)alias.c 3.27 11/21/81 (with DBM)"; 84106Seric # else DBM 9*4999Seric static char SccsId[] = "@(#)alias.c 3.27 11/21/81 (without DBM)"; 104106Seric # endif DBM 11402Seric 12292Seric /* 13292Seric ** ALIAS -- Compute aliases. 14292Seric ** 154098Seric ** Scans the file /usr/lib/aliases for a set of aliases. 163185Seric ** If found, it arranges to deliver to them. Uses libdbm 173185Seric ** database if -DDBM. 18292Seric ** 19292Seric ** Parameters: 204097Seric ** a -- address to alias. 21*4999Seric ** sendq -- a pointer to the head of the send queue 22*4999Seric ** to put the aliases in. 23292Seric ** 24292Seric ** Returns: 25292Seric ** none 26292Seric ** 27292Seric ** Side Effects: 283185Seric ** Aliases found are expanded. 29292Seric ** 30292Seric ** Files: 314098Seric ** /usr/lib/aliases -- the mail aliases. The format is 32569Seric ** a series of lines of the form: 33569Seric ** alias:name1,name2,name3,... 34569Seric ** where 'alias' expands to all of 35569Seric ** 'name[i]'. Continuations begin with 36569Seric ** space or tab. 374098Seric ** /usr/lib/aliases.pag, /usr/lib/aliases.dir: libdbm version 381503Smark ** of alias file. Keys are aliases, datums 391503Smark ** (data?) are name1,name2, ... 40292Seric ** 41292Seric ** Notes: 42292Seric ** If NoAlias (the "-n" flag) is set, no aliasing is 43292Seric ** done. 44292Seric ** 45292Seric ** Deficiencies: 46292Seric ** It should complain about names that are aliased to 47292Seric ** nothing. 48292Seric ** It is unsophisticated about line overflows. 49292Seric */ 50292Seric 51292Seric 521503Smark #ifdef DBM 532966Seric typedef struct 542966Seric { 552966Seric char *dptr; 564157Seric int dsize; 574157Seric } DATUM; 584157Seric DATUM lhs, rhs; 594157Seric extern DATUM fetch(); 601503Smark #endif DBM 61292Seric 62*4999Seric alias(a, sendq) 634097Seric register ADDRESS *a; 64*4999Seric ADDRESS **sendq; 65292Seric { 664081Seric register char *p; 674081Seric # ifndef DBM 684098Seric register STAB *s; 694081Seric # endif DBM 70292Seric 71292Seric if (NoAlias) 72292Seric return; 73292Seric # ifdef DEBUG 74292Seric if (Debug) 754098Seric printf("alias(%s)\n", a->q_paddr); 76292Seric # endif 77292Seric 784098Seric /* don't realias already aliased names */ 794098Seric if (bitset(QDONTSEND, a->q_flags)) 804098Seric return; 814098Seric 824098Seric To = a->q_paddr; 834098Seric 844314Seric /* 854314Seric ** Look up this name 864314Seric */ 874314Seric 884097Seric # ifdef DBM 894098Seric /* create a key for fetch */ 904098Seric lhs.dptr = a->q_user; 914098Seric lhs.dsize = strlen(a->q_user) + 1; 924098Seric rhs = fetch(lhs); 934098Seric 944098Seric /* find this alias? */ 954098Seric p = rhs.dptr; 964098Seric if (p == NULL) 974098Seric return; 984098Seric # else DBM 994098Seric s = stab(a->q_user, ST_ALIAS, ST_FIND); 1004098Seric if (s == NULL) 1014098Seric return; 1024098Seric p = s->s_alias; 1034097Seric # endif DBM 104292Seric 105292Seric /* 1064098Seric ** Match on Alias. 1074098Seric ** Deliver to the target list. 1081515Seric */ 1091515Seric 1104098Seric # ifdef DEBUG 1114098Seric if (Debug) 1124098Seric printf("%s (%s, %s) aliased to %s\n", 1134098Seric a->q_paddr, a->q_host, a->q_user, p); 1144098Seric # endif 1154098Seric if (Verbose) 1164165Seric message(Arpa_Info, "aliased to %s", p); 1174098Seric AliasLevel++; 118*4999Seric sendto(p, 1, a, sendq); 1194098Seric AliasLevel--; 1204098Seric } 1214098Seric /* 1224098Seric ** INITALIASES -- initialize for aliasing 1234098Seric ** 1244098Seric ** Very different depending on whether we are running DBM or not. 1254098Seric ** 1264098Seric ** Parameters: 1274098Seric ** aliasfile -- location of aliases. 1284157Seric ** init -- if set and if DBM, initialize the DBM files. 1294098Seric ** 1304098Seric ** Returns: 1314098Seric ** none. 1324098Seric ** 1334098Seric ** Side Effects: 1344098Seric ** initializes aliases: 1354098Seric ** if DBM: opens the database. 1364098Seric ** if ~DBM: reads the aliases into the symbol table. 1374098Seric */ 1384098Seric 1394157Seric # define DBMMODE 0666 1404157Seric 1414157Seric initaliases(aliasfile, init) 1424098Seric char *aliasfile; 1434157Seric bool init; 1444098Seric { 1454322Seric char buf[MAXNAME]; 1464322Seric struct stat stb; 1474322Seric time_t modtime; 1484322Seric 1494322Seric /* 1504322Seric ** See if the DBM version of the file is out of date with 1514322Seric ** the text version. If so, go into 'init' mode automatically. 1524322Seric ** This only happens if our effective userid owns the DBM 1534325Seric ** version or if the mode of the database is 666 -- this 1544322Seric ** is an attempt to avoid protection problems. Note the 1554322Seric ** unpalatable hack to see if the stat succeeded. 1564322Seric */ 1574322Seric 1584322Seric if (stat(aliasfile, &stb) < 0) 1594322Seric return; 1604098Seric # ifdef DBM 1614322Seric modtime = stb.st_mtime; 1624322Seric (void) strcpy(buf, aliasfile); 1634322Seric (void) strcat(buf, ".pag"); 1644322Seric stb.st_ino = 0; 1654322Seric if ((stat(buf, &stb) < 0 || stb.st_mtime < modtime) && !init) 1664322Seric { 1674322Seric if (stb.st_ino != 0 && 1684325Seric ((stb.st_mode & 0666) == 0666 || stb.st_uid == geteuid())) 1694322Seric { 1704322Seric init = TRUE; 1714322Seric if (Verbose) 1724322Seric message(Arpa_Info, "rebuilding alias database"); 1734322Seric } 1744322Seric else 1754322Seric { 1764322Seric message(Arpa_Info, "Warning: alias database out of date"); 1774322Seric } 1784322Seric } 1794322Seric # endif DBM 1804322Seric 1814322Seric /* 1824322Seric ** If initializing, create the new files. 1834322Seric ** We should lock the alias file here to prevent other 1844322Seric ** instantiations of sendmail from reading an incomplete 1854322Seric ** file -- or worse yet, doing a concurrent initialize. 1864322Seric */ 1874322Seric 1884322Seric # ifdef DBM 1894157Seric if (init) 1904157Seric { 1914157Seric (void) strcpy(buf, aliasfile); 1924157Seric (void) strcat(buf, ".dir"); 1934157Seric if (close(creat(buf, DBMMODE)) < 0) 1944157Seric { 1954157Seric syserr("cannot make %s", buf); 1964157Seric return; 1974157Seric } 1984157Seric (void) strcpy(buf, aliasfile); 1994157Seric (void) strcat(buf, ".pag"); 2004157Seric if (close(creat(buf, DBMMODE)) < 0) 2014157Seric { 2024157Seric syserr("cannot make %s", buf); 2034157Seric return; 2044157Seric } 2054157Seric } 2064322Seric 2074322Seric /* 2084322Seric ** Open and, if necessary, load the DBM file. 2094322Seric ** If running without DBM, load the symbol table. 2104322Seric */ 2114322Seric 2124098Seric dbminit(aliasfile); 2134157Seric if (init) 2144157Seric readaliases(aliasfile, TRUE); 2154098Seric # else DBM 2164157Seric readaliases(aliasfile, init); 2174157Seric # endif DBM 2184157Seric } 2194157Seric /* 2204157Seric ** READALIASES -- read and process the alias file. 2214157Seric ** 2224157Seric ** This routine implements the part of initaliases that occurs 2234157Seric ** when we are not going to use the DBM stuff. 2244157Seric ** 2254157Seric ** Parameters: 2264157Seric ** aliasfile -- the pathname of the alias file master. 2274157Seric ** init -- if set, initialize the DBM stuff. 2284157Seric ** 2294157Seric ** Returns: 2304157Seric ** none. 2314157Seric ** 2324157Seric ** Side Effects: 2334157Seric ** Reads aliasfile into the symbol table. 2344157Seric ** Optionally, builds the .dir & .pag files. 2354157Seric */ 2364157Seric 2374157Seric static 2384157Seric readaliases(aliasfile, init) 2394157Seric char *aliasfile; 2404157Seric bool init; 2414157Seric { 2424098Seric char line[BUFSIZ]; 2434098Seric register char *p; 2444098Seric char *p2; 2454098Seric char *rhs; 2464098Seric bool skipping; 2474098Seric ADDRESS al, bl; 2484106Seric FILE *af; 2494106Seric int lineno; 2504106Seric register STAB *s; 2514322Seric int naliases, bytes, longest; 2524098Seric 2534098Seric if ((af = fopen(aliasfile, "r")) == NULL) 2541515Seric { 2554098Seric # ifdef DEBUG 2564098Seric if (Debug) 2574106Seric printf("Can't open %s\n", aliasfile); 2584098Seric # endif 2594098Seric errno = 0; 2604098Seric NoAlias++; 2614098Seric return; 2624098Seric } 2634314Seric 2644314Seric /* 2654314Seric ** Read and interpret lines 2664314Seric */ 2674314Seric 2684098Seric lineno = 0; 2694322Seric naliases = bytes = longest = 0; 2704098Seric skipping = FALSE; 2714098Seric while (fgets(line, sizeof (line), af) != NULL) 2724098Seric { 2734322Seric int lhssize, rhssize; 2744322Seric 2754098Seric lineno++; 2764098Seric switch (line[0]) 2774098Seric { 2784098Seric case '#': 2794098Seric case '\n': 2804098Seric case '\0': 2814098Seric skipping = FALSE; 2824098Seric continue; 2834065Seric 2844098Seric case ' ': 2854098Seric case '\t': 2864098Seric if (!skipping) 2874098Seric syserr("aliases: %d: Non-continuation line starts with space", lineno); 2884098Seric skipping = TRUE; 2894097Seric continue; 2904098Seric } 2914098Seric skipping = FALSE; 2921874Seric 2934314Seric /* 2944314Seric ** Process the LHS 2954314Seric ** Find the final colon, and parse the address. 2964314Seric ** It should resolve to a local name -- this will 2974314Seric ** be checked later (we want to optionally do 2984314Seric ** parsing of the RHS first to maximize error 2994314Seric ** detection). 3004314Seric */ 3014314Seric 3024098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 3034097Seric continue; 3044098Seric if (*p == '\0' || *p == '\n') 3054098Seric { 3064098Seric syntaxerr: 3074098Seric syserr("aliases: %d: missing colon", lineno); 3084097Seric continue; 3094098Seric } 3104098Seric *p++ = '\0'; 3114098Seric if (parse(line, &al, 1) == NULL) 3124098Seric { 3134098Seric *--p = ':'; 3144098Seric goto syntaxerr; 3154098Seric } 3164314Seric 3174314Seric /* 3184314Seric ** Process the RHS. 3194314Seric ** 'al' is the internal form of the LHS address. 3204314Seric ** 'p' points to the text of the RHS. 3214314Seric */ 3224314Seric 3234098Seric rhs = p; 3244098Seric for (;;) 3254098Seric { 3264098Seric register char c; 3271515Seric 3284157Seric if (init) 3294098Seric { 3304157Seric /* do parsing & compression of addresses */ 3314098Seric c = *p; 3324157Seric while (c != '\0') 3334098Seric { 3344157Seric p2 = p; 3354157Seric while (*p != '\n' && *p != ',' && *p != '\0') 3364157Seric p++; 3374157Seric c = *p; 3384157Seric *p++ = '\0'; 3394322Seric if (c == '\n') 3404322Seric c = '\0'; 3414157Seric if (*p2 == '\0') 3424157Seric { 3434157Seric p[-1] = c; 3444157Seric continue; 3454157Seric } 3464314Seric (void) parse(p2, &bl, -1); 3474098Seric p[-1] = c; 3484157Seric while (isspace(*p)) 3494157Seric p++; 3504098Seric } 3514098Seric } 3524157Seric else 3534157Seric p = &p[strlen(p)]; 3541515Seric 3554098Seric /* see if there should be a continuation line */ 3564106Seric c = fgetc(af); 3574106Seric if (!feof(af)) 3584314Seric (void) ungetc(c, af); 3594106Seric if (c != ' ' && c != '\t') 3604098Seric break; 3614098Seric 3624098Seric /* read continuation line */ 3634098Seric p--; 3644098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 3654098Seric break; 3664098Seric lineno++; 3674098Seric } 3684594Seric if (al.q_mailer != LocalMailer) 3694098Seric { 3704098Seric syserr("aliases: %d: cannot alias non-local names", lineno); 3714098Seric continue; 3724098Seric } 3734314Seric 3744314Seric /* 3754314Seric ** Insert alias into symbol table or DBM file 3764314Seric */ 3774314Seric 3784322Seric lhssize = strlen(al.q_user) + 1; 3794322Seric rhssize = strlen(rhs) + 1; 3804322Seric 3814157Seric # ifdef DBM 3824157Seric if (init) 3834157Seric { 3844157Seric DATUM key, content; 3854157Seric 3864322Seric key.dsize = lhssize; 3874157Seric key.dptr = al.q_user; 3884322Seric content.dsize = rhssize; 3894157Seric content.dptr = rhs; 3904157Seric store(key, content); 3914157Seric } 3924157Seric else 3934157Seric # endif DBM 3944157Seric { 3954157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 3964157Seric s->s_alias = newstr(rhs); 3974157Seric } 3984322Seric 3994322Seric /* statistics */ 4004322Seric naliases++; 4014322Seric bytes += lhssize + rhssize; 4024322Seric if (rhssize > longest) 4034322Seric longest = rhssize; 4041515Seric } 4054098Seric (void) fclose(af); 4064322Seric To = NULL; 4074322Seric if (Verbose) 4084322Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 4094322Seric naliases, longest, bytes); 410292Seric } 411292Seric /* 412292Seric ** FORWARD -- Try to forward mail 413292Seric ** 414292Seric ** This is similar but not identical to aliasing. 415292Seric ** 416292Seric ** Parameters: 4174314Seric ** user -- the name of the user who's mail we would like 4184314Seric ** to forward to. It must have been verified -- 4194314Seric ** i.e., the q_home field must have been filled 4204314Seric ** in. 421*4999Seric ** sendq -- a pointer to the head of the send queue to 422*4999Seric ** put this user's aliases in. 423292Seric ** 424292Seric ** Returns: 4254098Seric ** none. 426292Seric ** 427292Seric ** Side Effects: 4283185Seric ** New names are added to send queues. 429292Seric */ 430292Seric 431*4999Seric forward(user, sendq) 4322966Seric ADDRESS *user; 433*4999Seric ADDRESS **sendq; 434292Seric { 4354078Seric char buf[60]; 4364536Seric extern bool safefile(); 4374069Seric 4384098Seric # ifdef DEBUG 4394098Seric if (Debug) 4404098Seric printf("forward(%s)\n", user->q_paddr); 4414098Seric # endif DEBUG 4424098Seric 4434594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 4444098Seric return; 4454314Seric # ifdef DEBUG 4464314Seric if (user->q_home == NULL) 4474314Seric syserr("forward: no home"); 4484314Seric # endif DEBUG 4494069Seric 4504069Seric /* good address -- look for .forward file in home */ 4514104Seric define('z', user->q_home); 4524081Seric (void) expand("$z/.forward", buf, &buf[sizeof buf - 1]); 4534536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 4544098Seric return; 4554069Seric 4564069Seric /* we do have an address to forward to -- do it */ 457*4999Seric include(buf, "forwarding", user, sendq); 458292Seric } 459