xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 22694)
1*22694Sdist /*
2*22694Sdist **  Sendmail
3*22694Sdist **  Copyright (c) 1983  Eric P. Allman
4*22694Sdist **  Berkeley, California
5*22694Sdist **
6*22694Sdist **  Copyright (c) 1983 Regents of the University of California.
7*22694Sdist **  All rights reserved.  The Berkeley software License Agreement
8*22694Sdist **  specifies the terms and conditions for redistribution.
9*22694Sdist */
10*22694Sdist 
11*22694Sdist #ifndef lint
12*22694Sdist static char	SccsId[] = "@(#)alias.c	5.1 (Berkeley) 06/07/85";
13*22694Sdist #endif not lint
14*22694Sdist 
15292Seric # include <pwd.h>
164212Seric # include <sys/types.h>
174212Seric # include <sys/stat.h>
188437Seric # include <signal.h>
1919784Seric # include <errno.h>
203309Seric # include "sendmail.h"
2119784Seric # ifdef FLOCK
2219784Seric # include <sys/file.h>
2319784Seric # endif FLOCK
24292Seric 
254106Seric # ifdef DBM
26*22694Sdist SCCSID(@(#)alias.c	5.1		06/07/85	(with DBM));
274106Seric # else DBM
28*22694Sdist SCCSID(@(#)alias.c	5.1		06/07/85	(without DBM));
294106Seric # endif DBM
30402Seric 
31292Seric /*
32292Seric **  ALIAS -- Compute aliases.
33292Seric **
349368Seric **	Scans the alias file for an alias for the given address.
359368Seric **	If found, it arranges to deliver to the alias list instead.
369368Seric **	Uses libdbm database if -DDBM.
37292Seric **
38292Seric **	Parameters:
394097Seric **		a -- address to alias.
404999Seric **		sendq -- a pointer to the head of the send queue
414999Seric **			to put the aliases in.
42292Seric **
43292Seric **	Returns:
44292Seric **		none
45292Seric **
46292Seric **	Side Effects:
473185Seric **		Aliases found are expanded.
48292Seric **
49292Seric **	Notes:
50292Seric **		If NoAlias (the "-n" flag) is set, no aliasing is
51292Seric **			done.
52292Seric **
53292Seric **	Deficiencies:
54292Seric **		It should complain about names that are aliased to
55292Seric **			nothing.
56292Seric */
57292Seric 
58292Seric 
591503Smark #ifdef DBM
602966Seric typedef struct
612966Seric {
622966Seric 	char	*dptr;
634157Seric 	int	dsize;
644157Seric } DATUM;
654157Seric extern DATUM fetch();
661503Smark #endif DBM
67292Seric 
684999Seric alias(a, sendq)
694097Seric 	register ADDRESS *a;
704999Seric 	ADDRESS **sendq;
71292Seric {
724081Seric 	register char *p;
735701Seric 	extern char *aliaslookup();
74292Seric 
75292Seric 	if (NoAlias)
76292Seric 		return;
77292Seric # ifdef DEBUG
787671Seric 	if (tTd(27, 1))
794098Seric 		printf("alias(%s)\n", a->q_paddr);
80292Seric # endif
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 
925701Seric 	p = aliaslookup(a->q_user);
934098Seric 	if (p == NULL)
944098Seric 		return;
95292Seric 
96292Seric 	/*
974098Seric 	**  Match on Alias.
984098Seric 	**	Deliver to the target list.
991515Seric 	*/
1001515Seric 
1014098Seric # ifdef DEBUG
1027671Seric 	if (tTd(27, 1))
1034098Seric 		printf("%s (%s, %s) aliased to %s\n",
1044098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
1054098Seric # endif
1067051Seric 	message(Arpa_Info, "aliased to %s", p);
1074098Seric 	AliasLevel++;
1089614Seric 	sendtolist(p, a, sendq);
1094098Seric 	AliasLevel--;
1104098Seric }
1114098Seric /*
1125701Seric **  ALIASLOOKUP -- look up a name in the alias file.
1135701Seric **
1145701Seric **	Parameters:
1155701Seric **		name -- the name to look up.
1165701Seric **
1175701Seric **	Returns:
1185701Seric **		the value of name.
1195701Seric **		NULL if unknown.
1205701Seric **
1215701Seric **	Side Effects:
1225701Seric **		none.
1235701Seric **
1245701Seric **	Warnings:
1255701Seric **		The return value will be trashed across calls.
1265701Seric */
1275701Seric 
1285701Seric char *
1295701Seric aliaslookup(name)
1305701Seric 	char *name;
1315701Seric {
1325701Seric # ifdef DBM
1335701Seric 	DATUM rhs, lhs;
1345701Seric 
1355701Seric 	/* create a key for fetch */
1365701Seric 	lhs.dptr = name;
1375701Seric 	lhs.dsize = strlen(name) + 1;
1385701Seric 	rhs = fetch(lhs);
1395701Seric 	return (rhs.dptr);
1405701Seric # else DBM
1415701Seric 	register STAB *s;
1425701Seric 
1435701Seric 	s = stab(name, ST_ALIAS, ST_FIND);
1445701Seric 	if (s == NULL)
1455701Seric 		return (NULL);
1465701Seric 	return (s->s_alias);
1475701Seric # endif DBM
1485701Seric }
1495701Seric /*
1504098Seric **  INITALIASES -- initialize for aliasing
1514098Seric **
1524098Seric **	Very different depending on whether we are running DBM or not.
1534098Seric **
1544098Seric **	Parameters:
1554098Seric **		aliasfile -- location of aliases.
1564157Seric **		init -- if set and if DBM, initialize the DBM files.
1574098Seric **
1584098Seric **	Returns:
1594098Seric **		none.
1604098Seric **
1614098Seric **	Side Effects:
1624098Seric **		initializes aliases:
1634098Seric **		if DBM:  opens the database.
1644098Seric **		if ~DBM: reads the aliases into the symbol table.
1654098Seric */
1664098Seric 
1674157Seric # define DBMMODE	0666
1684157Seric 
1694157Seric initaliases(aliasfile, init)
1704098Seric 	char *aliasfile;
1714157Seric 	bool init;
1724098Seric {
1739368Seric #ifdef DBM
1748437Seric 	int atcnt;
1754322Seric 	char buf[MAXNAME];
1764322Seric 	time_t modtime;
1779368Seric #endif DBM
1789368Seric 	struct stat stb;
1794322Seric 
18017984Seric 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
1818437Seric 	{
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 
1948437Seric 	dbminit(aliasfile);
19517471Seric 	atcnt = SafeAlias * 2;
19617471Seric 	if (atcnt > 0)
19717471Seric 	{
19817471Seric 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
19917471Seric 			sleep(30);
20017471Seric 	}
20117471Seric 	else
20217471Seric 		atcnt = 1;
2038437Seric 
2048437Seric 	/*
2054322Seric 	**  See if the DBM version of the file is out of date with
2064322Seric 	**  the text version.  If so, go into 'init' mode automatically.
2074322Seric 	**	This only happens if our effective userid owns the DBM
2084325Seric 	**	version or if the mode of the database is 666 -- this
2094322Seric 	**	is an attempt to avoid protection problems.  Note the
2104322Seric 	**	unpalatable hack to see if the stat succeeded.
2114322Seric 	*/
2124322Seric 
2134322Seric 	modtime = stb.st_mtime;
2144322Seric 	(void) strcpy(buf, aliasfile);
2154322Seric 	(void) strcat(buf, ".pag");
2164322Seric 	stb.st_ino = 0;
21719039Seric 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
2184322Seric 	{
21911937Seric 		errno = 0;
2209150Seric 		if (AutoRebuild && stb.st_ino != 0 &&
2219368Seric 		    ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
2224322Seric 		{
2234322Seric 			init = TRUE;
2247051Seric 			message(Arpa_Info, "rebuilding alias database");
2254322Seric 		}
2264322Seric 		else
2274322Seric 		{
22819039Seric #ifdef LOG
22919039Seric 			syslog(LOG_INFO, "alias database out of date");
23019039Seric #endif LOG
2314322Seric 			message(Arpa_Info, "Warning: alias database out of date");
2324322Seric 		}
2334322Seric 	}
2344322Seric 
2354322Seric 
2364322Seric 	/*
2378437Seric 	**  If necessary, load the DBM file.
2384322Seric 	**	If running without DBM, load the symbol table.
2394322Seric 	*/
2404322Seric 
2414157Seric 	if (init)
2428437Seric 	{
2434157Seric 		readaliases(aliasfile, TRUE);
2448437Seric 	}
2454098Seric # else DBM
2464157Seric 	readaliases(aliasfile, init);
2474157Seric # endif DBM
2484157Seric }
2494157Seric /*
2504157Seric **  READALIASES -- read and process the alias file.
2514157Seric **
2524157Seric **	This routine implements the part of initaliases that occurs
2534157Seric **	when we are not going to use the DBM stuff.
2544157Seric **
2554157Seric **	Parameters:
2564157Seric **		aliasfile -- the pathname of the alias file master.
2574157Seric **		init -- if set, initialize the DBM stuff.
2584157Seric **
2594157Seric **	Returns:
2604157Seric **		none.
2614157Seric **
2624157Seric **	Side Effects:
2634157Seric **		Reads aliasfile into the symbol table.
2644157Seric **		Optionally, builds the .dir & .pag files.
2654157Seric */
2664157Seric 
2674157Seric static
2684157Seric readaliases(aliasfile, init)
2694157Seric 	char *aliasfile;
2704157Seric 	bool init;
2714157Seric {
2724098Seric 	register char *p;
2734098Seric 	char *p2;
2744098Seric 	char *rhs;
2754098Seric 	bool skipping;
2769368Seric 	int naliases, bytes, longest;
2779368Seric 	FILE *af;
27819784Seric 	int (*oldsigint)();
2794098Seric 	ADDRESS al, bl;
2804106Seric 	register STAB *s;
2819368Seric 	char line[BUFSIZ];
2824098Seric 
2834098Seric 	if ((af = fopen(aliasfile, "r")) == NULL)
2841515Seric 	{
2854098Seric # ifdef DEBUG
2867671Seric 		if (tTd(27, 1))
2874106Seric 			printf("Can't open %s\n", aliasfile);
2884098Seric # endif
2894098Seric 		errno = 0;
2904098Seric 		NoAlias++;
2914098Seric 		return;
2924098Seric 	}
2934314Seric 
29419784Seric # ifdef DBM
29519784Seric # ifdef FLOCK
29619784Seric 	/* see if someone else is rebuilding the alias file already */
29719784Seric 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
29819784Seric 	{
29919784Seric 		/* yes, they are -- wait until done and then return */
30019784Seric 		message(Arpa_Info, "Alias file is already being rebuilt");
30119784Seric 		if (OpMode != MD_INITALIAS)
30219784Seric 		{
30319784Seric 			/* wait for other rebuild to complete */
30419784Seric 			(void) flock(fileno(af), LOCK_EX);
30519784Seric 		}
30619784Seric 		fclose(af);
30719784Seric 		errno = 0;
30819784Seric 		return;
30919784Seric 	}
31019784Seric # endif FLOCK
31119784Seric # endif DBM
31219784Seric 
3134314Seric 	/*
31419784Seric 	**  If initializing, create the new DBM files.
31519784Seric 	*/
31619784Seric 
31719784Seric 	if (init)
31819784Seric 	{
31919784Seric 		oldsigint = signal(SIGINT, SIG_IGN);
32019784Seric 		(void) strcpy(line, aliasfile);
32119784Seric 		(void) strcat(line, ".dir");
32219784Seric 		if (close(creat(line, DBMMODE)) < 0)
32319784Seric 		{
32419784Seric 			syserr("cannot make %s", line);
32519784Seric 			(void) signal(SIGINT, oldsigint);
32619784Seric 			return;
32719784Seric 		}
32819784Seric 		(void) strcpy(line, aliasfile);
32919784Seric 		(void) strcat(line, ".pag");
33019784Seric 		if (close(creat(line, DBMMODE)) < 0)
33119784Seric 		{
33219784Seric 			syserr("cannot make %s", line);
33319784Seric 			(void) signal(SIGINT, oldsigint);
33419784Seric 			return;
33519784Seric 		}
33619784Seric 	}
33719784Seric 
33819784Seric 	/*
3394314Seric 	**  Read and interpret lines
3404314Seric 	*/
3414314Seric 
3429368Seric 	FileName = aliasfile;
3439368Seric 	LineNumber = 0;
3444322Seric 	naliases = bytes = longest = 0;
3454098Seric 	skipping = FALSE;
3464098Seric 	while (fgets(line, sizeof (line), af) != NULL)
3474098Seric 	{
3484322Seric 		int lhssize, rhssize;
3494322Seric 
3509368Seric 		LineNumber++;
3514098Seric 		switch (line[0])
3524098Seric 		{
3534098Seric 		  case '#':
3544098Seric 		  case '\n':
3554098Seric 		  case '\0':
3564098Seric 			skipping = FALSE;
3574098Seric 			continue;
3584065Seric 
3594098Seric 		  case ' ':
3604098Seric 		  case '\t':
3614098Seric 			if (!skipping)
3629368Seric 				syserr("Non-continuation line starts with space");
3634098Seric 			skipping = TRUE;
3644097Seric 			continue;
3654098Seric 		}
3664098Seric 		skipping = FALSE;
3671874Seric 
3684314Seric 		/*
3694314Seric 		**  Process the LHS
3704314Seric 		**	Find the final colon, and parse the address.
37116898Seric 		**	It should resolve to a local name -- this will
37216898Seric 		**	be checked later (we want to optionally do
37316898Seric 		**	parsing of the RHS first to maximize error
37416898Seric 		**	detection).
3754314Seric 		*/
3764314Seric 
3774098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
3784097Seric 			continue;
37916898Seric 		if (*p++ != ':')
3804098Seric 		{
3819368Seric 			syserr("missing colon");
3824097Seric 			continue;
3834098Seric 		}
38416898Seric 		if (parseaddr(line, &al, 1, ':') == NULL)
3854098Seric 		{
38616898Seric 			syserr("illegal alias name");
38716898Seric 			continue;
3884098Seric 		}
38916898Seric 		loweraddr(&al);
3904314Seric 
3914314Seric 		/*
3924314Seric 		**  Process the RHS.
3934314Seric 		**	'al' is the internal form of the LHS address.
3944314Seric 		**	'p' points to the text of the RHS.
3954314Seric 		*/
3964314Seric 
3974098Seric 		rhs = p;
3984098Seric 		for (;;)
3994098Seric 		{
4004098Seric 			register char c;
4011515Seric 
4024157Seric 			if (init)
4034098Seric 			{
4044157Seric 				/* do parsing & compression of addresses */
4054098Seric 				c = *p;
4064157Seric 				while (c != '\0')
4074098Seric 				{
4084157Seric 					p2 = p;
4094157Seric 					while (*p != '\n' && *p != ',' && *p != '\0')
4104157Seric 						p++;
4114157Seric 					c = *p;
4124322Seric 					if (c == '\n')
4134322Seric 						c = '\0';
41417199Seric 					*p = '\0';
41517199Seric 					if (*p2 != '\0')
41617199Seric 						(void) parseaddr(p2, &bl, -1, ',');
41717199Seric 					if (c != '\0')
41817199Seric 						*p++ = c;
4194098Seric 				}
4204098Seric 			}
4214157Seric 			else
42215769Seric 			{
42316898Seric 				p = &p[strlen(p)];
42416898Seric 				if (p[-1] == '\n')
42516898Seric 					*--p = '\0';
42615769Seric 			}
4271515Seric 
4284098Seric 			/* see if there should be a continuation line */
4294106Seric 			c = fgetc(af);
4304106Seric 			if (!feof(af))
4314314Seric 				(void) ungetc(c, af);
4324106Seric 			if (c != ' ' && c != '\t')
4334098Seric 				break;
4344098Seric 
4354098Seric 			/* read continuation line */
4364098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
4374098Seric 				break;
4389368Seric 			LineNumber++;
4394098Seric 		}
44016898Seric 		if (al.q_mailer != LocalMailer)
44116898Seric 		{
44216898Seric 			syserr("cannot alias non-local names");
44316898Seric 			continue;
44416898Seric 		}
4454314Seric 
4464314Seric 		/*
4474314Seric 		**  Insert alias into symbol table or DBM file
4484314Seric 		*/
4494314Seric 
45016898Seric 		lhssize = strlen(al.q_user) + 1;
4514322Seric 		rhssize = strlen(rhs) + 1;
4524322Seric 
4534157Seric # ifdef DBM
4544157Seric 		if (init)
4554157Seric 		{
4564157Seric 			DATUM key, content;
4574157Seric 
4584322Seric 			key.dsize = lhssize;
4594157Seric 			key.dptr = al.q_user;
4604322Seric 			content.dsize = rhssize;
4614157Seric 			content.dptr = rhs;
4624157Seric 			store(key, content);
4634157Seric 		}
4644157Seric 		else
4654157Seric # endif DBM
4664157Seric 		{
4674157Seric 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
4684157Seric 			s->s_alias = newstr(rhs);
4694157Seric 		}
4704322Seric 
4714322Seric 		/* statistics */
4724322Seric 		naliases++;
4734322Seric 		bytes += lhssize + rhssize;
4744322Seric 		if (rhssize > longest)
4754322Seric 			longest = rhssize;
4761515Seric 	}
47719784Seric 
47819784Seric # ifdef DBM
47919784Seric 	if (init)
48019784Seric 	{
48119784Seric 		/* add the distinquished alias "@" */
48219784Seric 		DATUM key;
48319784Seric 
48419784Seric 		key.dsize = 2;
48519784Seric 		key.dptr = "@";
48619784Seric 		store(key, key);
48719784Seric 
48819784Seric 		/* restore the old signal */
48919784Seric 		(void) signal(SIGINT, oldsigint);
49019784Seric 	}
49119784Seric # endif DBM
49219784Seric 
49319784Seric 	/* closing the alias file drops the lock */
4944098Seric 	(void) fclose(af);
4956898Seric 	CurEnv->e_to = NULL;
4969368Seric 	FileName = NULL;
4977051Seric 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
4984322Seric 			naliases, longest, bytes);
499292Seric }
500292Seric /*
501292Seric **  FORWARD -- Try to forward mail
502292Seric **
503292Seric **	This is similar but not identical to aliasing.
504292Seric **
505292Seric **	Parameters:
5064314Seric **		user -- the name of the user who's mail we would like
5074314Seric **			to forward to.  It must have been verified --
5084314Seric **			i.e., the q_home field must have been filled
5094314Seric **			in.
5104999Seric **		sendq -- a pointer to the head of the send queue to
5114999Seric **			put this user's aliases in.
512292Seric **
513292Seric **	Returns:
5144098Seric **		none.
515292Seric **
516292Seric **	Side Effects:
5173185Seric **		New names are added to send queues.
518292Seric */
519292Seric 
5204999Seric forward(user, sendq)
5212966Seric 	ADDRESS *user;
5224999Seric 	ADDRESS **sendq;
523292Seric {
5244078Seric 	char buf[60];
5254536Seric 	extern bool safefile();
5264069Seric 
5274098Seric # ifdef DEBUG
5287671Seric 	if (tTd(27, 1))
5294098Seric 		printf("forward(%s)\n", user->q_paddr);
5304098Seric # endif DEBUG
5314098Seric 
5324594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
5334098Seric 		return;
5344314Seric # ifdef DEBUG
5354314Seric 	if (user->q_home == NULL)
5364314Seric 		syserr("forward: no home");
5374314Seric # endif DEBUG
5384069Seric 
5394069Seric 	/* good address -- look for .forward file in home */
5409368Seric 	define('z', user->q_home, CurEnv);
54116154Seric 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
5424536Seric 	if (!safefile(buf, user->q_uid, S_IREAD))
5434098Seric 		return;
5444069Seric 
5454069Seric 	/* we do have an address to forward to -- do it */
5464999Seric 	include(buf, "forwarding", user, sendq);
547292Seric }
548