1292Seric # include <pwd.h> 24212Seric # include <sys/types.h> 34212Seric # include <sys/stat.h> 43309Seric # include "sendmail.h" 5292Seric 64106Seric # ifdef DBM 7*4536Seric static char SccsId[] = "@(#)alias.c 3.25 10/17/81 (with DBM)"; 84106Seric # else DBM 9*4536Seric static char SccsId[] = "@(#)alias.c 3.25 10/17/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. 21292Seric ** 22292Seric ** Returns: 23292Seric ** none 24292Seric ** 25292Seric ** Side Effects: 263185Seric ** Aliases found are expanded. 27292Seric ** 28292Seric ** Files: 294098Seric ** /usr/lib/aliases -- the mail aliases. The format is 30569Seric ** a series of lines of the form: 31569Seric ** alias:name1,name2,name3,... 32569Seric ** where 'alias' expands to all of 33569Seric ** 'name[i]'. Continuations begin with 34569Seric ** space or tab. 354098Seric ** /usr/lib/aliases.pag, /usr/lib/aliases.dir: libdbm version 361503Smark ** of alias file. Keys are aliases, datums 371503Smark ** (data?) are name1,name2, ... 38292Seric ** 39292Seric ** Notes: 40292Seric ** If NoAlias (the "-n" flag) is set, no aliasing is 41292Seric ** done. 42292Seric ** 43292Seric ** Deficiencies: 44292Seric ** It should complain about names that are aliased to 45292Seric ** nothing. 46292Seric ** It is unsophisticated about line overflows. 47292Seric */ 48292Seric 49292Seric 501503Smark #ifdef DBM 512966Seric typedef struct 522966Seric { 532966Seric char *dptr; 544157Seric int dsize; 554157Seric } DATUM; 564157Seric DATUM lhs, rhs; 574157Seric extern DATUM fetch(); 581503Smark #endif DBM 59292Seric 604097Seric alias(a) 614097Seric register ADDRESS *a; 62292Seric { 634081Seric register char *p; 644081Seric # ifndef DBM 654098Seric register STAB *s; 664081Seric # endif DBM 67292Seric 68292Seric if (NoAlias) 69292Seric return; 70292Seric # ifdef DEBUG 71292Seric if (Debug) 724098Seric printf("alias(%s)\n", a->q_paddr); 73292Seric # endif 74292Seric 754098Seric /* don't realias already aliased names */ 764098Seric if (bitset(QDONTSEND, a->q_flags)) 774098Seric return; 784098Seric 794098Seric To = a->q_paddr; 804098Seric 814314Seric /* 824314Seric ** Look up this name 834314Seric */ 844314Seric 854097Seric # ifdef DBM 864098Seric /* create a key for fetch */ 874098Seric lhs.dptr = a->q_user; 884098Seric lhs.dsize = strlen(a->q_user) + 1; 894098Seric rhs = fetch(lhs); 904098Seric 914098Seric /* find this alias? */ 924098Seric p = rhs.dptr; 934098Seric if (p == NULL) 944098Seric return; 954098Seric # else DBM 964098Seric s = stab(a->q_user, ST_ALIAS, ST_FIND); 974098Seric if (s == NULL) 984098Seric return; 994098Seric p = s->s_alias; 1004097Seric # endif DBM 101292Seric 102292Seric /* 1034098Seric ** Match on Alias. 1044098Seric ** Deliver to the target list. 1051515Seric */ 1061515Seric 1074098Seric # ifdef DEBUG 1084098Seric if (Debug) 1094098Seric printf("%s (%s, %s) aliased to %s\n", 1104098Seric a->q_paddr, a->q_host, a->q_user, p); 1114098Seric # endif 1124098Seric if (Verbose) 1134165Seric message(Arpa_Info, "aliased to %s", p); 1144098Seric AliasLevel++; 1154442Seric sendto(p, 1, a); 1164098Seric AliasLevel--; 1174098Seric } 1184098Seric /* 1194098Seric ** INITALIASES -- initialize for aliasing 1204098Seric ** 1214098Seric ** Very different depending on whether we are running DBM or not. 1224098Seric ** 1234098Seric ** Parameters: 1244098Seric ** aliasfile -- location of aliases. 1254157Seric ** init -- if set and if DBM, initialize the DBM files. 1264098Seric ** 1274098Seric ** Returns: 1284098Seric ** none. 1294098Seric ** 1304098Seric ** Side Effects: 1314098Seric ** initializes aliases: 1324098Seric ** if DBM: opens the database. 1334098Seric ** if ~DBM: reads the aliases into the symbol table. 1344098Seric */ 1354098Seric 1364157Seric # define DBMMODE 0666 1374157Seric 1384157Seric initaliases(aliasfile, init) 1394098Seric char *aliasfile; 1404157Seric bool init; 1414098Seric { 1424322Seric char buf[MAXNAME]; 1434322Seric struct stat stb; 1444322Seric time_t modtime; 1454322Seric 1464322Seric /* 1474322Seric ** See if the DBM version of the file is out of date with 1484322Seric ** the text version. If so, go into 'init' mode automatically. 1494322Seric ** This only happens if our effective userid owns the DBM 1504325Seric ** version or if the mode of the database is 666 -- this 1514322Seric ** is an attempt to avoid protection problems. Note the 1524322Seric ** unpalatable hack to see if the stat succeeded. 1534322Seric */ 1544322Seric 1554322Seric if (stat(aliasfile, &stb) < 0) 1564322Seric return; 1574098Seric # ifdef DBM 1584322Seric modtime = stb.st_mtime; 1594322Seric (void) strcpy(buf, aliasfile); 1604322Seric (void) strcat(buf, ".pag"); 1614322Seric stb.st_ino = 0; 1624322Seric if ((stat(buf, &stb) < 0 || stb.st_mtime < modtime) && !init) 1634322Seric { 1644322Seric if (stb.st_ino != 0 && 1654325Seric ((stb.st_mode & 0666) == 0666 || stb.st_uid == geteuid())) 1664322Seric { 1674322Seric init = TRUE; 1684322Seric if (Verbose) 1694322Seric message(Arpa_Info, "rebuilding alias database"); 1704322Seric } 1714322Seric else 1724322Seric { 1734322Seric message(Arpa_Info, "Warning: alias database out of date"); 1744322Seric } 1754322Seric } 1764322Seric # endif DBM 1774322Seric 1784322Seric /* 1794322Seric ** If initializing, create the new files. 1804322Seric ** We should lock the alias file here to prevent other 1814322Seric ** instantiations of sendmail from reading an incomplete 1824322Seric ** file -- or worse yet, doing a concurrent initialize. 1834322Seric */ 1844322Seric 1854322Seric # ifdef DBM 1864157Seric if (init) 1874157Seric { 1884157Seric (void) strcpy(buf, aliasfile); 1894157Seric (void) strcat(buf, ".dir"); 1904157Seric if (close(creat(buf, DBMMODE)) < 0) 1914157Seric { 1924157Seric syserr("cannot make %s", buf); 1934157Seric return; 1944157Seric } 1954157Seric (void) strcpy(buf, aliasfile); 1964157Seric (void) strcat(buf, ".pag"); 1974157Seric if (close(creat(buf, DBMMODE)) < 0) 1984157Seric { 1994157Seric syserr("cannot make %s", buf); 2004157Seric return; 2014157Seric } 2024157Seric } 2034322Seric 2044322Seric /* 2054322Seric ** Open and, if necessary, load the DBM file. 2064322Seric ** If running without DBM, load the symbol table. 2074322Seric */ 2084322Seric 2094098Seric dbminit(aliasfile); 2104157Seric if (init) 2114157Seric readaliases(aliasfile, TRUE); 2124098Seric # else DBM 2134157Seric readaliases(aliasfile, init); 2144157Seric # endif DBM 2154157Seric } 2164157Seric /* 2174157Seric ** READALIASES -- read and process the alias file. 2184157Seric ** 2194157Seric ** This routine implements the part of initaliases that occurs 2204157Seric ** when we are not going to use the DBM stuff. 2214157Seric ** 2224157Seric ** Parameters: 2234157Seric ** aliasfile -- the pathname of the alias file master. 2244157Seric ** init -- if set, initialize the DBM stuff. 2254157Seric ** 2264157Seric ** Returns: 2274157Seric ** none. 2284157Seric ** 2294157Seric ** Side Effects: 2304157Seric ** Reads aliasfile into the symbol table. 2314157Seric ** Optionally, builds the .dir & .pag files. 2324157Seric */ 2334157Seric 2344157Seric static 2354157Seric readaliases(aliasfile, init) 2364157Seric char *aliasfile; 2374157Seric bool init; 2384157Seric { 2394098Seric char line[BUFSIZ]; 2404098Seric register char *p; 2414098Seric char *p2; 2424098Seric char *rhs; 2434098Seric bool skipping; 2444098Seric ADDRESS al, bl; 2454106Seric FILE *af; 2464106Seric int lineno; 2474106Seric register STAB *s; 2484322Seric int naliases, bytes, longest; 2494098Seric 2504098Seric if ((af = fopen(aliasfile, "r")) == NULL) 2511515Seric { 2524098Seric # ifdef DEBUG 2534098Seric if (Debug) 2544106Seric printf("Can't open %s\n", aliasfile); 2554098Seric # endif 2564098Seric errno = 0; 2574098Seric NoAlias++; 2584098Seric return; 2594098Seric } 2604314Seric 2614314Seric /* 2624314Seric ** Read and interpret lines 2634314Seric */ 2644314Seric 2654098Seric lineno = 0; 2664322Seric naliases = bytes = longest = 0; 2674098Seric skipping = FALSE; 2684098Seric while (fgets(line, sizeof (line), af) != NULL) 2694098Seric { 2704322Seric int lhssize, rhssize; 2714322Seric 2724098Seric lineno++; 2734098Seric switch (line[0]) 2744098Seric { 2754098Seric case '#': 2764098Seric case '\n': 2774098Seric case '\0': 2784098Seric skipping = FALSE; 2794098Seric continue; 2804065Seric 2814098Seric case ' ': 2824098Seric case '\t': 2834098Seric if (!skipping) 2844098Seric syserr("aliases: %d: Non-continuation line starts with space", lineno); 2854098Seric skipping = TRUE; 2864097Seric continue; 2874098Seric } 2884098Seric skipping = FALSE; 2891874Seric 2904314Seric /* 2914314Seric ** Process the LHS 2924314Seric ** Find the final colon, and parse the address. 2934314Seric ** It should resolve to a local name -- this will 2944314Seric ** be checked later (we want to optionally do 2954314Seric ** parsing of the RHS first to maximize error 2964314Seric ** detection). 2974314Seric */ 2984314Seric 2994098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 3004097Seric continue; 3014098Seric if (*p == '\0' || *p == '\n') 3024098Seric { 3034098Seric syntaxerr: 3044098Seric syserr("aliases: %d: missing colon", lineno); 3054097Seric continue; 3064098Seric } 3074098Seric *p++ = '\0'; 3084098Seric if (parse(line, &al, 1) == NULL) 3094098Seric { 3104098Seric *--p = ':'; 3114098Seric goto syntaxerr; 3124098Seric } 3134314Seric 3144314Seric /* 3154314Seric ** Process the RHS. 3164314Seric ** 'al' is the internal form of the LHS address. 3174314Seric ** 'p' points to the text of the RHS. 3184314Seric */ 3194314Seric 3204098Seric rhs = p; 3214098Seric for (;;) 3224098Seric { 3234098Seric register char c; 3241515Seric 3254157Seric if (init) 3264098Seric { 3274157Seric /* do parsing & compression of addresses */ 3284098Seric c = *p; 3294157Seric while (c != '\0') 3304098Seric { 3314157Seric p2 = p; 3324157Seric while (*p != '\n' && *p != ',' && *p != '\0') 3334157Seric p++; 3344157Seric c = *p; 3354157Seric *p++ = '\0'; 3364322Seric if (c == '\n') 3374322Seric c = '\0'; 3384157Seric if (*p2 == '\0') 3394157Seric { 3404157Seric p[-1] = c; 3414157Seric continue; 3424157Seric } 3434314Seric (void) parse(p2, &bl, -1); 3444098Seric p[-1] = c; 3454157Seric while (isspace(*p)) 3464157Seric p++; 3474098Seric } 3484098Seric } 3494157Seric else 3504157Seric p = &p[strlen(p)]; 3511515Seric 3524098Seric /* see if there should be a continuation line */ 3534106Seric c = fgetc(af); 3544106Seric if (!feof(af)) 3554314Seric (void) ungetc(c, af); 3564106Seric if (c != ' ' && c != '\t') 3574098Seric break; 3584098Seric 3594098Seric /* read continuation line */ 3604098Seric p--; 3614098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 3624098Seric break; 3634098Seric lineno++; 3644098Seric } 3654193Seric if (al.q_mailer != MN_LOCAL) 3664098Seric { 3674098Seric syserr("aliases: %d: cannot alias non-local names", lineno); 3684098Seric continue; 3694098Seric } 3704314Seric 3714314Seric /* 3724314Seric ** Insert alias into symbol table or DBM file 3734314Seric */ 3744314Seric 3754322Seric lhssize = strlen(al.q_user) + 1; 3764322Seric rhssize = strlen(rhs) + 1; 3774322Seric 3784157Seric # ifdef DBM 3794157Seric if (init) 3804157Seric { 3814157Seric DATUM key, content; 3824157Seric 3834322Seric key.dsize = lhssize; 3844157Seric key.dptr = al.q_user; 3854322Seric content.dsize = rhssize; 3864157Seric content.dptr = rhs; 3874157Seric store(key, content); 3884157Seric } 3894157Seric else 3904157Seric # endif DBM 3914157Seric { 3924157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 3934157Seric s->s_alias = newstr(rhs); 3944157Seric } 3954322Seric 3964322Seric /* statistics */ 3974322Seric naliases++; 3984322Seric bytes += lhssize + rhssize; 3994322Seric if (rhssize > longest) 4004322Seric longest = rhssize; 4011515Seric } 4024098Seric (void) fclose(af); 4034322Seric To = NULL; 4044322Seric if (Verbose) 4054322Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 4064322Seric naliases, longest, bytes); 407292Seric } 408292Seric /* 409292Seric ** FORWARD -- Try to forward mail 410292Seric ** 411292Seric ** This is similar but not identical to aliasing. 412292Seric ** 413292Seric ** Parameters: 4144314Seric ** user -- the name of the user who's mail we would like 4154314Seric ** to forward to. It must have been verified -- 4164314Seric ** i.e., the q_home field must have been filled 4174314Seric ** in. 418292Seric ** 419292Seric ** Returns: 4204098Seric ** none. 421292Seric ** 422292Seric ** Side Effects: 4233185Seric ** New names are added to send queues. 424292Seric */ 425292Seric 426292Seric forward(user) 4272966Seric ADDRESS *user; 428292Seric { 4294078Seric char buf[60]; 430*4536Seric extern bool safefile(); 4314069Seric 4324098Seric # ifdef DEBUG 4334098Seric if (Debug) 4344098Seric printf("forward(%s)\n", user->q_paddr); 4354098Seric # endif DEBUG 4364098Seric 4374193Seric if (user->q_mailer != MN_LOCAL || bitset(QBADADDR, user->q_flags)) 4384098Seric return; 4394314Seric # ifdef DEBUG 4404314Seric if (user->q_home == NULL) 4414314Seric syserr("forward: no home"); 4424314Seric # endif DEBUG 4434069Seric 4444069Seric /* good address -- look for .forward file in home */ 4454104Seric define('z', user->q_home); 4464081Seric (void) expand("$z/.forward", buf, &buf[sizeof buf - 1]); 447*4536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 4484098Seric return; 4494069Seric 4504069Seric /* we do have an address to forward to -- do it */ 4514396Seric include(buf, "forwarding", user); 452292Seric } 453