1292Seric # include <pwd.h> 24212Seric # include <sys/types.h> 34212Seric # include <sys/stat.h> 43309Seric # include "sendmail.h" 5292Seric 64106Seric # ifdef DBM 7*4322Seric static char SccsId[] = "@(#)alias.c 3.20 09/07/81 (with DBM)"; 84106Seric # else DBM 9*4322Seric static char SccsId[] = "@(#)alias.c 3.20 09/07/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 a->q_flags |= QDONTSEND; 1154098Seric AliasLevel++; 1164098Seric sendto(p, 1); 1174098Seric AliasLevel--; 1184098Seric } 1194098Seric /* 1204098Seric ** INITALIASES -- initialize for aliasing 1214098Seric ** 1224098Seric ** Very different depending on whether we are running DBM or not. 1234098Seric ** 1244098Seric ** Parameters: 1254098Seric ** aliasfile -- location of aliases. 1264157Seric ** init -- if set and if DBM, initialize the DBM files. 1274098Seric ** 1284098Seric ** Returns: 1294098Seric ** none. 1304098Seric ** 1314098Seric ** Side Effects: 1324098Seric ** initializes aliases: 1334098Seric ** if DBM: opens the database. 1344098Seric ** if ~DBM: reads the aliases into the symbol table. 1354098Seric */ 1364098Seric 1374157Seric # define DBMMODE 0666 1384157Seric 1394157Seric initaliases(aliasfile, init) 1404098Seric char *aliasfile; 1414157Seric bool init; 1424098Seric { 143*4322Seric char buf[MAXNAME]; 144*4322Seric struct stat stb; 145*4322Seric time_t modtime; 146*4322Seric 147*4322Seric /* 148*4322Seric ** See if the DBM version of the file is out of date with 149*4322Seric ** the text version. If so, go into 'init' mode automatically. 150*4322Seric ** This only happens if our effective userid owns the DBM 151*4322Seric ** version or if the mode of the database is 777 -- this 152*4322Seric ** is an attempt to avoid protection problems. Note the 153*4322Seric ** unpalatable hack to see if the stat succeeded. 154*4322Seric */ 155*4322Seric 156*4322Seric if (stat(aliasfile, &stb) < 0) 157*4322Seric return; 1584098Seric # ifdef DBM 159*4322Seric modtime = stb.st_mtime; 160*4322Seric (void) strcpy(buf, aliasfile); 161*4322Seric (void) strcat(buf, ".pag"); 162*4322Seric stb.st_ino = 0; 163*4322Seric if ((stat(buf, &stb) < 0 || stb.st_mtime < modtime) && !init) 164*4322Seric { 165*4322Seric if (stb.st_ino != 0 && 166*4322Seric ((stb.st_mode & 0777) == 0777 || stb.st_uid == geteuid())) 167*4322Seric { 168*4322Seric init = TRUE; 169*4322Seric if (Verbose) 170*4322Seric message(Arpa_Info, "rebuilding alias database"); 171*4322Seric } 172*4322Seric else 173*4322Seric { 174*4322Seric message(Arpa_Info, "Warning: alias database out of date"); 175*4322Seric } 176*4322Seric } 177*4322Seric # endif DBM 178*4322Seric 179*4322Seric /* 180*4322Seric ** If initializing, create the new files. 181*4322Seric ** We should lock the alias file here to prevent other 182*4322Seric ** instantiations of sendmail from reading an incomplete 183*4322Seric ** file -- or worse yet, doing a concurrent initialize. 184*4322Seric */ 185*4322Seric 186*4322Seric # ifdef DBM 1874157Seric if (init) 1884157Seric { 1894157Seric (void) strcpy(buf, aliasfile); 1904157Seric (void) strcat(buf, ".dir"); 1914157Seric if (close(creat(buf, DBMMODE)) < 0) 1924157Seric { 1934157Seric syserr("cannot make %s", buf); 1944157Seric return; 1954157Seric } 1964157Seric (void) strcpy(buf, aliasfile); 1974157Seric (void) strcat(buf, ".pag"); 1984157Seric if (close(creat(buf, DBMMODE)) < 0) 1994157Seric { 2004157Seric syserr("cannot make %s", buf); 2014157Seric return; 2024157Seric } 2034157Seric } 204*4322Seric 205*4322Seric /* 206*4322Seric ** Open and, if necessary, load the DBM file. 207*4322Seric ** If running without DBM, load the symbol table. 208*4322Seric */ 209*4322Seric 2104098Seric dbminit(aliasfile); 2114157Seric if (init) 2124157Seric readaliases(aliasfile, TRUE); 2134098Seric # else DBM 2144157Seric readaliases(aliasfile, init); 2154157Seric # endif DBM 2164157Seric } 2174157Seric /* 2184157Seric ** READALIASES -- read and process the alias file. 2194157Seric ** 2204157Seric ** This routine implements the part of initaliases that occurs 2214157Seric ** when we are not going to use the DBM stuff. 2224157Seric ** 2234157Seric ** Parameters: 2244157Seric ** aliasfile -- the pathname of the alias file master. 2254157Seric ** init -- if set, initialize the DBM stuff. 2264157Seric ** 2274157Seric ** Returns: 2284157Seric ** none. 2294157Seric ** 2304157Seric ** Side Effects: 2314157Seric ** Reads aliasfile into the symbol table. 2324157Seric ** Optionally, builds the .dir & .pag files. 2334157Seric */ 2344157Seric 2354157Seric static 2364157Seric readaliases(aliasfile, init) 2374157Seric char *aliasfile; 2384157Seric bool init; 2394157Seric { 2404098Seric char line[BUFSIZ]; 2414098Seric register char *p; 2424098Seric char *p2; 2434098Seric char *rhs; 2444098Seric bool skipping; 2454098Seric ADDRESS al, bl; 2464106Seric FILE *af; 2474106Seric int lineno; 2484106Seric register STAB *s; 249*4322Seric int naliases, bytes, longest; 2504098Seric 2514098Seric if ((af = fopen(aliasfile, "r")) == NULL) 2521515Seric { 2534098Seric # ifdef DEBUG 2544098Seric if (Debug) 2554106Seric printf("Can't open %s\n", aliasfile); 2564098Seric # endif 2574098Seric errno = 0; 2584098Seric NoAlias++; 2594098Seric return; 2604098Seric } 2614314Seric 2624314Seric /* 2634314Seric ** Read and interpret lines 2644314Seric */ 2654314Seric 2664098Seric lineno = 0; 267*4322Seric naliases = bytes = longest = 0; 2684098Seric skipping = FALSE; 2694098Seric while (fgets(line, sizeof (line), af) != NULL) 2704098Seric { 271*4322Seric int lhssize, rhssize; 272*4322Seric 2734098Seric lineno++; 2744098Seric switch (line[0]) 2754098Seric { 2764098Seric case '#': 2774098Seric case '\n': 2784098Seric case '\0': 2794098Seric skipping = FALSE; 2804098Seric continue; 2814065Seric 2824098Seric case ' ': 2834098Seric case '\t': 2844098Seric if (!skipping) 2854098Seric syserr("aliases: %d: Non-continuation line starts with space", lineno); 2864098Seric skipping = TRUE; 2874097Seric continue; 2884098Seric } 2894098Seric skipping = FALSE; 2901874Seric 2914314Seric /* 2924314Seric ** Process the LHS 2934314Seric ** Find the final colon, and parse the address. 2944314Seric ** It should resolve to a local name -- this will 2954314Seric ** be checked later (we want to optionally do 2964314Seric ** parsing of the RHS first to maximize error 2974314Seric ** detection). 2984314Seric */ 2994314Seric 3004098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 3014097Seric continue; 3024098Seric if (*p == '\0' || *p == '\n') 3034098Seric { 3044098Seric syntaxerr: 3054098Seric syserr("aliases: %d: missing colon", lineno); 3064097Seric continue; 3074098Seric } 3084098Seric *p++ = '\0'; 3094098Seric if (parse(line, &al, 1) == NULL) 3104098Seric { 3114098Seric *--p = ':'; 3124098Seric goto syntaxerr; 3134098Seric } 3144314Seric 3154314Seric /* 3164314Seric ** Process the RHS. 3174314Seric ** 'al' is the internal form of the LHS address. 3184314Seric ** 'p' points to the text of the RHS. 3194314Seric */ 3204314Seric 3214098Seric rhs = p; 3224098Seric for (;;) 3234098Seric { 3244098Seric register char c; 3251515Seric 3264157Seric if (init) 3274098Seric { 3284157Seric /* do parsing & compression of addresses */ 3294098Seric c = *p; 3304157Seric while (c != '\0') 3314098Seric { 3324157Seric p2 = p; 3334157Seric while (*p != '\n' && *p != ',' && *p != '\0') 3344157Seric p++; 3354157Seric c = *p; 3364157Seric *p++ = '\0'; 337*4322Seric if (c == '\n') 338*4322Seric c = '\0'; 3394157Seric if (*p2 == '\0') 3404157Seric { 3414157Seric p[-1] = c; 3424157Seric continue; 3434157Seric } 3444314Seric (void) parse(p2, &bl, -1); 3454098Seric p[-1] = c; 3464157Seric while (isspace(*p)) 3474157Seric p++; 3484098Seric } 3494098Seric } 3504157Seric else 3514157Seric p = &p[strlen(p)]; 3521515Seric 3534098Seric /* see if there should be a continuation line */ 3544106Seric c = fgetc(af); 3554106Seric if (!feof(af)) 3564314Seric (void) ungetc(c, af); 3574106Seric if (c != ' ' && c != '\t') 3584098Seric break; 3594098Seric 3604098Seric /* read continuation line */ 3614098Seric p--; 3624098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 3634098Seric break; 3644098Seric lineno++; 3654098Seric } 3664193Seric if (al.q_mailer != MN_LOCAL) 3674098Seric { 3684098Seric syserr("aliases: %d: cannot alias non-local names", lineno); 3694098Seric continue; 3704098Seric } 3714314Seric 3724314Seric /* 3734314Seric ** Insert alias into symbol table or DBM file 3744314Seric */ 3754314Seric 376*4322Seric lhssize = strlen(al.q_user) + 1; 377*4322Seric rhssize = strlen(rhs) + 1; 378*4322Seric 3794157Seric # ifdef DBM 3804157Seric if (init) 3814157Seric { 3824157Seric DATUM key, content; 3834157Seric 384*4322Seric key.dsize = lhssize; 3854157Seric key.dptr = al.q_user; 386*4322Seric content.dsize = rhssize; 3874157Seric content.dptr = rhs; 3884157Seric store(key, content); 3894157Seric } 3904157Seric else 3914157Seric # endif DBM 3924157Seric { 3934157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 3944157Seric s->s_alias = newstr(rhs); 3954157Seric } 396*4322Seric 397*4322Seric /* statistics */ 398*4322Seric naliases++; 399*4322Seric bytes += lhssize + rhssize; 400*4322Seric if (rhssize > longest) 401*4322Seric longest = rhssize; 4021515Seric } 4034098Seric (void) fclose(af); 404*4322Seric To = NULL; 405*4322Seric if (Verbose) 406*4322Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 407*4322Seric naliases, longest, bytes); 408292Seric } 409292Seric /* 410292Seric ** FORWARD -- Try to forward mail 411292Seric ** 412292Seric ** This is similar but not identical to aliasing. 413292Seric ** 414292Seric ** Parameters: 4154314Seric ** user -- the name of the user who's mail we would like 4164314Seric ** to forward to. It must have been verified -- 4174314Seric ** i.e., the q_home field must have been filled 4184314Seric ** in. 419292Seric ** 420292Seric ** Returns: 4214098Seric ** none. 422292Seric ** 423292Seric ** Side Effects: 4243185Seric ** New names are added to send queues. 4254098Seric ** Sets the QDONTSEND bit in addresses that are forwarded. 426292Seric */ 427292Seric 428292Seric forward(user) 4292966Seric ADDRESS *user; 430292Seric { 4314078Seric char buf[60]; 4324212Seric struct stat stbuf; 4334069Seric 4344098Seric # ifdef DEBUG 4354098Seric if (Debug) 4364098Seric printf("forward(%s)\n", user->q_paddr); 4374098Seric # endif DEBUG 4384098Seric 4394193Seric if (user->q_mailer != MN_LOCAL || bitset(QBADADDR, user->q_flags)) 4404098Seric return; 4414314Seric # ifdef DEBUG 4424314Seric if (user->q_home == NULL) 4434314Seric syserr("forward: no home"); 4444314Seric # endif DEBUG 4454069Seric 4464069Seric /* good address -- look for .forward file in home */ 4474104Seric define('z', user->q_home); 4484081Seric (void) expand("$z/.forward", buf, &buf[sizeof buf - 1]); 4494212Seric if (stat(buf, &stbuf) < 0 || stbuf.st_uid != user->q_uid || 4504212Seric !bitset(S_IREAD, stbuf.st_mode)) 4514098Seric return; 4524069Seric 4534069Seric /* we do have an address to forward to -- do it */ 4544098Seric user->q_flags |= QDONTSEND; 4554177Seric include(buf, "forwarding"); 456292Seric } 457