xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 46928)
122694Sdist /*
235073Sbostic  * Copyright (c) 1983 Eric P. Allman
333728Sbostic  * Copyright (c) 1988 Regents of the University of California.
433728Sbostic  * All rights reserved.
533728Sbostic  *
642824Sbostic  * %sccs.include.redist.c%
733728Sbostic  */
822694Sdist 
933728Sbostic #ifndef lint
1033728Sbostic #ifdef DBM
11*46928Sbostic static char sccsid[] = "@(#)alias.c	5.22 (Berkeley) 03/02/91 (with DBM)";
1233728Sbostic #else
13*46928Sbostic static char sccsid[] = "@(#)alias.c	5.22 (Berkeley) 03/02/91 (without DBM)";
1433728Sbostic #endif
1533728Sbostic #endif /* not lint */
1633728Sbostic 
174212Seric # include <sys/types.h>
184212Seric # include <sys/stat.h>
198437Seric # include <signal.h>
2019784Seric # include <errno.h>
213309Seric # include "sendmail.h"
2219784Seric # include <sys/file.h>
2336928Sbostic # include <pwd.h>
24292Seric 
25292Seric /*
26292Seric **  ALIAS -- Compute aliases.
27292Seric **
289368Seric **	Scans the alias file for an alias for the given address.
299368Seric **	If found, it arranges to deliver to the alias list instead.
309368Seric **	Uses libdbm database if -DDBM.
31292Seric **
32292Seric **	Parameters:
334097Seric **		a -- address to alias.
344999Seric **		sendq -- a pointer to the head of the send queue
354999Seric **			to put the aliases in.
36292Seric **
37292Seric **	Returns:
38292Seric **		none
39292Seric **
40292Seric **	Side Effects:
413185Seric **		Aliases found are expanded.
42292Seric **
43292Seric **	Notes:
44292Seric **		If NoAlias (the "-n" flag) is set, no aliasing is
45292Seric **			done.
46292Seric **
47292Seric **	Deficiencies:
48292Seric **		It should complain about names that are aliased to
49292Seric **			nothing.
50292Seric */
51292Seric 
52292Seric 
531503Smark #ifdef DBM
542966Seric typedef struct
552966Seric {
562966Seric 	char	*dptr;
574157Seric 	int	dsize;
584157Seric } DATUM;
594157Seric extern DATUM fetch();
601503Smark #endif DBM
61292Seric 
624999Seric alias(a, sendq)
634097Seric 	register ADDRESS *a;
644999Seric 	ADDRESS **sendq;
65292Seric {
664081Seric 	register char *p;
675701Seric 	extern char *aliaslookup();
68292Seric 
697671Seric 	if (tTd(27, 1))
704098Seric 		printf("alias(%s)\n", a->q_paddr);
71292Seric 
724098Seric 	/* don't realias already aliased names */
734098Seric 	if (bitset(QDONTSEND, a->q_flags))
744098Seric 		return;
754098Seric 
766898Seric 	CurEnv->e_to = a->q_paddr;
774098Seric 
784314Seric 	/*
794314Seric 	**  Look up this name
804314Seric 	*/
814314Seric 
8224944Seric 	if (NoAlias)
8324944Seric 		p = NULL;
8424944Seric 	else
8524944Seric 		p = aliaslookup(a->q_user);
864098Seric 	if (p == NULL)
874098Seric 		return;
88292Seric 
89292Seric 	/*
904098Seric 	**  Match on Alias.
914098Seric 	**	Deliver to the target list.
921515Seric 	*/
931515Seric 
947671Seric 	if (tTd(27, 1))
954098Seric 		printf("%s (%s, %s) aliased to %s\n",
964098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
977051Seric 	message(Arpa_Info, "aliased to %s", p);
984098Seric 	AliasLevel++;
999614Seric 	sendtolist(p, a, sendq);
1004098Seric 	AliasLevel--;
1014098Seric }
1024098Seric /*
1035701Seric **  ALIASLOOKUP -- look up a name in the alias file.
1045701Seric **
1055701Seric **	Parameters:
1065701Seric **		name -- the name to look up.
1075701Seric **
1085701Seric **	Returns:
1095701Seric **		the value of name.
1105701Seric **		NULL if unknown.
1115701Seric **
1125701Seric **	Side Effects:
1135701Seric **		none.
1145701Seric **
1155701Seric **	Warnings:
1165701Seric **		The return value will be trashed across calls.
1175701Seric */
1185701Seric 
1195701Seric char *
1205701Seric aliaslookup(name)
1215701Seric 	char *name;
1225701Seric {
1235701Seric # ifdef DBM
1245701Seric 	DATUM rhs, lhs;
1255701Seric 
1265701Seric 	/* create a key for fetch */
1275701Seric 	lhs.dptr = name;
1285701Seric 	lhs.dsize = strlen(name) + 1;
1295701Seric 	rhs = fetch(lhs);
1305701Seric 	return (rhs.dptr);
1315701Seric # else DBM
1325701Seric 	register STAB *s;
1335701Seric 
1345701Seric 	s = stab(name, ST_ALIAS, ST_FIND);
1355701Seric 	if (s == NULL)
1365701Seric 		return (NULL);
1375701Seric 	return (s->s_alias);
1385701Seric # endif DBM
1395701Seric }
1405701Seric /*
1414098Seric **  INITALIASES -- initialize for aliasing
1424098Seric **
1434098Seric **	Very different depending on whether we are running DBM or not.
1444098Seric **
1454098Seric **	Parameters:
1464098Seric **		aliasfile -- location of aliases.
1474157Seric **		init -- if set and if DBM, initialize the DBM files.
1484098Seric **
1494098Seric **	Returns:
1504098Seric **		none.
1514098Seric **
1524098Seric **	Side Effects:
1534098Seric **		initializes aliases:
1544098Seric **		if DBM:  opens the database.
1554098Seric **		if ~DBM: reads the aliases into the symbol table.
1564098Seric */
1574098Seric 
15840559Sbostic # define DBMMODE	0644
1594157Seric 
1604157Seric initaliases(aliasfile, init)
1614098Seric 	char *aliasfile;
1624157Seric 	bool init;
1634098Seric {
1649368Seric #ifdef DBM
1658437Seric 	int atcnt;
16625522Seric 	time_t modtime;
16725522Seric 	bool automatic = FALSE;
1684322Seric 	char buf[MAXNAME];
1699368Seric #endif DBM
1709368Seric 	struct stat stb;
17127176Seric 	static bool initialized = FALSE;
172*46928Sbostic 	static int readaliases();
1734322Seric 
17427176Seric 	if (initialized)
17527176Seric 		return;
17627176Seric 	initialized = TRUE;
17727176Seric 
17817984Seric 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
1798437Seric 	{
18025522Seric 		if (aliasfile != NULL && init)
18125522Seric 			syserr("Cannot open %s", aliasfile);
1828437Seric 		NoAlias = TRUE;
18311937Seric 		errno = 0;
1848437Seric 		return;
1858437Seric 	}
1868437Seric 
1878928Seric # ifdef DBM
1884322Seric 	/*
1898437Seric 	**  Check to see that the alias file is complete.
1908437Seric 	**	If not, we will assume that someone died, and it is up
1918437Seric 	**	to us to rebuild it.
1928437Seric 	*/
1938437Seric 
19425689Seric 	if (!init)
19525689Seric 		dbminit(aliasfile);
19617471Seric 	atcnt = SafeAlias * 2;
19717471Seric 	if (atcnt > 0)
19817471Seric 	{
19917471Seric 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
20025689Seric 		{
20125689Seric 			/*
20225689Seric 			**  Reinitialize alias file in case the new
20325689Seric 			**  one is mv'ed in instead of cp'ed in.
20425689Seric 			**
20525689Seric 			**	Only works with new DBM -- old one will
20625689Seric 			**	just consume file descriptors forever.
20725689Seric 			**	If you have a dbmclose() it can be
20825689Seric 			**	added before the sleep(30).
20925689Seric 			*/
21025689Seric 
21117471Seric 			sleep(30);
21225689Seric # ifdef NDBM
21325689Seric 			dbminit(aliasfile);
21425689Seric # endif NDBM
21525689Seric 		}
21617471Seric 	}
21717471Seric 	else
21817471Seric 		atcnt = 1;
2198437Seric 
2208437Seric 	/*
2214322Seric 	**  See if the DBM version of the file is out of date with
2224322Seric 	**  the text version.  If so, go into 'init' mode automatically.
22340559Sbostic 	**	This only happens if our effective userid owns the DBM.
22440559Sbostic 	**	Note the unpalatable hack to see if the stat succeeded.
2254322Seric 	*/
2264322Seric 
2274322Seric 	modtime = stb.st_mtime;
2284322Seric 	(void) strcpy(buf, aliasfile);
2294322Seric 	(void) strcat(buf, ".pag");
2304322Seric 	stb.st_ino = 0;
23119039Seric 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
2324322Seric 	{
23311937Seric 		errno = 0;
23440559Sbostic 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
2354322Seric 		{
2364322Seric 			init = TRUE;
23725522Seric 			automatic = TRUE;
2387051Seric 			message(Arpa_Info, "rebuilding alias database");
23924944Seric #ifdef LOG
24024944Seric 			if (LogLevel >= 7)
24124944Seric 				syslog(LOG_INFO, "rebuilding alias database");
24224944Seric #endif LOG
2434322Seric 		}
2444322Seric 		else
2454322Seric 		{
24619039Seric #ifdef LOG
24724944Seric 			if (LogLevel >= 7)
24824944Seric 				syslog(LOG_INFO, "alias database out of date");
24919039Seric #endif LOG
2504322Seric 			message(Arpa_Info, "Warning: alias database out of date");
2514322Seric 		}
2524322Seric 	}
2534322Seric 
2544322Seric 
2554322Seric 	/*
2568437Seric 	**  If necessary, load the DBM file.
2574322Seric 	**	If running without DBM, load the symbol table.
2584322Seric 	*/
2594322Seric 
2604157Seric 	if (init)
2618437Seric 	{
26225522Seric #ifdef LOG
26325522Seric 		if (LogLevel >= 6)
26425522Seric 		{
26525522Seric 			extern char *username();
26625522Seric 
26725522Seric 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
26825522Seric 				automatic ? "auto" : "", username());
26925522Seric 		}
27025522Seric #endif LOG
2714157Seric 		readaliases(aliasfile, TRUE);
2728437Seric 	}
2734098Seric # else DBM
2744157Seric 	readaliases(aliasfile, init);
2754157Seric # endif DBM
2764157Seric }
2774157Seric /*
2784157Seric **  READALIASES -- read and process the alias file.
2794157Seric **
2804157Seric **	This routine implements the part of initaliases that occurs
2814157Seric **	when we are not going to use the DBM stuff.
2824157Seric **
2834157Seric **	Parameters:
2844157Seric **		aliasfile -- the pathname of the alias file master.
2854157Seric **		init -- if set, initialize the DBM stuff.
2864157Seric **
2874157Seric **	Returns:
2884157Seric **		none.
2894157Seric **
2904157Seric **	Side Effects:
2914157Seric **		Reads aliasfile into the symbol table.
2924157Seric **		Optionally, builds the .dir & .pag files.
2934157Seric */
2944157Seric 
2954157Seric static
2964157Seric readaliases(aliasfile, init)
2974157Seric 	char *aliasfile;
2984157Seric 	bool init;
2994157Seric {
3004098Seric 	register char *p;
3014098Seric 	char *rhs;
3024098Seric 	bool skipping;
3039368Seric 	int naliases, bytes, longest;
3049368Seric 	FILE *af;
30540970Sbostic 	void (*oldsigint)();
3064098Seric 	ADDRESS al, bl;
3074106Seric 	register STAB *s;
3089368Seric 	char line[BUFSIZ];
3094098Seric 
3104098Seric 	if ((af = fopen(aliasfile, "r")) == NULL)
3111515Seric 	{
3127671Seric 		if (tTd(27, 1))
3134106Seric 			printf("Can't open %s\n", aliasfile);
3144098Seric 		errno = 0;
3154098Seric 		NoAlias++;
3164098Seric 		return;
3174098Seric 	}
3184314Seric 
31919784Seric # ifdef DBM
32019784Seric 	/* see if someone else is rebuilding the alias file already */
32119784Seric 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
32219784Seric 	{
32319784Seric 		/* yes, they are -- wait until done and then return */
32419784Seric 		message(Arpa_Info, "Alias file is already being rebuilt");
32519784Seric 		if (OpMode != MD_INITALIAS)
32619784Seric 		{
32719784Seric 			/* wait for other rebuild to complete */
32819784Seric 			(void) flock(fileno(af), LOCK_EX);
32919784Seric 		}
33023108Seric 		(void) fclose(af);
33119784Seric 		errno = 0;
33219784Seric 		return;
33319784Seric 	}
33419784Seric # endif DBM
33519784Seric 
3364314Seric 	/*
33719784Seric 	**  If initializing, create the new DBM files.
33819784Seric 	*/
33919784Seric 
34019784Seric 	if (init)
34119784Seric 	{
34219784Seric 		oldsigint = signal(SIGINT, SIG_IGN);
34319784Seric 		(void) strcpy(line, aliasfile);
34419784Seric 		(void) strcat(line, ".dir");
34519784Seric 		if (close(creat(line, DBMMODE)) < 0)
34619784Seric 		{
34719784Seric 			syserr("cannot make %s", line);
34819784Seric 			(void) signal(SIGINT, oldsigint);
34919784Seric 			return;
35019784Seric 		}
35119784Seric 		(void) strcpy(line, aliasfile);
35219784Seric 		(void) strcat(line, ".pag");
35319784Seric 		if (close(creat(line, DBMMODE)) < 0)
35419784Seric 		{
35519784Seric 			syserr("cannot make %s", line);
35619784Seric 			(void) signal(SIGINT, oldsigint);
35719784Seric 			return;
35819784Seric 		}
35926501Seric 		dbminit(aliasfile);
36019784Seric 	}
36119784Seric 
36219784Seric 	/*
3634314Seric 	**  Read and interpret lines
3644314Seric 	*/
3654314Seric 
3669368Seric 	FileName = aliasfile;
3679368Seric 	LineNumber = 0;
3684322Seric 	naliases = bytes = longest = 0;
3694098Seric 	skipping = FALSE;
3704098Seric 	while (fgets(line, sizeof (line), af) != NULL)
3714098Seric 	{
3724322Seric 		int lhssize, rhssize;
3734322Seric 
3749368Seric 		LineNumber++;
37525278Seric 		p = index(line, '\n');
37625278Seric 		if (p != NULL)
37725278Seric 			*p = '\0';
3784098Seric 		switch (line[0])
3794098Seric 		{
3804098Seric 		  case '#':
3814098Seric 		  case '\0':
3824098Seric 			skipping = FALSE;
3834098Seric 			continue;
3844065Seric 
3854098Seric 		  case ' ':
3864098Seric 		  case '\t':
3874098Seric 			if (!skipping)
3889368Seric 				syserr("Non-continuation line starts with space");
3894098Seric 			skipping = TRUE;
3904097Seric 			continue;
3914098Seric 		}
3924098Seric 		skipping = FALSE;
3931874Seric 
3944314Seric 		/*
3954314Seric 		**  Process the LHS
3964314Seric 		**	Find the final colon, and parse the address.
39716898Seric 		**	It should resolve to a local name -- this will
39816898Seric 		**	be checked later (we want to optionally do
39916898Seric 		**	parsing of the RHS first to maximize error
40016898Seric 		**	detection).
4014314Seric 		*/
4024314Seric 
4034098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
4044097Seric 			continue;
40516898Seric 		if (*p++ != ':')
4064098Seric 		{
4079368Seric 			syserr("missing colon");
4084097Seric 			continue;
4094098Seric 		}
41016898Seric 		if (parseaddr(line, &al, 1, ':') == NULL)
4114098Seric 		{
41216898Seric 			syserr("illegal alias name");
41316898Seric 			continue;
4144098Seric 		}
41516898Seric 		loweraddr(&al);
4164314Seric 
4174314Seric 		/*
4184314Seric 		**  Process the RHS.
4194314Seric 		**	'al' is the internal form of the LHS address.
4204314Seric 		**	'p' points to the text of the RHS.
4214314Seric 		*/
4224314Seric 
4234098Seric 		rhs = p;
4244098Seric 		for (;;)
4254098Seric 		{
4264098Seric 			register char c;
4271515Seric 
42825821Seric 			if (init && CheckAliases)
4294098Seric 			{
4304157Seric 				/* do parsing & compression of addresses */
43125278Seric 				while (*p != '\0')
4324098Seric 				{
43325278Seric 					extern char *DelimChar;
43425278Seric 
43525278Seric 					while (isspace(*p) || *p == ',')
4364157Seric 						p++;
43725278Seric 					if (*p == '\0')
43825278Seric 						break;
43925278Seric 					if (parseaddr(p, &bl, -1, ',') == NULL)
44025278Seric 						usrerr("%s... bad address", p);
44125278Seric 					p = DelimChar;
4424098Seric 				}
4434098Seric 			}
4444157Seric 			else
44515769Seric 			{
44616898Seric 				p = &p[strlen(p)];
44716898Seric 				if (p[-1] == '\n')
44816898Seric 					*--p = '\0';
44915769Seric 			}
4501515Seric 
4514098Seric 			/* see if there should be a continuation line */
4524106Seric 			c = fgetc(af);
4534106Seric 			if (!feof(af))
4544314Seric 				(void) ungetc(c, af);
4554106Seric 			if (c != ' ' && c != '\t')
4564098Seric 				break;
4574098Seric 
4584098Seric 			/* read continuation line */
4594098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
4604098Seric 				break;
4619368Seric 			LineNumber++;
4624098Seric 		}
46316898Seric 		if (al.q_mailer != LocalMailer)
46416898Seric 		{
46516898Seric 			syserr("cannot alias non-local names");
46616898Seric 			continue;
46716898Seric 		}
4684314Seric 
4694314Seric 		/*
4704314Seric 		**  Insert alias into symbol table or DBM file
4714314Seric 		*/
4724314Seric 
47316898Seric 		lhssize = strlen(al.q_user) + 1;
4744322Seric 		rhssize = strlen(rhs) + 1;
4754322Seric 
4764157Seric # ifdef DBM
4774157Seric 		if (init)
4784157Seric 		{
4794157Seric 			DATUM key, content;
4804157Seric 
4814322Seric 			key.dsize = lhssize;
4824157Seric 			key.dptr = al.q_user;
4834322Seric 			content.dsize = rhssize;
4844157Seric 			content.dptr = rhs;
4854157Seric 			store(key, content);
4864157Seric 		}
4874157Seric 		else
4884157Seric # endif DBM
4894157Seric 		{
4904157Seric 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
4914157Seric 			s->s_alias = newstr(rhs);
4924157Seric 		}
4934322Seric 
4944322Seric 		/* statistics */
4954322Seric 		naliases++;
4964322Seric 		bytes += lhssize + rhssize;
4974322Seric 		if (rhssize > longest)
4984322Seric 			longest = rhssize;
4991515Seric 	}
50019784Seric 
50119784Seric # ifdef DBM
50219784Seric 	if (init)
50319784Seric 	{
50419784Seric 		/* add the distinquished alias "@" */
50519784Seric 		DATUM key;
50619784Seric 
50719784Seric 		key.dsize = 2;
50819784Seric 		key.dptr = "@";
50919784Seric 		store(key, key);
51019784Seric 
51119784Seric 		/* restore the old signal */
51219784Seric 		(void) signal(SIGINT, oldsigint);
51319784Seric 	}
51419784Seric # endif DBM
51519784Seric 
51619784Seric 	/* closing the alias file drops the lock */
5174098Seric 	(void) fclose(af);
5186898Seric 	CurEnv->e_to = NULL;
5199368Seric 	FileName = NULL;
5207051Seric 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
5214322Seric 			naliases, longest, bytes);
52224944Seric # ifdef LOG
52324944Seric 	if (LogLevel >= 8)
52424944Seric 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
52524944Seric 			naliases, longest, bytes);
52624944Seric # endif LOG
527292Seric }
528292Seric /*
529292Seric **  FORWARD -- Try to forward mail
530292Seric **
531292Seric **	This is similar but not identical to aliasing.
532292Seric **
533292Seric **	Parameters:
5344314Seric **		user -- the name of the user who's mail we would like
5354314Seric **			to forward to.  It must have been verified --
5364314Seric **			i.e., the q_home field must have been filled
5374314Seric **			in.
5384999Seric **		sendq -- a pointer to the head of the send queue to
5394999Seric **			put this user's aliases in.
540292Seric **
541292Seric **	Returns:
5424098Seric **		none.
543292Seric **
544292Seric **	Side Effects:
5453185Seric **		New names are added to send queues.
546292Seric */
547292Seric 
5484999Seric forward(user, sendq)
5492966Seric 	ADDRESS *user;
5504999Seric 	ADDRESS **sendq;
551292Seric {
5524078Seric 	char buf[60];
5534536Seric 	extern bool safefile();
5544069Seric 
5557671Seric 	if (tTd(27, 1))
5564098Seric 		printf("forward(%s)\n", user->q_paddr);
5574098Seric 
5584594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
5594098Seric 		return;
5604314Seric 	if (user->q_home == NULL)
5614314Seric 		syserr("forward: no home");
5624069Seric 
5634069Seric 	/* good address -- look for .forward file in home */
5649368Seric 	define('z', user->q_home, CurEnv);
56516154Seric 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
5664536Seric 	if (!safefile(buf, user->q_uid, S_IREAD))
5674098Seric 		return;
5684069Seric 
5694069Seric 	/* we do have an address to forward to -- do it */
5704999Seric 	include(buf, "forwarding", user, sendq);
571292Seric }
572