xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 33728)
122694Sdist /*
2*33728Sbostic  * Copyright (c) 1988 Regents of the University of California.
3*33728Sbostic  * All rights reserved.
4*33728Sbostic  *
5*33728Sbostic  * Redistribution and use in source and binary forms are permitted
6*33728Sbostic  * provided that this notice is preserved and that due credit is given
7*33728Sbostic  * to the University of California at Berkeley. The name of the University
8*33728Sbostic  * may not be used to endorse or promote products derived from this
9*33728Sbostic  * software without specific prior written permission. This software
10*33728Sbostic  * is provided ``as is'' without express or implied warranty.
11*33728Sbostic  *
12*33728Sbostic  *  Sendmail
13*33728Sbostic  *  Copyright (c) 1983  Eric P. Allman
14*33728Sbostic  *  Berkeley, California
15*33728Sbostic  */
1622694Sdist 
17*33728Sbostic #ifndef lint
18*33728Sbostic #ifdef DBM
19*33728Sbostic static char sccsid[] = "@(#)alias.c	5.14 (Berkeley) 03/13/88 (with DBM)";
20*33728Sbostic #else
21*33728Sbostic static char sccsid[] = "@(#)alias.c	5.14 (Berkeley) 03/13/88 (without DBM)";
22*33728Sbostic #endif
23*33728Sbostic #endif /* not lint */
24*33728Sbostic 
25292Seric # include <pwd.h>
264212Seric # include <sys/types.h>
274212Seric # include <sys/stat.h>
288437Seric # include <signal.h>
2919784Seric # include <errno.h>
303309Seric # include "sendmail.h"
3119784Seric # ifdef FLOCK
3219784Seric # include <sys/file.h>
3319784Seric # endif FLOCK
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 
79292Seric # ifdef DEBUG
807671Seric 	if (tTd(27, 1))
814098Seric 		printf("alias(%s)\n", a->q_paddr);
82292Seric # endif
83292Seric 
844098Seric 	/* don't realias already aliased names */
854098Seric 	if (bitset(QDONTSEND, a->q_flags))
864098Seric 		return;
874098Seric 
886898Seric 	CurEnv->e_to = a->q_paddr;
894098Seric 
904314Seric 	/*
914314Seric 	**  Look up this name
924314Seric 	*/
934314Seric 
9424944Seric 	if (NoAlias)
9524944Seric 		p = NULL;
9624944Seric 	else
9724944Seric 		p = aliaslookup(a->q_user);
984098Seric 	if (p == NULL)
994098Seric 		return;
100292Seric 
101292Seric 	/*
1024098Seric 	**  Match on Alias.
1034098Seric 	**	Deliver to the target list.
1041515Seric 	*/
1051515Seric 
1064098Seric # ifdef DEBUG
1077671Seric 	if (tTd(27, 1))
1084098Seric 		printf("%s (%s, %s) aliased to %s\n",
1094098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
1104098Seric # endif
1117051Seric 	message(Arpa_Info, "aliased to %s", p);
1124098Seric 	AliasLevel++;
1139614Seric 	sendtolist(p, a, sendq);
1144098Seric 	AliasLevel--;
1154098Seric }
1164098Seric /*
1175701Seric **  ALIASLOOKUP -- look up a name in the alias file.
1185701Seric **
1195701Seric **	Parameters:
1205701Seric **		name -- the name to look up.
1215701Seric **
1225701Seric **	Returns:
1235701Seric **		the value of name.
1245701Seric **		NULL if unknown.
1255701Seric **
1265701Seric **	Side Effects:
1275701Seric **		none.
1285701Seric **
1295701Seric **	Warnings:
1305701Seric **		The return value will be trashed across calls.
1315701Seric */
1325701Seric 
1335701Seric char *
1345701Seric aliaslookup(name)
1355701Seric 	char *name;
1365701Seric {
1375701Seric # ifdef DBM
1385701Seric 	DATUM rhs, lhs;
1395701Seric 
1405701Seric 	/* create a key for fetch */
1415701Seric 	lhs.dptr = name;
1425701Seric 	lhs.dsize = strlen(name) + 1;
1435701Seric 	rhs = fetch(lhs);
1445701Seric 	return (rhs.dptr);
1455701Seric # else DBM
1465701Seric 	register STAB *s;
1475701Seric 
1485701Seric 	s = stab(name, ST_ALIAS, ST_FIND);
1495701Seric 	if (s == NULL)
1505701Seric 		return (NULL);
1515701Seric 	return (s->s_alias);
1525701Seric # endif DBM
1535701Seric }
1545701Seric /*
1554098Seric **  INITALIASES -- initialize for aliasing
1564098Seric **
1574098Seric **	Very different depending on whether we are running DBM or not.
1584098Seric **
1594098Seric **	Parameters:
1604098Seric **		aliasfile -- location of aliases.
1614157Seric **		init -- if set and if DBM, initialize the DBM files.
1624098Seric **
1634098Seric **	Returns:
1644098Seric **		none.
1654098Seric **
1664098Seric **	Side Effects:
1674098Seric **		initializes aliases:
1684098Seric **		if DBM:  opens the database.
1694098Seric **		if ~DBM: reads the aliases into the symbol table.
1704098Seric */
1714098Seric 
1724157Seric # define DBMMODE	0666
1734157Seric 
1744157Seric initaliases(aliasfile, init)
1754098Seric 	char *aliasfile;
1764157Seric 	bool init;
1774098Seric {
1789368Seric #ifdef DBM
1798437Seric 	int atcnt;
18025522Seric 	time_t modtime;
18125522Seric 	bool automatic = FALSE;
1824322Seric 	char buf[MAXNAME];
1839368Seric #endif DBM
1849368Seric 	struct stat stb;
18527176Seric 	static bool initialized = FALSE;
1864322Seric 
18727176Seric 	if (initialized)
18827176Seric 		return;
18927176Seric 	initialized = TRUE;
19027176Seric 
19117984Seric 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
1928437Seric 	{
19325522Seric 		if (aliasfile != NULL && init)
19425522Seric 			syserr("Cannot open %s", aliasfile);
1958437Seric 		NoAlias = TRUE;
19611937Seric 		errno = 0;
1978437Seric 		return;
1988437Seric 	}
1998437Seric 
2008928Seric # ifdef DBM
2014322Seric 	/*
2028437Seric 	**  Check to see that the alias file is complete.
2038437Seric 	**	If not, we will assume that someone died, and it is up
2048437Seric 	**	to us to rebuild it.
2058437Seric 	*/
2068437Seric 
20725689Seric 	if (!init)
20825689Seric 		dbminit(aliasfile);
20917471Seric 	atcnt = SafeAlias * 2;
21017471Seric 	if (atcnt > 0)
21117471Seric 	{
21217471Seric 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
21325689Seric 		{
21425689Seric 			/*
21525689Seric 			**  Reinitialize alias file in case the new
21625689Seric 			**  one is mv'ed in instead of cp'ed in.
21725689Seric 			**
21825689Seric 			**	Only works with new DBM -- old one will
21925689Seric 			**	just consume file descriptors forever.
22025689Seric 			**	If you have a dbmclose() it can be
22125689Seric 			**	added before the sleep(30).
22225689Seric 			*/
22325689Seric 
22417471Seric 			sleep(30);
22525689Seric # ifdef NDBM
22625689Seric 			dbminit(aliasfile);
22725689Seric # endif NDBM
22825689Seric 		}
22917471Seric 	}
23017471Seric 	else
23117471Seric 		atcnt = 1;
2328437Seric 
2338437Seric 	/*
2344322Seric 	**  See if the DBM version of the file is out of date with
2354322Seric 	**  the text version.  If so, go into 'init' mode automatically.
2364322Seric 	**	This only happens if our effective userid owns the DBM
2374325Seric 	**	version or if the mode of the database is 666 -- this
2384322Seric 	**	is an attempt to avoid protection problems.  Note the
2394322Seric 	**	unpalatable hack to see if the stat succeeded.
2404322Seric 	*/
2414322Seric 
2424322Seric 	modtime = stb.st_mtime;
2434322Seric 	(void) strcpy(buf, aliasfile);
2444322Seric 	(void) strcat(buf, ".pag");
2454322Seric 	stb.st_ino = 0;
24619039Seric 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
2474322Seric 	{
24811937Seric 		errno = 0;
2499150Seric 		if (AutoRebuild && stb.st_ino != 0 &&
2509368Seric 		    ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
2514322Seric 		{
2524322Seric 			init = TRUE;
25325522Seric 			automatic = TRUE;
2547051Seric 			message(Arpa_Info, "rebuilding alias database");
25524944Seric #ifdef LOG
25624944Seric 			if (LogLevel >= 7)
25724944Seric 				syslog(LOG_INFO, "rebuilding alias database");
25824944Seric #endif LOG
2594322Seric 		}
2604322Seric 		else
2614322Seric 		{
26219039Seric #ifdef LOG
26324944Seric 			if (LogLevel >= 7)
26424944Seric 				syslog(LOG_INFO, "alias database out of date");
26519039Seric #endif LOG
2664322Seric 			message(Arpa_Info, "Warning: alias database out of date");
2674322Seric 		}
2684322Seric 	}
2694322Seric 
2704322Seric 
2714322Seric 	/*
2728437Seric 	**  If necessary, load the DBM file.
2734322Seric 	**	If running without DBM, load the symbol table.
2744322Seric 	*/
2754322Seric 
2764157Seric 	if (init)
2778437Seric 	{
27825522Seric #ifdef LOG
27925522Seric 		if (LogLevel >= 6)
28025522Seric 		{
28125522Seric 			extern char *username();
28225522Seric 
28325522Seric 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
28425522Seric 				automatic ? "auto" : "", username());
28525522Seric 		}
28625522Seric #endif LOG
2874157Seric 		readaliases(aliasfile, TRUE);
2888437Seric 	}
2894098Seric # else DBM
2904157Seric 	readaliases(aliasfile, init);
2914157Seric # endif DBM
2924157Seric }
2934157Seric /*
2944157Seric **  READALIASES -- read and process the alias file.
2954157Seric **
2964157Seric **	This routine implements the part of initaliases that occurs
2974157Seric **	when we are not going to use the DBM stuff.
2984157Seric **
2994157Seric **	Parameters:
3004157Seric **		aliasfile -- the pathname of the alias file master.
3014157Seric **		init -- if set, initialize the DBM stuff.
3024157Seric **
3034157Seric **	Returns:
3044157Seric **		none.
3054157Seric **
3064157Seric **	Side Effects:
3074157Seric **		Reads aliasfile into the symbol table.
3084157Seric **		Optionally, builds the .dir & .pag files.
3094157Seric */
3104157Seric 
3114157Seric static
3124157Seric readaliases(aliasfile, init)
3134157Seric 	char *aliasfile;
3144157Seric 	bool init;
3154157Seric {
3164098Seric 	register char *p;
3174098Seric 	char *rhs;
3184098Seric 	bool skipping;
3199368Seric 	int naliases, bytes, longest;
3209368Seric 	FILE *af;
32119784Seric 	int (*oldsigint)();
3224098Seric 	ADDRESS al, bl;
3234106Seric 	register STAB *s;
3249368Seric 	char line[BUFSIZ];
3254098Seric 
3264098Seric 	if ((af = fopen(aliasfile, "r")) == NULL)
3271515Seric 	{
3284098Seric # ifdef DEBUG
3297671Seric 		if (tTd(27, 1))
3304106Seric 			printf("Can't open %s\n", aliasfile);
3314098Seric # endif
3324098Seric 		errno = 0;
3334098Seric 		NoAlias++;
3344098Seric 		return;
3354098Seric 	}
3364314Seric 
33719784Seric # ifdef DBM
33819784Seric # ifdef FLOCK
33919784Seric 	/* see if someone else is rebuilding the alias file already */
34019784Seric 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
34119784Seric 	{
34219784Seric 		/* yes, they are -- wait until done and then return */
34319784Seric 		message(Arpa_Info, "Alias file is already being rebuilt");
34419784Seric 		if (OpMode != MD_INITALIAS)
34519784Seric 		{
34619784Seric 			/* wait for other rebuild to complete */
34719784Seric 			(void) flock(fileno(af), LOCK_EX);
34819784Seric 		}
34923108Seric 		(void) fclose(af);
35019784Seric 		errno = 0;
35119784Seric 		return;
35219784Seric 	}
35319784Seric # endif FLOCK
35419784Seric # endif DBM
35519784Seric 
3564314Seric 	/*
35719784Seric 	**  If initializing, create the new DBM files.
35819784Seric 	*/
35919784Seric 
36019784Seric 	if (init)
36119784Seric 	{
36219784Seric 		oldsigint = signal(SIGINT, SIG_IGN);
36319784Seric 		(void) strcpy(line, aliasfile);
36419784Seric 		(void) strcat(line, ".dir");
36519784Seric 		if (close(creat(line, DBMMODE)) < 0)
36619784Seric 		{
36719784Seric 			syserr("cannot make %s", line);
36819784Seric 			(void) signal(SIGINT, oldsigint);
36919784Seric 			return;
37019784Seric 		}
37119784Seric 		(void) strcpy(line, aliasfile);
37219784Seric 		(void) strcat(line, ".pag");
37319784Seric 		if (close(creat(line, DBMMODE)) < 0)
37419784Seric 		{
37519784Seric 			syserr("cannot make %s", line);
37619784Seric 			(void) signal(SIGINT, oldsigint);
37719784Seric 			return;
37819784Seric 		}
37926501Seric 		dbminit(aliasfile);
38019784Seric 	}
38119784Seric 
38219784Seric 	/*
3834314Seric 	**  Read and interpret lines
3844314Seric 	*/
3854314Seric 
3869368Seric 	FileName = aliasfile;
3879368Seric 	LineNumber = 0;
3884322Seric 	naliases = bytes = longest = 0;
3894098Seric 	skipping = FALSE;
3904098Seric 	while (fgets(line, sizeof (line), af) != NULL)
3914098Seric 	{
3924322Seric 		int lhssize, rhssize;
3934322Seric 
3949368Seric 		LineNumber++;
39525278Seric 		p = index(line, '\n');
39625278Seric 		if (p != NULL)
39725278Seric 			*p = '\0';
3984098Seric 		switch (line[0])
3994098Seric 		{
4004098Seric 		  case '#':
4014098Seric 		  case '\0':
4024098Seric 			skipping = FALSE;
4034098Seric 			continue;
4044065Seric 
4054098Seric 		  case ' ':
4064098Seric 		  case '\t':
4074098Seric 			if (!skipping)
4089368Seric 				syserr("Non-continuation line starts with space");
4094098Seric 			skipping = TRUE;
4104097Seric 			continue;
4114098Seric 		}
4124098Seric 		skipping = FALSE;
4131874Seric 
4144314Seric 		/*
4154314Seric 		**  Process the LHS
4164314Seric 		**	Find the final colon, and parse the address.
41716898Seric 		**	It should resolve to a local name -- this will
41816898Seric 		**	be checked later (we want to optionally do
41916898Seric 		**	parsing of the RHS first to maximize error
42016898Seric 		**	detection).
4214314Seric 		*/
4224314Seric 
4234098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
4244097Seric 			continue;
42516898Seric 		if (*p++ != ':')
4264098Seric 		{
4279368Seric 			syserr("missing colon");
4284097Seric 			continue;
4294098Seric 		}
43016898Seric 		if (parseaddr(line, &al, 1, ':') == NULL)
4314098Seric 		{
43216898Seric 			syserr("illegal alias name");
43316898Seric 			continue;
4344098Seric 		}
43516898Seric 		loweraddr(&al);
4364314Seric 
4374314Seric 		/*
4384314Seric 		**  Process the RHS.
4394314Seric 		**	'al' is the internal form of the LHS address.
4404314Seric 		**	'p' points to the text of the RHS.
4414314Seric 		*/
4424314Seric 
4434098Seric 		rhs = p;
4444098Seric 		for (;;)
4454098Seric 		{
4464098Seric 			register char c;
4471515Seric 
44825821Seric 			if (init && CheckAliases)
4494098Seric 			{
4504157Seric 				/* do parsing & compression of addresses */
45125278Seric 				while (*p != '\0')
4524098Seric 				{
45325278Seric 					extern char *DelimChar;
45425278Seric 
45525278Seric 					while (isspace(*p) || *p == ',')
4564157Seric 						p++;
45725278Seric 					if (*p == '\0')
45825278Seric 						break;
45925278Seric 					if (parseaddr(p, &bl, -1, ',') == NULL)
46025278Seric 						usrerr("%s... bad address", p);
46125278Seric 					p = DelimChar;
4624098Seric 				}
4634098Seric 			}
4644157Seric 			else
46515769Seric 			{
46616898Seric 				p = &p[strlen(p)];
46716898Seric 				if (p[-1] == '\n')
46816898Seric 					*--p = '\0';
46915769Seric 			}
4701515Seric 
4714098Seric 			/* see if there should be a continuation line */
4724106Seric 			c = fgetc(af);
4734106Seric 			if (!feof(af))
4744314Seric 				(void) ungetc(c, af);
4754106Seric 			if (c != ' ' && c != '\t')
4764098Seric 				break;
4774098Seric 
4784098Seric 			/* read continuation line */
4794098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
4804098Seric 				break;
4819368Seric 			LineNumber++;
4824098Seric 		}
48316898Seric 		if (al.q_mailer != LocalMailer)
48416898Seric 		{
48516898Seric 			syserr("cannot alias non-local names");
48616898Seric 			continue;
48716898Seric 		}
4884314Seric 
4894314Seric 		/*
4904314Seric 		**  Insert alias into symbol table or DBM file
4914314Seric 		*/
4924314Seric 
49316898Seric 		lhssize = strlen(al.q_user) + 1;
4944322Seric 		rhssize = strlen(rhs) + 1;
4954322Seric 
4964157Seric # ifdef DBM
4974157Seric 		if (init)
4984157Seric 		{
4994157Seric 			DATUM key, content;
5004157Seric 
5014322Seric 			key.dsize = lhssize;
5024157Seric 			key.dptr = al.q_user;
5034322Seric 			content.dsize = rhssize;
5044157Seric 			content.dptr = rhs;
5054157Seric 			store(key, content);
5064157Seric 		}
5074157Seric 		else
5084157Seric # endif DBM
5094157Seric 		{
5104157Seric 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
5114157Seric 			s->s_alias = newstr(rhs);
5124157Seric 		}
5134322Seric 
5144322Seric 		/* statistics */
5154322Seric 		naliases++;
5164322Seric 		bytes += lhssize + rhssize;
5174322Seric 		if (rhssize > longest)
5184322Seric 			longest = rhssize;
5191515Seric 	}
52019784Seric 
52119784Seric # ifdef DBM
52219784Seric 	if (init)
52319784Seric 	{
52419784Seric 		/* add the distinquished alias "@" */
52519784Seric 		DATUM key;
52619784Seric 
52719784Seric 		key.dsize = 2;
52819784Seric 		key.dptr = "@";
52919784Seric 		store(key, key);
53019784Seric 
53119784Seric 		/* restore the old signal */
53219784Seric 		(void) signal(SIGINT, oldsigint);
53319784Seric 	}
53419784Seric # endif DBM
53519784Seric 
53619784Seric 	/* closing the alias file drops the lock */
5374098Seric 	(void) fclose(af);
5386898Seric 	CurEnv->e_to = NULL;
5399368Seric 	FileName = NULL;
5407051Seric 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
5414322Seric 			naliases, longest, bytes);
54224944Seric # ifdef LOG
54324944Seric 	if (LogLevel >= 8)
54424944Seric 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
54524944Seric 			naliases, longest, bytes);
54624944Seric # endif LOG
547292Seric }
548292Seric /*
549292Seric **  FORWARD -- Try to forward mail
550292Seric **
551292Seric **	This is similar but not identical to aliasing.
552292Seric **
553292Seric **	Parameters:
5544314Seric **		user -- the name of the user who's mail we would like
5554314Seric **			to forward to.  It must have been verified --
5564314Seric **			i.e., the q_home field must have been filled
5574314Seric **			in.
5584999Seric **		sendq -- a pointer to the head of the send queue to
5594999Seric **			put this user's aliases in.
560292Seric **
561292Seric **	Returns:
5624098Seric **		none.
563292Seric **
564292Seric **	Side Effects:
5653185Seric **		New names are added to send queues.
566292Seric */
567292Seric 
5684999Seric forward(user, sendq)
5692966Seric 	ADDRESS *user;
5704999Seric 	ADDRESS **sendq;
571292Seric {
5724078Seric 	char buf[60];
5734536Seric 	extern bool safefile();
5744069Seric 
5754098Seric # ifdef DEBUG
5767671Seric 	if (tTd(27, 1))
5774098Seric 		printf("forward(%s)\n", user->q_paddr);
5784098Seric # endif DEBUG
5794098Seric 
5804594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
5814098Seric 		return;
5824314Seric # ifdef DEBUG
5834314Seric 	if (user->q_home == NULL)
5844314Seric 		syserr("forward: no home");
5854314Seric # endif DEBUG
5864069Seric 
5874069Seric 	/* good address -- look for .forward file in home */
5889368Seric 	define('z', user->q_home, CurEnv);
58916154Seric 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
5904536Seric 	if (!safefile(buf, user->q_uid, S_IREAD))
5914098Seric 		return;
5924069Seric 
5934069Seric 	/* we do have an address to forward to -- do it */
5944999Seric 	include(buf, "forwarding", user, sendq);
595292Seric }
596