xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 17984)
1292Seric # include <pwd.h>
24212Seric # include <sys/types.h>
34212Seric # include <sys/stat.h>
48437Seric # include <signal.h>
53309Seric # include "sendmail.h"
6292Seric 
74106Seric # ifdef DBM
8*17984Seric SCCSID(@(#)alias.c	4.8		02/15/85	(with DBM));
94106Seric # else DBM
10*17984Seric SCCSID(@(#)alias.c	4.8		02/15/85	(without DBM));
114106Seric # endif DBM
12402Seric 
13292Seric /*
14292Seric **  ALIAS -- Compute aliases.
15292Seric **
169368Seric **	Scans the alias file for an alias for the given address.
179368Seric **	If found, it arranges to deliver to the alias list instead.
189368Seric **	Uses libdbm database if -DDBM.
19292Seric **
20292Seric **	Parameters:
214097Seric **		a -- address to alias.
224999Seric **		sendq -- a pointer to the head of the send queue
234999Seric **			to put the aliases in.
24292Seric **
25292Seric **	Returns:
26292Seric **		none
27292Seric **
28292Seric **	Side Effects:
293185Seric **		Aliases found are expanded.
30292Seric **
31292Seric **	Notes:
32292Seric **		If NoAlias (the "-n" flag) is set, no aliasing is
33292Seric **			done.
34292Seric **
35292Seric **	Deficiencies:
36292Seric **		It should complain about names that are aliased to
37292Seric **			nothing.
38292Seric */
39292Seric 
40292Seric 
411503Smark #ifdef DBM
422966Seric typedef struct
432966Seric {
442966Seric 	char	*dptr;
454157Seric 	int	dsize;
464157Seric } DATUM;
474157Seric extern DATUM fetch();
481503Smark #endif DBM
49292Seric 
504999Seric alias(a, sendq)
514097Seric 	register ADDRESS *a;
524999Seric 	ADDRESS **sendq;
53292Seric {
544081Seric 	register char *p;
555701Seric 	extern char *aliaslookup();
56292Seric 
57292Seric 	if (NoAlias)
58292Seric 		return;
59292Seric # ifdef DEBUG
607671Seric 	if (tTd(27, 1))
614098Seric 		printf("alias(%s)\n", a->q_paddr);
62292Seric # endif
63292Seric 
644098Seric 	/* don't realias already aliased names */
654098Seric 	if (bitset(QDONTSEND, a->q_flags))
664098Seric 		return;
674098Seric 
686898Seric 	CurEnv->e_to = a->q_paddr;
694098Seric 
704314Seric 	/*
714314Seric 	**  Look up this name
724314Seric 	*/
734314Seric 
745701Seric 	p = aliaslookup(a->q_user);
754098Seric 	if (p == NULL)
764098Seric 		return;
77292Seric 
78292Seric 	/*
794098Seric 	**  Match on Alias.
804098Seric 	**	Deliver to the target list.
811515Seric 	*/
821515Seric 
834098Seric # ifdef DEBUG
847671Seric 	if (tTd(27, 1))
854098Seric 		printf("%s (%s, %s) aliased to %s\n",
864098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
874098Seric # endif
887051Seric 	message(Arpa_Info, "aliased to %s", p);
894098Seric 	AliasLevel++;
909614Seric 	sendtolist(p, a, sendq);
914098Seric 	AliasLevel--;
924098Seric }
934098Seric /*
945701Seric **  ALIASLOOKUP -- look up a name in the alias file.
955701Seric **
965701Seric **	Parameters:
975701Seric **		name -- the name to look up.
985701Seric **
995701Seric **	Returns:
1005701Seric **		the value of name.
1015701Seric **		NULL if unknown.
1025701Seric **
1035701Seric **	Side Effects:
1045701Seric **		none.
1055701Seric **
1065701Seric **	Warnings:
1075701Seric **		The return value will be trashed across calls.
1085701Seric */
1095701Seric 
1105701Seric char *
1115701Seric aliaslookup(name)
1125701Seric 	char *name;
1135701Seric {
1145701Seric # ifdef DBM
1155701Seric 	DATUM rhs, lhs;
1165701Seric 
1175701Seric 	/* create a key for fetch */
1185701Seric 	lhs.dptr = name;
1195701Seric 	lhs.dsize = strlen(name) + 1;
1205701Seric 	rhs = fetch(lhs);
1215701Seric 	return (rhs.dptr);
1225701Seric # else DBM
1235701Seric 	register STAB *s;
1245701Seric 
1255701Seric 	s = stab(name, ST_ALIAS, ST_FIND);
1265701Seric 	if (s == NULL)
1275701Seric 		return (NULL);
1285701Seric 	return (s->s_alias);
1295701Seric # endif DBM
1305701Seric }
1315701Seric /*
1324098Seric **  INITALIASES -- initialize for aliasing
1334098Seric **
1344098Seric **	Very different depending on whether we are running DBM or not.
1354098Seric **
1364098Seric **	Parameters:
1374098Seric **		aliasfile -- location of aliases.
1384157Seric **		init -- if set and if DBM, initialize the DBM files.
1394098Seric **
1404098Seric **	Returns:
1414098Seric **		none.
1424098Seric **
1434098Seric **	Side Effects:
1444098Seric **		initializes aliases:
1454098Seric **		if DBM:  opens the database.
1464098Seric **		if ~DBM: reads the aliases into the symbol table.
1474098Seric */
1484098Seric 
1494157Seric # define DBMMODE	0666
1504157Seric 
1514157Seric initaliases(aliasfile, init)
1524098Seric 	char *aliasfile;
1534157Seric 	bool init;
1544098Seric {
1559368Seric #ifdef DBM
1568437Seric 	int atcnt;
1574322Seric 	char buf[MAXNAME];
1584322Seric 	time_t modtime;
1598437Seric 	int (*oldsigint)();
1609368Seric #endif DBM
1619368Seric 	struct stat stb;
1624322Seric 
163*17984Seric 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
1648437Seric 	{
1658437Seric 		NoAlias = TRUE;
16611937Seric 		errno = 0;
1678437Seric 		return;
1688437Seric 	}
1698437Seric 
1708928Seric # ifdef DBM
1714322Seric 	/*
1728437Seric 	**  Check to see that the alias file is complete.
1738437Seric 	**	If not, we will assume that someone died, and it is up
1748437Seric 	**	to us to rebuild it.
1758437Seric 	*/
1768437Seric 
1778437Seric 	dbminit(aliasfile);
17817471Seric 	atcnt = SafeAlias * 2;
17917471Seric 	if (atcnt > 0)
18017471Seric 	{
18117471Seric 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
18217471Seric 			sleep(30);
18317471Seric 	}
18417471Seric 	else
18517471Seric 		atcnt = 1;
1868437Seric 
1878437Seric 	/*
1884322Seric 	**  See if the DBM version of the file is out of date with
1894322Seric 	**  the text version.  If so, go into 'init' mode automatically.
1904322Seric 	**	This only happens if our effective userid owns the DBM
1914325Seric 	**	version or if the mode of the database is 666 -- this
1924322Seric 	**	is an attempt to avoid protection problems.  Note the
1934322Seric 	**	unpalatable hack to see if the stat succeeded.
1944322Seric 	*/
1954322Seric 
1964322Seric 	modtime = stb.st_mtime;
1974322Seric 	(void) strcpy(buf, aliasfile);
1984322Seric 	(void) strcat(buf, ".pag");
1994322Seric 	stb.st_ino = 0;
2009150Seric 	if (!init && (atcnt < 0 || stat(buf, &stb) < 0 || stb.st_mtime < modtime))
2014322Seric 	{
20211937Seric 		errno = 0;
2039150Seric 		if (AutoRebuild && stb.st_ino != 0 &&
2049368Seric 		    ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
2054322Seric 		{
2064322Seric 			init = TRUE;
2077051Seric 			message(Arpa_Info, "rebuilding alias database");
2084322Seric 		}
2094322Seric 		else
2104322Seric 		{
2117051Seric 			bool oldverb = Verbose;
2127051Seric 
2137051Seric 			Verbose = TRUE;
2144322Seric 			message(Arpa_Info, "Warning: alias database out of date");
2157051Seric 			Verbose = oldverb;
2164322Seric 		}
2174322Seric 	}
2184322Seric 
2194322Seric 	/*
2204322Seric 	**  If initializing, create the new files.
2214322Seric 	**	We should lock the alias file here to prevent other
2224322Seric 	**	instantiations of sendmail from reading an incomplete
2234322Seric 	**	file -- or worse yet, doing a concurrent initialize.
2244322Seric 	*/
2254322Seric 
2264157Seric 	if (init)
2274157Seric 	{
2288437Seric 		oldsigint = signal(SIGINT, SIG_IGN);
2294157Seric 		(void) strcpy(buf, aliasfile);
2304157Seric 		(void) strcat(buf, ".dir");
2314157Seric 		if (close(creat(buf, DBMMODE)) < 0)
2324157Seric 		{
2334157Seric 			syserr("cannot make %s", buf);
2349368Seric 			(void) signal(SIGINT, oldsigint);
2354157Seric 			return;
2364157Seric 		}
2374157Seric 		(void) strcpy(buf, aliasfile);
2384157Seric 		(void) strcat(buf, ".pag");
2394157Seric 		if (close(creat(buf, DBMMODE)) < 0)
2404157Seric 		{
2414157Seric 			syserr("cannot make %s", buf);
2429368Seric 			(void) signal(SIGINT, oldsigint);
2434157Seric 			return;
2444157Seric 		}
2454157Seric 	}
2464322Seric 
2474322Seric 	/*
2488437Seric 	**  If necessary, load the DBM file.
2494322Seric 	**	If running without DBM, load the symbol table.
2508437Seric 	**	After loading the DBM file, add the distinquished alias "@".
2514322Seric 	*/
2524322Seric 
2534157Seric 	if (init)
2548437Seric 	{
2558437Seric 		DATUM key;
2568437Seric 
2574157Seric 		readaliases(aliasfile, TRUE);
2588437Seric 		key.dsize = 2;
2598437Seric 		key.dptr = "@";
2608437Seric 		store(key, key);
2618437Seric 		(void) signal(SIGINT, oldsigint);
2628437Seric 	}
2634098Seric # else DBM
2644157Seric 	readaliases(aliasfile, init);
2654157Seric # endif DBM
2664157Seric }
2674157Seric /*
2684157Seric **  READALIASES -- read and process the alias file.
2694157Seric **
2704157Seric **	This routine implements the part of initaliases that occurs
2714157Seric **	when we are not going to use the DBM stuff.
2724157Seric **
2734157Seric **	Parameters:
2744157Seric **		aliasfile -- the pathname of the alias file master.
2754157Seric **		init -- if set, initialize the DBM stuff.
2764157Seric **
2774157Seric **	Returns:
2784157Seric **		none.
2794157Seric **
2804157Seric **	Side Effects:
2814157Seric **		Reads aliasfile into the symbol table.
2824157Seric **		Optionally, builds the .dir & .pag files.
2834157Seric */
2844157Seric 
2854157Seric static
2864157Seric readaliases(aliasfile, init)
2874157Seric 	char *aliasfile;
2884157Seric 	bool init;
2894157Seric {
2904098Seric 	register char *p;
2914098Seric 	char *p2;
2924098Seric 	char *rhs;
2934098Seric 	bool skipping;
2949368Seric 	int naliases, bytes, longest;
2959368Seric 	FILE *af;
2964098Seric 	ADDRESS al, bl;
2974106Seric 	register STAB *s;
2989368Seric 	char line[BUFSIZ];
2994098Seric 
3004098Seric 	if ((af = fopen(aliasfile, "r")) == NULL)
3011515Seric 	{
3024098Seric # ifdef DEBUG
3037671Seric 		if (tTd(27, 1))
3044106Seric 			printf("Can't open %s\n", aliasfile);
3054098Seric # endif
3064098Seric 		errno = 0;
3074098Seric 		NoAlias++;
3084098Seric 		return;
3094098Seric 	}
3104314Seric 
3114314Seric 	/*
3124314Seric 	**  Read and interpret lines
3134314Seric 	*/
3144314Seric 
3159368Seric 	FileName = aliasfile;
3169368Seric 	LineNumber = 0;
3174322Seric 	naliases = bytes = longest = 0;
3184098Seric 	skipping = FALSE;
3194098Seric 	while (fgets(line, sizeof (line), af) != NULL)
3204098Seric 	{
3214322Seric 		int lhssize, rhssize;
3224322Seric 
3239368Seric 		LineNumber++;
3244098Seric 		switch (line[0])
3254098Seric 		{
3264098Seric 		  case '#':
3274098Seric 		  case '\n':
3284098Seric 		  case '\0':
3294098Seric 			skipping = FALSE;
3304098Seric 			continue;
3314065Seric 
3324098Seric 		  case ' ':
3334098Seric 		  case '\t':
3344098Seric 			if (!skipping)
3359368Seric 				syserr("Non-continuation line starts with space");
3364098Seric 			skipping = TRUE;
3374097Seric 			continue;
3384098Seric 		}
3394098Seric 		skipping = FALSE;
3401874Seric 
3414314Seric 		/*
3424314Seric 		**  Process the LHS
3434314Seric 		**	Find the final colon, and parse the address.
34416898Seric 		**	It should resolve to a local name -- this will
34516898Seric 		**	be checked later (we want to optionally do
34616898Seric 		**	parsing of the RHS first to maximize error
34716898Seric 		**	detection).
3484314Seric 		*/
3494314Seric 
3504098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
3514097Seric 			continue;
35216898Seric 		if (*p++ != ':')
3534098Seric 		{
3549368Seric 			syserr("missing colon");
3554097Seric 			continue;
3564098Seric 		}
35716898Seric 		if (parseaddr(line, &al, 1, ':') == NULL)
3584098Seric 		{
35916898Seric 			syserr("illegal alias name");
36016898Seric 			continue;
3614098Seric 		}
36216898Seric 		loweraddr(&al);
3634314Seric 
3644314Seric 		/*
3654314Seric 		**  Process the RHS.
3664314Seric 		**	'al' is the internal form of the LHS address.
3674314Seric 		**	'p' points to the text of the RHS.
3684314Seric 		*/
3694314Seric 
3704098Seric 		rhs = p;
3714098Seric 		for (;;)
3724098Seric 		{
3734098Seric 			register char c;
3741515Seric 
3754157Seric 			if (init)
3764098Seric 			{
3774157Seric 				/* do parsing & compression of addresses */
3784098Seric 				c = *p;
3794157Seric 				while (c != '\0')
3804098Seric 				{
3814157Seric 					p2 = p;
3824157Seric 					while (*p != '\n' && *p != ',' && *p != '\0')
3834157Seric 						p++;
3844157Seric 					c = *p;
3854322Seric 					if (c == '\n')
3864322Seric 						c = '\0';
38717199Seric 					*p = '\0';
38817199Seric 					if (*p2 != '\0')
38917199Seric 						(void) parseaddr(p2, &bl, -1, ',');
39017199Seric 					if (c != '\0')
39117199Seric 						*p++ = c;
3924098Seric 				}
3934098Seric 			}
3944157Seric 			else
39515769Seric 			{
39616898Seric 				p = &p[strlen(p)];
39716898Seric 				if (p[-1] == '\n')
39816898Seric 					*--p = '\0';
39915769Seric 			}
4001515Seric 
4014098Seric 			/* see if there should be a continuation line */
4024106Seric 			c = fgetc(af);
4034106Seric 			if (!feof(af))
4044314Seric 				(void) ungetc(c, af);
4054106Seric 			if (c != ' ' && c != '\t')
4064098Seric 				break;
4074098Seric 
4084098Seric 			/* read continuation line */
4094098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
4104098Seric 				break;
4119368Seric 			LineNumber++;
4124098Seric 		}
41316898Seric 		if (al.q_mailer != LocalMailer)
41416898Seric 		{
41516898Seric 			syserr("cannot alias non-local names");
41616898Seric 			continue;
41716898Seric 		}
4184314Seric 
4194314Seric 		/*
4204314Seric 		**  Insert alias into symbol table or DBM file
4214314Seric 		*/
4224314Seric 
42316898Seric 		lhssize = strlen(al.q_user) + 1;
4244322Seric 		rhssize = strlen(rhs) + 1;
4254322Seric 
4264157Seric # ifdef DBM
4274157Seric 		if (init)
4284157Seric 		{
4294157Seric 			DATUM key, content;
4304157Seric 
4314322Seric 			key.dsize = lhssize;
4324157Seric 			key.dptr = al.q_user;
4334322Seric 			content.dsize = rhssize;
4344157Seric 			content.dptr = rhs;
4354157Seric 			store(key, content);
4364157Seric 		}
4374157Seric 		else
4384157Seric # endif DBM
4394157Seric 		{
4404157Seric 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
4414157Seric 			s->s_alias = newstr(rhs);
4424157Seric 		}
4434322Seric 
4444322Seric 		/* statistics */
4454322Seric 		naliases++;
4464322Seric 		bytes += lhssize + rhssize;
4474322Seric 		if (rhssize > longest)
4484322Seric 			longest = rhssize;
4491515Seric 	}
4504098Seric 	(void) fclose(af);
4516898Seric 	CurEnv->e_to = NULL;
4529368Seric 	FileName = NULL;
4537051Seric 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
4544322Seric 			naliases, longest, bytes);
455292Seric }
456292Seric /*
457292Seric **  FORWARD -- Try to forward mail
458292Seric **
459292Seric **	This is similar but not identical to aliasing.
460292Seric **
461292Seric **	Parameters:
4624314Seric **		user -- the name of the user who's mail we would like
4634314Seric **			to forward to.  It must have been verified --
4644314Seric **			i.e., the q_home field must have been filled
4654314Seric **			in.
4664999Seric **		sendq -- a pointer to the head of the send queue to
4674999Seric **			put this user's aliases in.
468292Seric **
469292Seric **	Returns:
4704098Seric **		none.
471292Seric **
472292Seric **	Side Effects:
4733185Seric **		New names are added to send queues.
474292Seric */
475292Seric 
4764999Seric forward(user, sendq)
4772966Seric 	ADDRESS *user;
4784999Seric 	ADDRESS **sendq;
479292Seric {
4804078Seric 	char buf[60];
4814536Seric 	extern bool safefile();
4824069Seric 
4834098Seric # ifdef DEBUG
4847671Seric 	if (tTd(27, 1))
4854098Seric 		printf("forward(%s)\n", user->q_paddr);
4864098Seric # endif DEBUG
4874098Seric 
4884594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
4894098Seric 		return;
4904314Seric # ifdef DEBUG
4914314Seric 	if (user->q_home == NULL)
4924314Seric 		syserr("forward: no home");
4934314Seric # endif DEBUG
4944069Seric 
4954069Seric 	/* good address -- look for .forward file in home */
4969368Seric 	define('z', user->q_home, CurEnv);
49716154Seric 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
4984536Seric 	if (!safefile(buf, user->q_uid, S_IREAD))
4994098Seric 		return;
5004069Seric 
5014069Seric 	/* we do have an address to forward to -- do it */
5024999Seric 	include(buf, "forwarding", user, sendq);
503292Seric }
504