xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 27176)
122694Sdist /*
222694Sdist **  Sendmail
322694Sdist **  Copyright (c) 1983  Eric P. Allman
422694Sdist **  Berkeley, California
522694Sdist **
622694Sdist **  Copyright (c) 1983 Regents of the University of California.
722694Sdist **  All rights reserved.  The Berkeley software License Agreement
822694Sdist **  specifies the terms and conditions for redistribution.
922694Sdist */
1022694Sdist 
11292Seric # include <pwd.h>
124212Seric # include <sys/types.h>
134212Seric # include <sys/stat.h>
148437Seric # include <signal.h>
1519784Seric # include <errno.h>
163309Seric # include "sendmail.h"
1719784Seric # ifdef FLOCK
1819784Seric # include <sys/file.h>
1919784Seric # endif FLOCK
20292Seric 
2126435Seric #ifndef lint
2226435Seric # ifdef DBM
23*27176Seric static char	SccsId[] = "@(#)alias.c	5.13 (Berkeley) 04/17/86	(with DBM)";
2426435Seric # else DBM
25*27176Seric static char	SccsId[] = "@(#)alias.c	5.13 (Berkeley) 04/17/86	(without DBM)";
2626435Seric # endif DBM
2726435Seric #endif not lint
28402Seric 
2926435Seric 
30292Seric /*
31292Seric **  ALIAS -- Compute aliases.
32292Seric **
339368Seric **	Scans the alias file for an alias for the given address.
349368Seric **	If found, it arranges to deliver to the alias list instead.
359368Seric **	Uses libdbm database if -DDBM.
36292Seric **
37292Seric **	Parameters:
384097Seric **		a -- address to alias.
394999Seric **		sendq -- a pointer to the head of the send queue
404999Seric **			to put the aliases in.
41292Seric **
42292Seric **	Returns:
43292Seric **		none
44292Seric **
45292Seric **	Side Effects:
463185Seric **		Aliases found are expanded.
47292Seric **
48292Seric **	Notes:
49292Seric **		If NoAlias (the "-n" flag) is set, no aliasing is
50292Seric **			done.
51292Seric **
52292Seric **	Deficiencies:
53292Seric **		It should complain about names that are aliased to
54292Seric **			nothing.
55292Seric */
56292Seric 
57292Seric 
581503Smark #ifdef DBM
592966Seric typedef struct
602966Seric {
612966Seric 	char	*dptr;
624157Seric 	int	dsize;
634157Seric } DATUM;
644157Seric extern DATUM fetch();
651503Smark #endif DBM
66292Seric 
674999Seric alias(a, sendq)
684097Seric 	register ADDRESS *a;
694999Seric 	ADDRESS **sendq;
70292Seric {
714081Seric 	register char *p;
725701Seric 	extern char *aliaslookup();
73292Seric 
74292Seric # ifdef DEBUG
757671Seric 	if (tTd(27, 1))
764098Seric 		printf("alias(%s)\n", a->q_paddr);
77292Seric # endif
78292Seric 
794098Seric 	/* don't realias already aliased names */
804098Seric 	if (bitset(QDONTSEND, a->q_flags))
814098Seric 		return;
824098Seric 
836898Seric 	CurEnv->e_to = a->q_paddr;
844098Seric 
854314Seric 	/*
864314Seric 	**  Look up this name
874314Seric 	*/
884314Seric 
8924944Seric 	if (NoAlias)
9024944Seric 		p = NULL;
9124944Seric 	else
9224944Seric 		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;
17525522Seric 	time_t modtime;
17625522Seric 	bool automatic = FALSE;
1774322Seric 	char buf[MAXNAME];
1789368Seric #endif DBM
1799368Seric 	struct stat stb;
180*27176Seric 	static bool initialized = FALSE;
1814322Seric 
182*27176Seric 	if (initialized)
183*27176Seric 		return;
184*27176Seric 	initialized = TRUE;
185*27176Seric 
18617984Seric 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
1878437Seric 	{
18825522Seric 		if (aliasfile != NULL && init)
18925522Seric 			syserr("Cannot open %s", aliasfile);
1908437Seric 		NoAlias = TRUE;
19111937Seric 		errno = 0;
1928437Seric 		return;
1938437Seric 	}
1948437Seric 
1958928Seric # ifdef DBM
1964322Seric 	/*
1978437Seric 	**  Check to see that the alias file is complete.
1988437Seric 	**	If not, we will assume that someone died, and it is up
1998437Seric 	**	to us to rebuild it.
2008437Seric 	*/
2018437Seric 
20225689Seric 	if (!init)
20325689Seric 		dbminit(aliasfile);
20417471Seric 	atcnt = SafeAlias * 2;
20517471Seric 	if (atcnt > 0)
20617471Seric 	{
20717471Seric 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
20825689Seric 		{
20925689Seric 			/*
21025689Seric 			**  Reinitialize alias file in case the new
21125689Seric 			**  one is mv'ed in instead of cp'ed in.
21225689Seric 			**
21325689Seric 			**	Only works with new DBM -- old one will
21425689Seric 			**	just consume file descriptors forever.
21525689Seric 			**	If you have a dbmclose() it can be
21625689Seric 			**	added before the sleep(30).
21725689Seric 			*/
21825689Seric 
21917471Seric 			sleep(30);
22025689Seric # ifdef NDBM
22125689Seric 			dbminit(aliasfile);
22225689Seric # endif NDBM
22325689Seric 		}
22417471Seric 	}
22517471Seric 	else
22617471Seric 		atcnt = 1;
2278437Seric 
2288437Seric 	/*
2294322Seric 	**  See if the DBM version of the file is out of date with
2304322Seric 	**  the text version.  If so, go into 'init' mode automatically.
2314322Seric 	**	This only happens if our effective userid owns the DBM
2324325Seric 	**	version or if the mode of the database is 666 -- this
2334322Seric 	**	is an attempt to avoid protection problems.  Note the
2344322Seric 	**	unpalatable hack to see if the stat succeeded.
2354322Seric 	*/
2364322Seric 
2374322Seric 	modtime = stb.st_mtime;
2384322Seric 	(void) strcpy(buf, aliasfile);
2394322Seric 	(void) strcat(buf, ".pag");
2404322Seric 	stb.st_ino = 0;
24119039Seric 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
2424322Seric 	{
24311937Seric 		errno = 0;
2449150Seric 		if (AutoRebuild && stb.st_ino != 0 &&
2459368Seric 		    ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
2464322Seric 		{
2474322Seric 			init = TRUE;
24825522Seric 			automatic = TRUE;
2497051Seric 			message(Arpa_Info, "rebuilding alias database");
25024944Seric #ifdef LOG
25124944Seric 			if (LogLevel >= 7)
25224944Seric 				syslog(LOG_INFO, "rebuilding alias database");
25324944Seric #endif LOG
2544322Seric 		}
2554322Seric 		else
2564322Seric 		{
25719039Seric #ifdef LOG
25824944Seric 			if (LogLevel >= 7)
25924944Seric 				syslog(LOG_INFO, "alias database out of date");
26019039Seric #endif LOG
2614322Seric 			message(Arpa_Info, "Warning: alias database out of date");
2624322Seric 		}
2634322Seric 	}
2644322Seric 
2654322Seric 
2664322Seric 	/*
2678437Seric 	**  If necessary, load the DBM file.
2684322Seric 	**	If running without DBM, load the symbol table.
2694322Seric 	*/
2704322Seric 
2714157Seric 	if (init)
2728437Seric 	{
27325522Seric #ifdef LOG
27425522Seric 		if (LogLevel >= 6)
27525522Seric 		{
27625522Seric 			extern char *username();
27725522Seric 
27825522Seric 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
27925522Seric 				automatic ? "auto" : "", username());
28025522Seric 		}
28125522Seric #endif LOG
2824157Seric 		readaliases(aliasfile, TRUE);
2838437Seric 	}
2844098Seric # else DBM
2854157Seric 	readaliases(aliasfile, init);
2864157Seric # endif DBM
2874157Seric }
2884157Seric /*
2894157Seric **  READALIASES -- read and process the alias file.
2904157Seric **
2914157Seric **	This routine implements the part of initaliases that occurs
2924157Seric **	when we are not going to use the DBM stuff.
2934157Seric **
2944157Seric **	Parameters:
2954157Seric **		aliasfile -- the pathname of the alias file master.
2964157Seric **		init -- if set, initialize the DBM stuff.
2974157Seric **
2984157Seric **	Returns:
2994157Seric **		none.
3004157Seric **
3014157Seric **	Side Effects:
3024157Seric **		Reads aliasfile into the symbol table.
3034157Seric **		Optionally, builds the .dir & .pag files.
3044157Seric */
3054157Seric 
3064157Seric static
3074157Seric readaliases(aliasfile, init)
3084157Seric 	char *aliasfile;
3094157Seric 	bool init;
3104157Seric {
3114098Seric 	register char *p;
3124098Seric 	char *rhs;
3134098Seric 	bool skipping;
3149368Seric 	int naliases, bytes, longest;
3159368Seric 	FILE *af;
31619784Seric 	int (*oldsigint)();
3174098Seric 	ADDRESS al, bl;
3184106Seric 	register STAB *s;
3199368Seric 	char line[BUFSIZ];
3204098Seric 
3214098Seric 	if ((af = fopen(aliasfile, "r")) == NULL)
3221515Seric 	{
3234098Seric # ifdef DEBUG
3247671Seric 		if (tTd(27, 1))
3254106Seric 			printf("Can't open %s\n", aliasfile);
3264098Seric # endif
3274098Seric 		errno = 0;
3284098Seric 		NoAlias++;
3294098Seric 		return;
3304098Seric 	}
3314314Seric 
33219784Seric # ifdef DBM
33319784Seric # ifdef FLOCK
33419784Seric 	/* see if someone else is rebuilding the alias file already */
33519784Seric 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
33619784Seric 	{
33719784Seric 		/* yes, they are -- wait until done and then return */
33819784Seric 		message(Arpa_Info, "Alias file is already being rebuilt");
33919784Seric 		if (OpMode != MD_INITALIAS)
34019784Seric 		{
34119784Seric 			/* wait for other rebuild to complete */
34219784Seric 			(void) flock(fileno(af), LOCK_EX);
34319784Seric 		}
34423108Seric 		(void) fclose(af);
34519784Seric 		errno = 0;
34619784Seric 		return;
34719784Seric 	}
34819784Seric # endif FLOCK
34919784Seric # endif DBM
35019784Seric 
3514314Seric 	/*
35219784Seric 	**  If initializing, create the new DBM files.
35319784Seric 	*/
35419784Seric 
35519784Seric 	if (init)
35619784Seric 	{
35719784Seric 		oldsigint = signal(SIGINT, SIG_IGN);
35819784Seric 		(void) strcpy(line, aliasfile);
35919784Seric 		(void) strcat(line, ".dir");
36019784Seric 		if (close(creat(line, DBMMODE)) < 0)
36119784Seric 		{
36219784Seric 			syserr("cannot make %s", line);
36319784Seric 			(void) signal(SIGINT, oldsigint);
36419784Seric 			return;
36519784Seric 		}
36619784Seric 		(void) strcpy(line, aliasfile);
36719784Seric 		(void) strcat(line, ".pag");
36819784Seric 		if (close(creat(line, DBMMODE)) < 0)
36919784Seric 		{
37019784Seric 			syserr("cannot make %s", line);
37119784Seric 			(void) signal(SIGINT, oldsigint);
37219784Seric 			return;
37319784Seric 		}
37426501Seric 		dbminit(aliasfile);
37519784Seric 	}
37619784Seric 
37719784Seric 	/*
3784314Seric 	**  Read and interpret lines
3794314Seric 	*/
3804314Seric 
3819368Seric 	FileName = aliasfile;
3829368Seric 	LineNumber = 0;
3834322Seric 	naliases = bytes = longest = 0;
3844098Seric 	skipping = FALSE;
3854098Seric 	while (fgets(line, sizeof (line), af) != NULL)
3864098Seric 	{
3874322Seric 		int lhssize, rhssize;
3884322Seric 
3899368Seric 		LineNumber++;
39025278Seric 		p = index(line, '\n');
39125278Seric 		if (p != NULL)
39225278Seric 			*p = '\0';
3934098Seric 		switch (line[0])
3944098Seric 		{
3954098Seric 		  case '#':
3964098Seric 		  case '\0':
3974098Seric 			skipping = FALSE;
3984098Seric 			continue;
3994065Seric 
4004098Seric 		  case ' ':
4014098Seric 		  case '\t':
4024098Seric 			if (!skipping)
4039368Seric 				syserr("Non-continuation line starts with space");
4044098Seric 			skipping = TRUE;
4054097Seric 			continue;
4064098Seric 		}
4074098Seric 		skipping = FALSE;
4081874Seric 
4094314Seric 		/*
4104314Seric 		**  Process the LHS
4114314Seric 		**	Find the final colon, and parse the address.
41216898Seric 		**	It should resolve to a local name -- this will
41316898Seric 		**	be checked later (we want to optionally do
41416898Seric 		**	parsing of the RHS first to maximize error
41516898Seric 		**	detection).
4164314Seric 		*/
4174314Seric 
4184098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
4194097Seric 			continue;
42016898Seric 		if (*p++ != ':')
4214098Seric 		{
4229368Seric 			syserr("missing colon");
4234097Seric 			continue;
4244098Seric 		}
42516898Seric 		if (parseaddr(line, &al, 1, ':') == NULL)
4264098Seric 		{
42716898Seric 			syserr("illegal alias name");
42816898Seric 			continue;
4294098Seric 		}
43016898Seric 		loweraddr(&al);
4314314Seric 
4324314Seric 		/*
4334314Seric 		**  Process the RHS.
4344314Seric 		**	'al' is the internal form of the LHS address.
4354314Seric 		**	'p' points to the text of the RHS.
4364314Seric 		*/
4374314Seric 
4384098Seric 		rhs = p;
4394098Seric 		for (;;)
4404098Seric 		{
4414098Seric 			register char c;
4421515Seric 
44325821Seric 			if (init && CheckAliases)
4444098Seric 			{
4454157Seric 				/* do parsing & compression of addresses */
44625278Seric 				while (*p != '\0')
4474098Seric 				{
44825278Seric 					extern char *DelimChar;
44925278Seric 
45025278Seric 					while (isspace(*p) || *p == ',')
4514157Seric 						p++;
45225278Seric 					if (*p == '\0')
45325278Seric 						break;
45425278Seric 					if (parseaddr(p, &bl, -1, ',') == NULL)
45525278Seric 						usrerr("%s... bad address", p);
45625278Seric 					p = DelimChar;
4574098Seric 				}
4584098Seric 			}
4594157Seric 			else
46015769Seric 			{
46116898Seric 				p = &p[strlen(p)];
46216898Seric 				if (p[-1] == '\n')
46316898Seric 					*--p = '\0';
46415769Seric 			}
4651515Seric 
4664098Seric 			/* see if there should be a continuation line */
4674106Seric 			c = fgetc(af);
4684106Seric 			if (!feof(af))
4694314Seric 				(void) ungetc(c, af);
4704106Seric 			if (c != ' ' && c != '\t')
4714098Seric 				break;
4724098Seric 
4734098Seric 			/* read continuation line */
4744098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
4754098Seric 				break;
4769368Seric 			LineNumber++;
4774098Seric 		}
47816898Seric 		if (al.q_mailer != LocalMailer)
47916898Seric 		{
48016898Seric 			syserr("cannot alias non-local names");
48116898Seric 			continue;
48216898Seric 		}
4834314Seric 
4844314Seric 		/*
4854314Seric 		**  Insert alias into symbol table or DBM file
4864314Seric 		*/
4874314Seric 
48816898Seric 		lhssize = strlen(al.q_user) + 1;
4894322Seric 		rhssize = strlen(rhs) + 1;
4904322Seric 
4914157Seric # ifdef DBM
4924157Seric 		if (init)
4934157Seric 		{
4944157Seric 			DATUM key, content;
4954157Seric 
4964322Seric 			key.dsize = lhssize;
4974157Seric 			key.dptr = al.q_user;
4984322Seric 			content.dsize = rhssize;
4994157Seric 			content.dptr = rhs;
5004157Seric 			store(key, content);
5014157Seric 		}
5024157Seric 		else
5034157Seric # endif DBM
5044157Seric 		{
5054157Seric 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
5064157Seric 			s->s_alias = newstr(rhs);
5074157Seric 		}
5084322Seric 
5094322Seric 		/* statistics */
5104322Seric 		naliases++;
5114322Seric 		bytes += lhssize + rhssize;
5124322Seric 		if (rhssize > longest)
5134322Seric 			longest = rhssize;
5141515Seric 	}
51519784Seric 
51619784Seric # ifdef DBM
51719784Seric 	if (init)
51819784Seric 	{
51919784Seric 		/* add the distinquished alias "@" */
52019784Seric 		DATUM key;
52119784Seric 
52219784Seric 		key.dsize = 2;
52319784Seric 		key.dptr = "@";
52419784Seric 		store(key, key);
52519784Seric 
52619784Seric 		/* restore the old signal */
52719784Seric 		(void) signal(SIGINT, oldsigint);
52819784Seric 	}
52919784Seric # endif DBM
53019784Seric 
53119784Seric 	/* closing the alias file drops the lock */
5324098Seric 	(void) fclose(af);
5336898Seric 	CurEnv->e_to = NULL;
5349368Seric 	FileName = NULL;
5357051Seric 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
5364322Seric 			naliases, longest, bytes);
53724944Seric # ifdef LOG
53824944Seric 	if (LogLevel >= 8)
53924944Seric 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
54024944Seric 			naliases, longest, bytes);
54124944Seric # endif LOG
542292Seric }
543292Seric /*
544292Seric **  FORWARD -- Try to forward mail
545292Seric **
546292Seric **	This is similar but not identical to aliasing.
547292Seric **
548292Seric **	Parameters:
5494314Seric **		user -- the name of the user who's mail we would like
5504314Seric **			to forward to.  It must have been verified --
5514314Seric **			i.e., the q_home field must have been filled
5524314Seric **			in.
5534999Seric **		sendq -- a pointer to the head of the send queue to
5544999Seric **			put this user's aliases in.
555292Seric **
556292Seric **	Returns:
5574098Seric **		none.
558292Seric **
559292Seric **	Side Effects:
5603185Seric **		New names are added to send queues.
561292Seric */
562292Seric 
5634999Seric forward(user, sendq)
5642966Seric 	ADDRESS *user;
5654999Seric 	ADDRESS **sendq;
566292Seric {
5674078Seric 	char buf[60];
5684536Seric 	extern bool safefile();
5694069Seric 
5704098Seric # ifdef DEBUG
5717671Seric 	if (tTd(27, 1))
5724098Seric 		printf("forward(%s)\n", user->q_paddr);
5734098Seric # endif DEBUG
5744098Seric 
5754594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
5764098Seric 		return;
5774314Seric # ifdef DEBUG
5784314Seric 	if (user->q_home == NULL)
5794314Seric 		syserr("forward: no home");
5804314Seric # endif DEBUG
5814069Seric 
5824069Seric 	/* good address -- look for .forward file in home */
5839368Seric 	define('z', user->q_home, CurEnv);
58416154Seric 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
5854536Seric 	if (!safefile(buf, user->q_uid, S_IREAD))
5864098Seric 		return;
5874069Seric 
5884069Seric 	/* we do have an address to forward to -- do it */
5894999Seric 	include(buf, "forwarding", user, sendq);
590292Seric }
591