122694Sdist /* 235073Sbostic * Copyright (c) 1983 Eric P. Allman 333728Sbostic * Copyright (c) 1988 Regents of the University of California. 433728Sbostic * All rights reserved. 533728Sbostic * 633728Sbostic * Redistribution and use in source and binary forms are permitted 735073Sbostic * provided that the above copyright notice and this paragraph are 835073Sbostic * duplicated in all such forms and that any documentation, 935073Sbostic * advertising materials, and other materials related to such 1035073Sbostic * distribution and use acknowledge that the software was developed 1135073Sbostic * by the University of California, Berkeley. The name of the 1235073Sbostic * University may not be used to endorse or promote products derived 1335073Sbostic * from this software without specific prior written permission. 1435073Sbostic * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR 1535073Sbostic * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED 1635073Sbostic * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE. 1733728Sbostic */ 1822694Sdist 1933728Sbostic #ifndef lint 2033728Sbostic #ifdef DBM 21*40970Sbostic static char sccsid[] = "@(#)alias.c 5.20 (Berkeley) 04/18/90 (with DBM)"; 2233728Sbostic #else 23*40970Sbostic static char sccsid[] = "@(#)alias.c 5.20 (Berkeley) 04/18/90 (without DBM)"; 2433728Sbostic #endif 2533728Sbostic #endif /* not lint */ 2633728Sbostic 274212Seric # include <sys/types.h> 284212Seric # include <sys/stat.h> 298437Seric # include <signal.h> 3019784Seric # include <errno.h> 313309Seric # include "sendmail.h" 3219784Seric # include <sys/file.h> 3336928Sbostic # include <pwd.h> 34292Seric 35292Seric /* 36292Seric ** ALIAS -- Compute aliases. 37292Seric ** 389368Seric ** Scans the alias file for an alias for the given address. 399368Seric ** If found, it arranges to deliver to the alias list instead. 409368Seric ** Uses libdbm database if -DDBM. 41292Seric ** 42292Seric ** Parameters: 434097Seric ** a -- address to alias. 444999Seric ** sendq -- a pointer to the head of the send queue 454999Seric ** to put the aliases in. 46292Seric ** 47292Seric ** Returns: 48292Seric ** none 49292Seric ** 50292Seric ** Side Effects: 513185Seric ** Aliases found are expanded. 52292Seric ** 53292Seric ** Notes: 54292Seric ** If NoAlias (the "-n" flag) is set, no aliasing is 55292Seric ** done. 56292Seric ** 57292Seric ** Deficiencies: 58292Seric ** It should complain about names that are aliased to 59292Seric ** nothing. 60292Seric */ 61292Seric 62292Seric 631503Smark #ifdef DBM 642966Seric typedef struct 652966Seric { 662966Seric char *dptr; 674157Seric int dsize; 684157Seric } DATUM; 694157Seric extern DATUM fetch(); 701503Smark #endif DBM 71292Seric 724999Seric alias(a, sendq) 734097Seric register ADDRESS *a; 744999Seric ADDRESS **sendq; 75292Seric { 764081Seric register char *p; 775701Seric extern char *aliaslookup(); 78292Seric 797671Seric if (tTd(27, 1)) 804098Seric printf("alias(%s)\n", a->q_paddr); 81292Seric 824098Seric /* don't realias already aliased names */ 834098Seric if (bitset(QDONTSEND, a->q_flags)) 844098Seric return; 854098Seric 866898Seric CurEnv->e_to = a->q_paddr; 874098Seric 884314Seric /* 894314Seric ** Look up this name 904314Seric */ 914314Seric 9224944Seric if (NoAlias) 9324944Seric p = NULL; 9424944Seric else 9524944Seric p = aliaslookup(a->q_user); 964098Seric if (p == NULL) 974098Seric return; 98292Seric 99292Seric /* 1004098Seric ** Match on Alias. 1014098Seric ** Deliver to the target list. 1021515Seric */ 1031515Seric 1047671Seric if (tTd(27, 1)) 1054098Seric printf("%s (%s, %s) aliased to %s\n", 1064098Seric a->q_paddr, a->q_host, a->q_user, p); 1077051Seric message(Arpa_Info, "aliased to %s", p); 1084098Seric AliasLevel++; 1099614Seric sendtolist(p, a, sendq); 1104098Seric AliasLevel--; 1114098Seric } 1124098Seric /* 1135701Seric ** ALIASLOOKUP -- look up a name in the alias file. 1145701Seric ** 1155701Seric ** Parameters: 1165701Seric ** name -- the name to look up. 1175701Seric ** 1185701Seric ** Returns: 1195701Seric ** the value of name. 1205701Seric ** NULL if unknown. 1215701Seric ** 1225701Seric ** Side Effects: 1235701Seric ** none. 1245701Seric ** 1255701Seric ** Warnings: 1265701Seric ** The return value will be trashed across calls. 1275701Seric */ 1285701Seric 1295701Seric char * 1305701Seric aliaslookup(name) 1315701Seric char *name; 1325701Seric { 1335701Seric # ifdef DBM 1345701Seric DATUM rhs, lhs; 1355701Seric 1365701Seric /* create a key for fetch */ 1375701Seric lhs.dptr = name; 1385701Seric lhs.dsize = strlen(name) + 1; 1395701Seric rhs = fetch(lhs); 1405701Seric return (rhs.dptr); 1415701Seric # else DBM 1425701Seric register STAB *s; 1435701Seric 1445701Seric s = stab(name, ST_ALIAS, ST_FIND); 1455701Seric if (s == NULL) 1465701Seric return (NULL); 1475701Seric return (s->s_alias); 1485701Seric # endif DBM 1495701Seric } 1505701Seric /* 1514098Seric ** INITALIASES -- initialize for aliasing 1524098Seric ** 1534098Seric ** Very different depending on whether we are running DBM or not. 1544098Seric ** 1554098Seric ** Parameters: 1564098Seric ** aliasfile -- location of aliases. 1574157Seric ** init -- if set and if DBM, initialize the DBM files. 1584098Seric ** 1594098Seric ** Returns: 1604098Seric ** none. 1614098Seric ** 1624098Seric ** Side Effects: 1634098Seric ** initializes aliases: 1644098Seric ** if DBM: opens the database. 1654098Seric ** if ~DBM: reads the aliases into the symbol table. 1664098Seric */ 1674098Seric 16840559Sbostic # define DBMMODE 0644 1694157Seric 1704157Seric initaliases(aliasfile, init) 1714098Seric char *aliasfile; 1724157Seric bool init; 1734098Seric { 1749368Seric #ifdef DBM 1758437Seric int atcnt; 17625522Seric time_t modtime; 17725522Seric bool automatic = FALSE; 1784322Seric char buf[MAXNAME]; 1799368Seric #endif DBM 1809368Seric struct stat stb; 18127176Seric static bool initialized = FALSE; 1824322Seric 18327176Seric if (initialized) 18427176Seric return; 18527176Seric initialized = TRUE; 18627176Seric 18717984Seric if (aliasfile == NULL || stat(aliasfile, &stb) < 0) 1888437Seric { 18925522Seric if (aliasfile != NULL && init) 19025522Seric syserr("Cannot open %s", aliasfile); 1918437Seric NoAlias = TRUE; 19211937Seric errno = 0; 1938437Seric return; 1948437Seric } 1958437Seric 1968928Seric # ifdef DBM 1974322Seric /* 1988437Seric ** Check to see that the alias file is complete. 1998437Seric ** If not, we will assume that someone died, and it is up 2008437Seric ** to us to rebuild it. 2018437Seric */ 2028437Seric 20325689Seric if (!init) 20425689Seric dbminit(aliasfile); 20517471Seric atcnt = SafeAlias * 2; 20617471Seric if (atcnt > 0) 20717471Seric { 20817471Seric while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL) 20925689Seric { 21025689Seric /* 21125689Seric ** Reinitialize alias file in case the new 21225689Seric ** one is mv'ed in instead of cp'ed in. 21325689Seric ** 21425689Seric ** Only works with new DBM -- old one will 21525689Seric ** just consume file descriptors forever. 21625689Seric ** If you have a dbmclose() it can be 21725689Seric ** added before the sleep(30). 21825689Seric */ 21925689Seric 22017471Seric sleep(30); 22125689Seric # ifdef NDBM 22225689Seric dbminit(aliasfile); 22325689Seric # endif NDBM 22425689Seric } 22517471Seric } 22617471Seric else 22717471Seric atcnt = 1; 2288437Seric 2298437Seric /* 2304322Seric ** See if the DBM version of the file is out of date with 2314322Seric ** the text version. If so, go into 'init' mode automatically. 23240559Sbostic ** This only happens if our effective userid owns the DBM. 23340559Sbostic ** Note the unpalatable hack to see if the stat succeeded. 2344322Seric */ 2354322Seric 2364322Seric modtime = stb.st_mtime; 2374322Seric (void) strcpy(buf, aliasfile); 2384322Seric (void) strcat(buf, ".pag"); 2394322Seric stb.st_ino = 0; 24019039Seric if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0)) 2414322Seric { 24211937Seric errno = 0; 24340559Sbostic if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid()) 2444322Seric { 2454322Seric init = TRUE; 24625522Seric automatic = TRUE; 2477051Seric message(Arpa_Info, "rebuilding alias database"); 24824944Seric #ifdef LOG 24924944Seric if (LogLevel >= 7) 25024944Seric syslog(LOG_INFO, "rebuilding alias database"); 25124944Seric #endif LOG 2524322Seric } 2534322Seric else 2544322Seric { 25519039Seric #ifdef LOG 25624944Seric if (LogLevel >= 7) 25724944Seric syslog(LOG_INFO, "alias database out of date"); 25819039Seric #endif LOG 2594322Seric message(Arpa_Info, "Warning: alias database out of date"); 2604322Seric } 2614322Seric } 2624322Seric 2634322Seric 2644322Seric /* 2658437Seric ** If necessary, load the DBM file. 2664322Seric ** If running without DBM, load the symbol table. 2674322Seric */ 2684322Seric 2694157Seric if (init) 2708437Seric { 27125522Seric #ifdef LOG 27225522Seric if (LogLevel >= 6) 27325522Seric { 27425522Seric extern char *username(); 27525522Seric 27625522Seric syslog(LOG_NOTICE, "alias database %srebuilt by %s", 27725522Seric automatic ? "auto" : "", username()); 27825522Seric } 27925522Seric #endif LOG 2804157Seric readaliases(aliasfile, TRUE); 2818437Seric } 2824098Seric # else DBM 2834157Seric readaliases(aliasfile, init); 2844157Seric # endif DBM 2854157Seric } 2864157Seric /* 2874157Seric ** READALIASES -- read and process the alias file. 2884157Seric ** 2894157Seric ** This routine implements the part of initaliases that occurs 2904157Seric ** when we are not going to use the DBM stuff. 2914157Seric ** 2924157Seric ** Parameters: 2934157Seric ** aliasfile -- the pathname of the alias file master. 2944157Seric ** init -- if set, initialize the DBM stuff. 2954157Seric ** 2964157Seric ** Returns: 2974157Seric ** none. 2984157Seric ** 2994157Seric ** Side Effects: 3004157Seric ** Reads aliasfile into the symbol table. 3014157Seric ** Optionally, builds the .dir & .pag files. 3024157Seric */ 3034157Seric 3044157Seric static 3054157Seric readaliases(aliasfile, init) 3064157Seric char *aliasfile; 3074157Seric bool init; 3084157Seric { 3094098Seric register char *p; 3104098Seric char *rhs; 3114098Seric bool skipping; 3129368Seric int naliases, bytes, longest; 3139368Seric FILE *af; 314*40970Sbostic void (*oldsigint)(); 3154098Seric ADDRESS al, bl; 3164106Seric register STAB *s; 3179368Seric char line[BUFSIZ]; 3184098Seric 3194098Seric if ((af = fopen(aliasfile, "r")) == NULL) 3201515Seric { 3217671Seric if (tTd(27, 1)) 3224106Seric printf("Can't open %s\n", aliasfile); 3234098Seric errno = 0; 3244098Seric NoAlias++; 3254098Seric return; 3264098Seric } 3274314Seric 32819784Seric # ifdef DBM 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 DBM 34419784Seric 3454314Seric /* 34619784Seric ** If initializing, create the new DBM files. 34719784Seric */ 34819784Seric 34919784Seric if (init) 35019784Seric { 35119784Seric oldsigint = signal(SIGINT, SIG_IGN); 35219784Seric (void) strcpy(line, aliasfile); 35319784Seric (void) strcat(line, ".dir"); 35419784Seric if (close(creat(line, DBMMODE)) < 0) 35519784Seric { 35619784Seric syserr("cannot make %s", line); 35719784Seric (void) signal(SIGINT, oldsigint); 35819784Seric return; 35919784Seric } 36019784Seric (void) strcpy(line, aliasfile); 36119784Seric (void) strcat(line, ".pag"); 36219784Seric if (close(creat(line, DBMMODE)) < 0) 36319784Seric { 36419784Seric syserr("cannot make %s", line); 36519784Seric (void) signal(SIGINT, oldsigint); 36619784Seric return; 36719784Seric } 36826501Seric dbminit(aliasfile); 36919784Seric } 37019784Seric 37119784Seric /* 3724314Seric ** Read and interpret lines 3734314Seric */ 3744314Seric 3759368Seric FileName = aliasfile; 3769368Seric LineNumber = 0; 3774322Seric naliases = bytes = longest = 0; 3784098Seric skipping = FALSE; 3794098Seric while (fgets(line, sizeof (line), af) != NULL) 3804098Seric { 3814322Seric int lhssize, rhssize; 3824322Seric 3839368Seric LineNumber++; 38425278Seric p = index(line, '\n'); 38525278Seric if (p != NULL) 38625278Seric *p = '\0'; 3874098Seric switch (line[0]) 3884098Seric { 3894098Seric case '#': 3904098Seric case '\0': 3914098Seric skipping = FALSE; 3924098Seric continue; 3934065Seric 3944098Seric case ' ': 3954098Seric case '\t': 3964098Seric if (!skipping) 3979368Seric syserr("Non-continuation line starts with space"); 3984098Seric skipping = TRUE; 3994097Seric continue; 4004098Seric } 4014098Seric skipping = FALSE; 4021874Seric 4034314Seric /* 4044314Seric ** Process the LHS 4054314Seric ** Find the final colon, and parse the address. 40616898Seric ** It should resolve to a local name -- this will 40716898Seric ** be checked later (we want to optionally do 40816898Seric ** parsing of the RHS first to maximize error 40916898Seric ** detection). 4104314Seric */ 4114314Seric 4124098Seric for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++) 4134097Seric continue; 41416898Seric if (*p++ != ':') 4154098Seric { 4169368Seric syserr("missing colon"); 4174097Seric continue; 4184098Seric } 41916898Seric if (parseaddr(line, &al, 1, ':') == NULL) 4204098Seric { 42116898Seric syserr("illegal alias name"); 42216898Seric continue; 4234098Seric } 42416898Seric loweraddr(&al); 4254314Seric 4264314Seric /* 4274314Seric ** Process the RHS. 4284314Seric ** 'al' is the internal form of the LHS address. 4294314Seric ** 'p' points to the text of the RHS. 4304314Seric */ 4314314Seric 4324098Seric rhs = p; 4334098Seric for (;;) 4344098Seric { 4354098Seric register char c; 4361515Seric 43725821Seric if (init && CheckAliases) 4384098Seric { 4394157Seric /* do parsing & compression of addresses */ 44025278Seric while (*p != '\0') 4414098Seric { 44225278Seric extern char *DelimChar; 44325278Seric 44425278Seric while (isspace(*p) || *p == ',') 4454157Seric p++; 44625278Seric if (*p == '\0') 44725278Seric break; 44825278Seric if (parseaddr(p, &bl, -1, ',') == NULL) 44925278Seric usrerr("%s... bad address", p); 45025278Seric p = DelimChar; 4514098Seric } 4524098Seric } 4534157Seric else 45415769Seric { 45516898Seric p = &p[strlen(p)]; 45616898Seric if (p[-1] == '\n') 45716898Seric *--p = '\0'; 45815769Seric } 4591515Seric 4604098Seric /* see if there should be a continuation line */ 4614106Seric c = fgetc(af); 4624106Seric if (!feof(af)) 4634314Seric (void) ungetc(c, af); 4644106Seric if (c != ' ' && c != '\t') 4654098Seric break; 4664098Seric 4674098Seric /* read continuation line */ 4684098Seric if (fgets(p, sizeof line - (p - line), af) == NULL) 4694098Seric break; 4709368Seric LineNumber++; 4714098Seric } 47216898Seric if (al.q_mailer != LocalMailer) 47316898Seric { 47416898Seric syserr("cannot alias non-local names"); 47516898Seric continue; 47616898Seric } 4774314Seric 4784314Seric /* 4794314Seric ** Insert alias into symbol table or DBM file 4804314Seric */ 4814314Seric 48216898Seric lhssize = strlen(al.q_user) + 1; 4834322Seric rhssize = strlen(rhs) + 1; 4844322Seric 4854157Seric # ifdef DBM 4864157Seric if (init) 4874157Seric { 4884157Seric DATUM key, content; 4894157Seric 4904322Seric key.dsize = lhssize; 4914157Seric key.dptr = al.q_user; 4924322Seric content.dsize = rhssize; 4934157Seric content.dptr = rhs; 4944157Seric store(key, content); 4954157Seric } 4964157Seric else 4974157Seric # endif DBM 4984157Seric { 4994157Seric s = stab(al.q_user, ST_ALIAS, ST_ENTER); 5004157Seric s->s_alias = newstr(rhs); 5014157Seric } 5024322Seric 5034322Seric /* statistics */ 5044322Seric naliases++; 5054322Seric bytes += lhssize + rhssize; 5064322Seric if (rhssize > longest) 5074322Seric longest = rhssize; 5081515Seric } 50919784Seric 51019784Seric # ifdef DBM 51119784Seric if (init) 51219784Seric { 51319784Seric /* add the distinquished alias "@" */ 51419784Seric DATUM key; 51519784Seric 51619784Seric key.dsize = 2; 51719784Seric key.dptr = "@"; 51819784Seric store(key, key); 51919784Seric 52019784Seric /* restore the old signal */ 52119784Seric (void) signal(SIGINT, oldsigint); 52219784Seric } 52319784Seric # endif DBM 52419784Seric 52519784Seric /* closing the alias file drops the lock */ 5264098Seric (void) fclose(af); 5276898Seric CurEnv->e_to = NULL; 5289368Seric FileName = NULL; 5297051Seric message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total", 5304322Seric naliases, longest, bytes); 53124944Seric # ifdef LOG 53224944Seric if (LogLevel >= 8) 53324944Seric syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total", 53424944Seric naliases, longest, bytes); 53524944Seric # endif LOG 536292Seric } 537292Seric /* 538292Seric ** FORWARD -- Try to forward mail 539292Seric ** 540292Seric ** This is similar but not identical to aliasing. 541292Seric ** 542292Seric ** Parameters: 5434314Seric ** user -- the name of the user who's mail we would like 5444314Seric ** to forward to. It must have been verified -- 5454314Seric ** i.e., the q_home field must have been filled 5464314Seric ** in. 5474999Seric ** sendq -- a pointer to the head of the send queue to 5484999Seric ** put this user's aliases in. 549292Seric ** 550292Seric ** Returns: 5514098Seric ** none. 552292Seric ** 553292Seric ** Side Effects: 5543185Seric ** New names are added to send queues. 555292Seric */ 556292Seric 5574999Seric forward(user, sendq) 5582966Seric ADDRESS *user; 5594999Seric ADDRESS **sendq; 560292Seric { 5614078Seric char buf[60]; 5624536Seric extern bool safefile(); 5634069Seric 5647671Seric if (tTd(27, 1)) 5654098Seric printf("forward(%s)\n", user->q_paddr); 5664098Seric 5674594Seric if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags)) 5684098Seric return; 5694314Seric if (user->q_home == NULL) 5704314Seric syserr("forward: no home"); 5714069Seric 5724069Seric /* good address -- look for .forward file in home */ 5739368Seric define('z', user->q_home, CurEnv); 57416154Seric expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv); 5754536Seric if (!safefile(buf, user->q_uid, S_IREAD)) 5764098Seric return; 5774069Seric 5784069Seric /* we do have an address to forward to -- do it */ 5794999Seric include(buf, "forwarding", user, sendq); 580292Seric } 581