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*8928Seric SCCSID(@(#)alias.c	3.39		10/28/82	(with DBM));
94106Seric # else DBM
10*8928Seric SCCSID(@(#)alias.c	3.39		10/28/82	(without DBM));
114106Seric # endif DBM
12402Seric 
13292Seric /*
14292Seric **  ALIAS -- Compute aliases.
15292Seric **
164098Seric **	Scans the file /usr/lib/aliases for a set of aliases.
173185Seric **	If found, it arranges to deliver to them.  Uses libdbm
183185Seric **	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 **	Files:
324098Seric **		/usr/lib/aliases -- the mail aliases.  The format is
33569Seric **			a series of lines of the form:
34569Seric **				alias:name1,name2,name3,...
35569Seric **			where 'alias' expands to all of
36569Seric **			'name[i]'.  Continuations begin with
37569Seric **			space or tab.
384098Seric **		/usr/lib/aliases.pag, /usr/lib/aliases.dir: libdbm version
391503Smark **			of alias file.  Keys are aliases, datums
401503Smark **			(data?) are name1,name2, ...
41292Seric **
42292Seric **	Notes:
43292Seric **		If NoAlias (the "-n" flag) is set, no aliasing is
44292Seric **			done.
45292Seric **
46292Seric **	Deficiencies:
47292Seric **		It should complain about names that are aliased to
48292Seric **			nothing.
49292Seric **		It is unsophisticated about line overflows.
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 
69292Seric 	if (NoAlias)
70292Seric 		return;
71292Seric # ifdef DEBUG
727671Seric 	if (tTd(27, 1))
734098Seric 		printf("alias(%s)\n", a->q_paddr);
74292Seric # endif
75292Seric 
764098Seric 	/* don't realias already aliased names */
774098Seric 	if (bitset(QDONTSEND, a->q_flags))
784098Seric 		return;
794098Seric 
806898Seric 	CurEnv->e_to = a->q_paddr;
814098Seric 
824314Seric 	/*
834314Seric 	**  Look up this name
844314Seric 	*/
854314Seric 
865701Seric 	p = aliaslookup(a->q_user);
874098Seric 	if (p == NULL)
884098Seric 		return;
89292Seric 
90292Seric 	/*
914098Seric 	**  Match on Alias.
924098Seric 	**	Deliver to the target list.
931515Seric 	*/
941515Seric 
954098Seric # ifdef DEBUG
967671Seric 	if (tTd(27, 1))
974098Seric 		printf("%s (%s, %s) aliased to %s\n",
984098Seric 		    a->q_paddr, a->q_host, a->q_user, p);
994098Seric # endif
1007051Seric 	message(Arpa_Info, "aliased to %s", p);
1014098Seric 	AliasLevel++;
1028076Seric 	sendto(p, a, sendq);
1034098Seric 	AliasLevel--;
1044098Seric }
1054098Seric /*
1065701Seric **  ALIASLOOKUP -- look up a name in the alias file.
1075701Seric **
1085701Seric **	Parameters:
1095701Seric **		name -- the name to look up.
1105701Seric **
1115701Seric **	Returns:
1125701Seric **		the value of name.
1135701Seric **		NULL if unknown.
1145701Seric **
1155701Seric **	Side Effects:
1165701Seric **		none.
1175701Seric **
1185701Seric **	Warnings:
1195701Seric **		The return value will be trashed across calls.
1205701Seric */
1215701Seric 
1225701Seric char *
1235701Seric aliaslookup(name)
1245701Seric 	char *name;
1255701Seric {
1265701Seric # ifdef DBM
1275701Seric 	DATUM rhs, lhs;
1285701Seric 
1295701Seric 	/* create a key for fetch */
1305701Seric 	lhs.dptr = name;
1315701Seric 	lhs.dsize = strlen(name) + 1;
1325701Seric 	rhs = fetch(lhs);
1335701Seric 	return (rhs.dptr);
1345701Seric # else DBM
1355701Seric 	register STAB *s;
1365701Seric 
1375701Seric 	s = stab(name, ST_ALIAS, ST_FIND);
1385701Seric 	if (s == NULL)
1395701Seric 		return (NULL);
1405701Seric 	return (s->s_alias);
1415701Seric # endif DBM
1425701Seric }
1435701Seric /*
1444098Seric **  INITALIASES -- initialize for aliasing
1454098Seric **
1464098Seric **	Very different depending on whether we are running DBM or not.
1474098Seric **
1484098Seric **	Parameters:
1494098Seric **		aliasfile -- location of aliases.
1504157Seric **		init -- if set and if DBM, initialize the DBM files.
1514098Seric **
1524098Seric **	Returns:
1534098Seric **		none.
1544098Seric **
1554098Seric **	Side Effects:
1564098Seric **		initializes aliases:
1574098Seric **		if DBM:  opens the database.
1584098Seric **		if ~DBM: reads the aliases into the symbol table.
1594098Seric */
1604098Seric 
1614157Seric # define DBMMODE	0666
1624157Seric 
1634157Seric initaliases(aliasfile, init)
1644098Seric 	char *aliasfile;
1654157Seric 	bool init;
1664098Seric {
1678437Seric 	int atcnt;
1684322Seric 	char buf[MAXNAME];
1694322Seric 	struct stat stb;
1704322Seric 	time_t modtime;
1718437Seric 	int (*oldsigint)();
1724322Seric 
1738437Seric 	if (stat(aliasfile, &stb) < 0)
1748437Seric 	{
1758437Seric 		NoAlias = TRUE;
1768437Seric 		return;
1778437Seric 	}
1788437Seric 
179*8928Seric # ifdef DBM
1804322Seric 	/*
1818437Seric 	**  Check to see that the alias file is complete.
1828437Seric 	**	If not, we will assume that someone died, and it is up
1838437Seric 	**	to us to rebuild it.
1848437Seric 	*/
1858437Seric 
1868437Seric 	dbminit(aliasfile);
187*8928Seric 	atcnt = 10;
188*8928Seric 	while (SafeAlias && !init && atcnt-- >= 0 && aliaslookup("@") == NULL)
1898437Seric 		sleep(30);
1908437Seric 
1918437Seric 	/*
1924322Seric 	**  See if the DBM version of the file is out of date with
1934322Seric 	**  the text version.  If so, go into 'init' mode automatically.
1944322Seric 	**	This only happens if our effective userid owns the DBM
1954325Seric 	**	version or if the mode of the database is 666 -- this
1964322Seric 	**	is an attempt to avoid protection problems.  Note the
1974322Seric 	**	unpalatable hack to see if the stat succeeded.
1984322Seric 	*/
1994322Seric 
2004322Seric 	modtime = stb.st_mtime;
2014322Seric 	(void) strcpy(buf, aliasfile);
2024322Seric 	(void) strcat(buf, ".pag");
2034322Seric 	stb.st_ino = 0;
2048437Seric 	if ((stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0) &&
2058437Seric 	    !init)
2064322Seric 	{
2074322Seric 		if (stb.st_ino != 0 &&
2084325Seric 		    ((stb.st_mode & 0666) == 0666 || stb.st_uid == geteuid()))
2094322Seric 		{
2104322Seric 			init = TRUE;
2117051Seric 			message(Arpa_Info, "rebuilding alias database");
2124322Seric 		}
2134322Seric 		else
2144322Seric 		{
2157051Seric 			bool oldverb = Verbose;
2167051Seric 
2177051Seric 			Verbose = TRUE;
2184322Seric 			message(Arpa_Info, "Warning: alias database out of date");
2197051Seric 			Verbose = oldverb;
2204322Seric 		}
2214322Seric 	}
2224322Seric 
2234322Seric 	/*
2244322Seric 	**  If initializing, create the new files.
2254322Seric 	**	We should lock the alias file here to prevent other
2264322Seric 	**	instantiations of sendmail from reading an incomplete
2274322Seric 	**	file -- or worse yet, doing a concurrent initialize.
2284322Seric 	*/
2294322Seric 
2304157Seric 	if (init)
2314157Seric 	{
2328437Seric 		oldsigint = signal(SIGINT, SIG_IGN);
2334157Seric 		(void) strcpy(buf, aliasfile);
2344157Seric 		(void) strcat(buf, ".dir");
2354157Seric 		if (close(creat(buf, DBMMODE)) < 0)
2364157Seric 		{
2374157Seric 			syserr("cannot make %s", buf);
2384157Seric 			return;
2394157Seric 		}
2404157Seric 		(void) strcpy(buf, aliasfile);
2414157Seric 		(void) strcat(buf, ".pag");
2424157Seric 		if (close(creat(buf, DBMMODE)) < 0)
2434157Seric 		{
2444157Seric 			syserr("cannot make %s", buf);
2454157Seric 			return;
2464157Seric 		}
2474157Seric 	}
2484322Seric 
2494322Seric 	/*
2508437Seric 	**  If necessary, load the DBM file.
2514322Seric 	**	If running without DBM, load the symbol table.
2528437Seric 	**	After loading the DBM file, add the distinquished alias "@".
2534322Seric 	*/
2544322Seric 
2554157Seric 	if (init)
2568437Seric 	{
2578437Seric 		DATUM key;
2588437Seric 
2594157Seric 		readaliases(aliasfile, TRUE);
2608437Seric 		key.dsize = 2;
2618437Seric 		key.dptr = "@";
2628437Seric 		store(key, key);
2638437Seric 		(void) signal(SIGINT, oldsigint);
2648437Seric 	}
2654098Seric # else DBM
2664157Seric 	readaliases(aliasfile, init);
2674157Seric # endif DBM
2684157Seric }
2694157Seric /*
2708437Seric **  DBMCLOSE -- close the dbm file.
2718437Seric **
2728437Seric **	This is highly implementation dependent.  It should be in the
2738437Seric **	DBM library rather than here.  So why isn't it?
2748437Seric **
2758437Seric **	This is really only needed to save file descriptors.  It can be
2768437Seric **	safely (??) replaced by the null routine.
2778437Seric **
2788437Seric **	Parameters:
2798437Seric **		none.
2808437Seric **
2818437Seric **	Returns:
2828437Seric **		none.
2838437Seric **
2848437Seric **	Side Effects:
2858437Seric **		Closes the DBM file.
2868437Seric */
2878437Seric 
2888437Seric dbmclose()
2898437Seric {
2908437Seric 	/* hack attack!! -- see comment above */
2918437Seric 	extern int pagf, dirf;
2928437Seric 
2938437Seric 	(void) close(pagf);
2948437Seric 	(void) close(dirf);
2958437Seric }
2968437Seric /*
2974157Seric **  READALIASES -- read and process the alias file.
2984157Seric **
2994157Seric **	This routine implements the part of initaliases that occurs
3004157Seric **	when we are not going to use the DBM stuff.
3014157Seric **
3024157Seric **	Parameters:
3034157Seric **		aliasfile -- the pathname of the alias file master.
3044157Seric **		init -- if set, initialize the DBM stuff.
3054157Seric **
3064157Seric **	Returns:
3074157Seric **		none.
3084157Seric **
3094157Seric **	Side Effects:
3104157Seric **		Reads aliasfile into the symbol table.
3114157Seric **		Optionally, builds the .dir & .pag files.
3124157Seric */
3134157Seric 
3144157Seric static
3154157Seric readaliases(aliasfile, init)
3164157Seric 	char *aliasfile;
3174157Seric 	bool init;
3184157Seric {
3194098Seric 	char line[BUFSIZ];
3204098Seric 	register char *p;
3214098Seric 	char *p2;
3224098Seric 	char *rhs;
3234098Seric 	bool skipping;
3244098Seric 	ADDRESS al, bl;
3254106Seric 	FILE *af;
3264106Seric 	int lineno;
3274106Seric 	register STAB *s;
3284322Seric 	int naliases, bytes, longest;
3294098Seric 
3304098Seric 	if ((af = fopen(aliasfile, "r")) == NULL)
3311515Seric 	{
3324098Seric # ifdef DEBUG
3337671Seric 		if (tTd(27, 1))
3344106Seric 			printf("Can't open %s\n", aliasfile);
3354098Seric # endif
3364098Seric 		errno = 0;
3374098Seric 		NoAlias++;
3384098Seric 		return;
3394098Seric 	}
3404314Seric 
3414314Seric 	/*
3424314Seric 	**  Read and interpret lines
3434314Seric 	*/
3444314Seric 
3454098Seric 	lineno = 0;
3464322Seric 	naliases = bytes = longest = 0;
3474098Seric 	skipping = FALSE;
3484098Seric 	while (fgets(line, sizeof (line), af) != NULL)
3494098Seric 	{
3504322Seric 		int lhssize, rhssize;
3514322Seric 
3524098Seric 		lineno++;
3534098Seric 		switch (line[0])
3544098Seric 		{
3554098Seric 		  case '#':
3564098Seric 		  case '\n':
3574098Seric 		  case '\0':
3584098Seric 			skipping = FALSE;
3594098Seric 			continue;
3604065Seric 
3614098Seric 		  case ' ':
3624098Seric 		  case '\t':
3634098Seric 			if (!skipping)
3644098Seric 				syserr("aliases: %d: Non-continuation line starts with space", lineno);
3654098Seric 			skipping = TRUE;
3664097Seric 			continue;
3674098Seric 		}
3684098Seric 		skipping = FALSE;
3691874Seric 
3704314Seric 		/*
3714314Seric 		**  Process the LHS
3724314Seric 		**	Find the final colon, and parse the address.
3734314Seric 		**	It should resolve to a local name -- this will
3744314Seric 		**	be checked later (we want to optionally do
3754314Seric 		**	parsing of the RHS first to maximize error
3764314Seric 		**	detection).
3774314Seric 		*/
3784314Seric 
3794098Seric 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
3804097Seric 			continue;
3814098Seric 		if (*p == '\0' || *p == '\n')
3824098Seric 		{
3834098Seric 		 syntaxerr:
3847048Seric 			syserr("aliases: %d: missing colon", lineno);
3854097Seric 			continue;
3864098Seric 		}
3874098Seric 		*p++ = '\0';
3884098Seric 		if (parse(line, &al, 1) == NULL)
3894098Seric 		{
3904098Seric 			*--p = ':';
3914098Seric 			goto syntaxerr;
3924098Seric 		}
3934314Seric 
3944314Seric 		/*
3954314Seric 		**  Process the RHS.
3964314Seric 		**	'al' is the internal form of the LHS address.
3974314Seric 		**	'p' points to the text of the RHS.
3984314Seric 		*/
3994314Seric 
4004098Seric 		rhs = p;
4014098Seric 		for (;;)
4024098Seric 		{
4034098Seric 			register char c;
4041515Seric 
4054157Seric 			if (init)
4064098Seric 			{
4074157Seric 				/* do parsing & compression of addresses */
4084098Seric 				c = *p;
4094157Seric 				while (c != '\0')
4104098Seric 				{
4114157Seric 					p2 = p;
4124157Seric 					while (*p != '\n' && *p != ',' && *p != '\0')
4134157Seric 						p++;
4144157Seric 					c = *p;
4154157Seric 					*p++ = '\0';
4164322Seric 					if (c == '\n')
4174322Seric 						c = '\0';
4184157Seric 					if (*p2 == '\0')
4194157Seric 					{
4204157Seric 						p[-1] = c;
4214157Seric 						continue;
4224157Seric 					}
4234314Seric 					(void) parse(p2, &bl, -1);
4244098Seric 					p[-1] = c;
4254157Seric 					while (isspace(*p))
4264157Seric 						p++;
4274098Seric 				}
4284098Seric 			}
4294157Seric 			else
4304157Seric 				p = &p[strlen(p)];
4311515Seric 
4324098Seric 			/* see if there should be a continuation line */
4334106Seric 			c = fgetc(af);
4344106Seric 			if (!feof(af))
4354314Seric 				(void) ungetc(c, af);
4364106Seric 			if (c != ' ' && c != '\t')
4374098Seric 				break;
4384098Seric 
4394098Seric 			/* read continuation line */
4404098Seric 			p--;
4414098Seric 			if (fgets(p, sizeof line - (p - line), af) == NULL)
4424098Seric 				break;
4434098Seric 			lineno++;
4444098Seric 		}
4454594Seric 		if (al.q_mailer != LocalMailer)
4464098Seric 		{
4474098Seric 			syserr("aliases: %d: cannot alias non-local names", lineno);
4484098Seric 			continue;
4494098Seric 		}
4504314Seric 
4514314Seric 		/*
4524314Seric 		**  Insert alias into symbol table or DBM file
4534314Seric 		*/
4544314Seric 
4554322Seric 		lhssize = strlen(al.q_user) + 1;
4564322Seric 		rhssize = strlen(rhs) + 1;
4574322Seric 
4584157Seric # ifdef DBM
4594157Seric 		if (init)
4604157Seric 		{
4614157Seric 			DATUM key, content;
4624157Seric 
4634322Seric 			key.dsize = lhssize;
4644157Seric 			key.dptr = al.q_user;
4654322Seric 			content.dsize = rhssize;
4664157Seric 			content.dptr = rhs;
4674157Seric 			store(key, content);
4684157Seric 		}
4694157Seric 		else
4704157Seric # endif DBM
4714157Seric 		{
4724157Seric 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
4734157Seric 			s->s_alias = newstr(rhs);
4744157Seric 		}
4754322Seric 
4764322Seric 		/* statistics */
4774322Seric 		naliases++;
4784322Seric 		bytes += lhssize + rhssize;
4794322Seric 		if (rhssize > longest)
4804322Seric 			longest = rhssize;
4811515Seric 	}
4824098Seric 	(void) fclose(af);
4836898Seric 	CurEnv->e_to = NULL;
4847051Seric 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
4854322Seric 			naliases, longest, bytes);
486292Seric }
487292Seric /*
488292Seric **  FORWARD -- Try to forward mail
489292Seric **
490292Seric **	This is similar but not identical to aliasing.
491292Seric **
492292Seric **	Parameters:
4934314Seric **		user -- the name of the user who's mail we would like
4944314Seric **			to forward to.  It must have been verified --
4954314Seric **			i.e., the q_home field must have been filled
4964314Seric **			in.
4974999Seric **		sendq -- a pointer to the head of the send queue to
4984999Seric **			put this user's aliases in.
499292Seric **
500292Seric **	Returns:
5014098Seric **		none.
502292Seric **
503292Seric **	Side Effects:
5043185Seric **		New names are added to send queues.
505292Seric */
506292Seric 
5074999Seric forward(user, sendq)
5082966Seric 	ADDRESS *user;
5094999Seric 	ADDRESS **sendq;
510292Seric {
5114078Seric 	char buf[60];
5124536Seric 	extern bool safefile();
5134069Seric 
5144098Seric # ifdef DEBUG
5157671Seric 	if (tTd(27, 1))
5164098Seric 		printf("forward(%s)\n", user->q_paddr);
5174098Seric # endif DEBUG
5184098Seric 
5194594Seric 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
5204098Seric 		return;
5214314Seric # ifdef DEBUG
5224314Seric 	if (user->q_home == NULL)
5234314Seric 		syserr("forward: no home");
5244314Seric # endif DEBUG
5254069Seric 
5264069Seric 	/* good address -- look for .forward file in home */
5274104Seric 	define('z', user->q_home);
5286973Seric 	expand("$z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
5294536Seric 	if (!safefile(buf, user->q_uid, S_IREAD))
5304098Seric 		return;
5314069Seric 
5324069Seric 	/* we do have an address to forward to -- do it */
5334999Seric 	include(buf, "forwarding", user, sendq);
534292Seric }
535