1 # include <pwd.h>
2 # include <sys/types.h>
3 # include <sys/stat.h>
4 # include <signal.h>
5 # include "sendmail.h"
6 
7 # ifdef DBM
8 SCCSID(@(#)alias.c	3.41		11/24/82	(with DBM));
9 # else DBM
10 SCCSID(@(#)alias.c	3.41		11/24/82	(without DBM));
11 # endif DBM
12 
13 /*
14 **  ALIAS -- Compute aliases.
15 **
16 **	Scans the file /usr/lib/aliases for a set of aliases.
17 **	If found, it arranges to deliver to them.  Uses libdbm
18 **	database if -DDBM.
19 **
20 **	Parameters:
21 **		a -- address to alias.
22 **		sendq -- a pointer to the head of the send queue
23 **			to put the aliases in.
24 **
25 **	Returns:
26 **		none
27 **
28 **	Side Effects:
29 **		Aliases found are expanded.
30 **
31 **	Files:
32 **		/usr/lib/aliases -- the mail aliases.  The format is
33 **			a series of lines of the form:
34 **				alias:name1,name2,name3,...
35 **			where 'alias' expands to all of
36 **			'name[i]'.  Continuations begin with
37 **			space or tab.
38 **		/usr/lib/aliases.pag, /usr/lib/aliases.dir: libdbm version
39 **			of alias file.  Keys are aliases, datums
40 **			(data?) are name1,name2, ...
41 **
42 **	Notes:
43 **		If NoAlias (the "-n" flag) is set, no aliasing is
44 **			done.
45 **
46 **	Deficiencies:
47 **		It should complain about names that are aliased to
48 **			nothing.
49 **		It is unsophisticated about line overflows.
50 */
51 
52 
53 #ifdef DBM
54 typedef struct
55 {
56 	char	*dptr;
57 	int	dsize;
58 } DATUM;
59 extern DATUM fetch();
60 #endif DBM
61 
62 alias(a, sendq)
63 	register ADDRESS *a;
64 	ADDRESS **sendq;
65 {
66 	register char *p;
67 	extern char *aliaslookup();
68 
69 	if (NoAlias)
70 		return;
71 # ifdef DEBUG
72 	if (tTd(27, 1))
73 		printf("alias(%s)\n", a->q_paddr);
74 # endif
75 
76 	/* don't realias already aliased names */
77 	if (bitset(QDONTSEND, a->q_flags))
78 		return;
79 
80 	CurEnv->e_to = a->q_paddr;
81 
82 	/*
83 	**  Look up this name
84 	*/
85 
86 	p = aliaslookup(a->q_user);
87 	if (p == NULL)
88 		return;
89 
90 	/*
91 	**  Match on Alias.
92 	**	Deliver to the target list.
93 	*/
94 
95 # ifdef DEBUG
96 	if (tTd(27, 1))
97 		printf("%s (%s, %s) aliased to %s\n",
98 		    a->q_paddr, a->q_host, a->q_user, p);
99 # endif
100 	message(Arpa_Info, "aliased to %s", p);
101 	AliasLevel++;
102 	sendto(p, a, sendq);
103 	AliasLevel--;
104 }
105 /*
106 **  ALIASLOOKUP -- look up a name in the alias file.
107 **
108 **	Parameters:
109 **		name -- the name to look up.
110 **
111 **	Returns:
112 **		the value of name.
113 **		NULL if unknown.
114 **
115 **	Side Effects:
116 **		none.
117 **
118 **	Warnings:
119 **		The return value will be trashed across calls.
120 */
121 
122 char *
123 aliaslookup(name)
124 	char *name;
125 {
126 # ifdef DBM
127 	DATUM rhs, lhs;
128 
129 	/* create a key for fetch */
130 	lhs.dptr = name;
131 	lhs.dsize = strlen(name) + 1;
132 	rhs = fetch(lhs);
133 	return (rhs.dptr);
134 # else DBM
135 	register STAB *s;
136 
137 	s = stab(name, ST_ALIAS, ST_FIND);
138 	if (s == NULL)
139 		return (NULL);
140 	return (s->s_alias);
141 # endif DBM
142 }
143 /*
144 **  INITALIASES -- initialize for aliasing
145 **
146 **	Very different depending on whether we are running DBM or not.
147 **
148 **	Parameters:
149 **		aliasfile -- location of aliases.
150 **		init -- if set and if DBM, initialize the DBM files.
151 **
152 **	Returns:
153 **		none.
154 **
155 **	Side Effects:
156 **		initializes aliases:
157 **		if DBM:  opens the database.
158 **		if ~DBM: reads the aliases into the symbol table.
159 */
160 
161 # define DBMMODE	0666
162 
163 initaliases(aliasfile, init)
164 	char *aliasfile;
165 	bool init;
166 {
167 	int atcnt;
168 	char buf[MAXNAME];
169 	struct stat stb;
170 	time_t modtime;
171 	int (*oldsigint)();
172 
173 	if (stat(aliasfile, &stb) < 0)
174 	{
175 		NoAlias = TRUE;
176 		return;
177 	}
178 
179 # ifdef DBM
180 	/*
181 	**  Check to see that the alias file is complete.
182 	**	If not, we will assume that someone died, and it is up
183 	**	to us to rebuild it.
184 	*/
185 
186 	dbminit(aliasfile);
187 	atcnt = 10;
188 	while (SafeAlias && !init && atcnt-- >= 0 && aliaslookup("@") == NULL)
189 		sleep(30);
190 
191 	/*
192 	**  See if the DBM version of the file is out of date with
193 	**  the text version.  If so, go into 'init' mode automatically.
194 	**	This only happens if our effective userid owns the DBM
195 	**	version or if the mode of the database is 666 -- this
196 	**	is an attempt to avoid protection problems.  Note the
197 	**	unpalatable hack to see if the stat succeeded.
198 	*/
199 
200 	modtime = stb.st_mtime;
201 	(void) strcpy(buf, aliasfile);
202 	(void) strcat(buf, ".pag");
203 	stb.st_ino = 0;
204 	if (!init && (atcnt < 0 || stat(buf, &stb) < 0 || stb.st_mtime < modtime))
205 	{
206 		if (AutoRebuild && stb.st_ino != 0 &&
207 		    ((stb.st_mode & 0666) == 0666 || stb.st_uid == geteuid()))
208 		{
209 			init = TRUE;
210 			message(Arpa_Info, "rebuilding alias database");
211 		}
212 		else
213 		{
214 			bool oldverb = Verbose;
215 
216 			Verbose = TRUE;
217 			message(Arpa_Info, "Warning: alias database out of date");
218 			Verbose = oldverb;
219 		}
220 	}
221 
222 	/*
223 	**  If initializing, create the new files.
224 	**	We should lock the alias file here to prevent other
225 	**	instantiations of sendmail from reading an incomplete
226 	**	file -- or worse yet, doing a concurrent initialize.
227 	*/
228 
229 	if (init)
230 	{
231 		oldsigint = signal(SIGINT, SIG_IGN);
232 		(void) strcpy(buf, aliasfile);
233 		(void) strcat(buf, ".dir");
234 		if (close(creat(buf, DBMMODE)) < 0)
235 		{
236 			syserr("cannot make %s", buf);
237 			return;
238 		}
239 		(void) strcpy(buf, aliasfile);
240 		(void) strcat(buf, ".pag");
241 		if (close(creat(buf, DBMMODE)) < 0)
242 		{
243 			syserr("cannot make %s", buf);
244 			return;
245 		}
246 	}
247 
248 	/*
249 	**  If necessary, load the DBM file.
250 	**	If running without DBM, load the symbol table.
251 	**	After loading the DBM file, add the distinquished alias "@".
252 	*/
253 
254 	if (init)
255 	{
256 		DATUM key;
257 
258 		readaliases(aliasfile, TRUE);
259 		key.dsize = 2;
260 		key.dptr = "@";
261 		store(key, key);
262 		(void) signal(SIGINT, oldsigint);
263 	}
264 # else DBM
265 	readaliases(aliasfile, init);
266 # endif DBM
267 }
268 /*
269 **  READALIASES -- read and process the alias file.
270 **
271 **	This routine implements the part of initaliases that occurs
272 **	when we are not going to use the DBM stuff.
273 **
274 **	Parameters:
275 **		aliasfile -- the pathname of the alias file master.
276 **		init -- if set, initialize the DBM stuff.
277 **
278 **	Returns:
279 **		none.
280 **
281 **	Side Effects:
282 **		Reads aliasfile into the symbol table.
283 **		Optionally, builds the .dir & .pag files.
284 */
285 
286 static
287 readaliases(aliasfile, init)
288 	char *aliasfile;
289 	bool init;
290 {
291 	char line[BUFSIZ];
292 	register char *p;
293 	char *p2;
294 	char *rhs;
295 	bool skipping;
296 	ADDRESS al, bl;
297 	FILE *af;
298 	int lineno;
299 	register STAB *s;
300 	int naliases, bytes, longest;
301 
302 	if ((af = fopen(aliasfile, "r")) == NULL)
303 	{
304 # ifdef DEBUG
305 		if (tTd(27, 1))
306 			printf("Can't open %s\n", aliasfile);
307 # endif
308 		errno = 0;
309 		NoAlias++;
310 		return;
311 	}
312 
313 	/*
314 	**  Read and interpret lines
315 	*/
316 
317 	lineno = 0;
318 	naliases = bytes = longest = 0;
319 	skipping = FALSE;
320 	while (fgets(line, sizeof (line), af) != NULL)
321 	{
322 		int lhssize, rhssize;
323 
324 		lineno++;
325 		switch (line[0])
326 		{
327 		  case '#':
328 		  case '\n':
329 		  case '\0':
330 			skipping = FALSE;
331 			continue;
332 
333 		  case ' ':
334 		  case '\t':
335 			if (!skipping)
336 				syserr("aliases: %d: Non-continuation line starts with space", lineno);
337 			skipping = TRUE;
338 			continue;
339 		}
340 		skipping = FALSE;
341 
342 		/*
343 		**  Process the LHS
344 		**	Find the final colon, and parse the address.
345 		**	It should resolve to a local name -- this will
346 		**	be checked later (we want to optionally do
347 		**	parsing of the RHS first to maximize error
348 		**	detection).
349 		*/
350 
351 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
352 			continue;
353 		if (*p == '\0' || *p == '\n')
354 		{
355 		 syntaxerr:
356 			syserr("aliases: %d: missing colon", lineno);
357 			continue;
358 		}
359 		*p++ = '\0';
360 		if (parse(line, &al, 1) == NULL)
361 		{
362 			*--p = ':';
363 			goto syntaxerr;
364 		}
365 
366 		/*
367 		**  Process the RHS.
368 		**	'al' is the internal form of the LHS address.
369 		**	'p' points to the text of the RHS.
370 		*/
371 
372 		rhs = p;
373 		for (;;)
374 		{
375 			register char c;
376 
377 			if (init)
378 			{
379 				/* do parsing & compression of addresses */
380 				c = *p;
381 				while (c != '\0')
382 				{
383 					p2 = p;
384 					while (*p != '\n' && *p != ',' && *p != '\0')
385 						p++;
386 					c = *p;
387 					*p++ = '\0';
388 					if (c == '\n')
389 						c = '\0';
390 					if (*p2 == '\0')
391 					{
392 						p[-1] = c;
393 						continue;
394 					}
395 					(void) parse(p2, &bl, -1);
396 					p[-1] = c;
397 					while (isspace(*p))
398 						p++;
399 				}
400 			}
401 			else
402 				p = &p[strlen(p)];
403 
404 			/* see if there should be a continuation line */
405 			c = fgetc(af);
406 			if (!feof(af))
407 				(void) ungetc(c, af);
408 			if (c != ' ' && c != '\t')
409 				break;
410 
411 			/* read continuation line */
412 			p--;
413 			if (fgets(p, sizeof line - (p - line), af) == NULL)
414 				break;
415 			lineno++;
416 		}
417 		if (al.q_mailer != LocalMailer)
418 		{
419 			syserr("aliases: %d: cannot alias non-local names", lineno);
420 			continue;
421 		}
422 
423 		/*
424 		**  Insert alias into symbol table or DBM file
425 		*/
426 
427 		lhssize = strlen(al.q_user) + 1;
428 		rhssize = strlen(rhs) + 1;
429 
430 # ifdef DBM
431 		if (init)
432 		{
433 			DATUM key, content;
434 
435 			key.dsize = lhssize;
436 			key.dptr = al.q_user;
437 			content.dsize = rhssize;
438 			content.dptr = rhs;
439 			store(key, content);
440 		}
441 		else
442 # endif DBM
443 		{
444 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
445 			s->s_alias = newstr(rhs);
446 		}
447 
448 		/* statistics */
449 		naliases++;
450 		bytes += lhssize + rhssize;
451 		if (rhssize > longest)
452 			longest = rhssize;
453 	}
454 	(void) fclose(af);
455 	CurEnv->e_to = NULL;
456 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
457 			naliases, longest, bytes);
458 }
459 /*
460 **  FORWARD -- Try to forward mail
461 **
462 **	This is similar but not identical to aliasing.
463 **
464 **	Parameters:
465 **		user -- the name of the user who's mail we would like
466 **			to forward to.  It must have been verified --
467 **			i.e., the q_home field must have been filled
468 **			in.
469 **		sendq -- a pointer to the head of the send queue to
470 **			put this user's aliases in.
471 **
472 **	Returns:
473 **		none.
474 **
475 **	Side Effects:
476 **		New names are added to send queues.
477 */
478 
479 forward(user, sendq)
480 	ADDRESS *user;
481 	ADDRESS **sendq;
482 {
483 	char buf[60];
484 	extern bool safefile();
485 
486 # ifdef DEBUG
487 	if (tTd(27, 1))
488 		printf("forward(%s)\n", user->q_paddr);
489 # endif DEBUG
490 
491 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
492 		return;
493 # ifdef DEBUG
494 	if (user->q_home == NULL)
495 		syserr("forward: no home");
496 # endif DEBUG
497 
498 	/* good address -- look for .forward file in home */
499 	define('z', user->q_home);
500 	expand("$z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
501 	if (!safefile(buf, user->q_uid, S_IREAD))
502 		return;
503 
504 	/* we do have an address to forward to -- do it */
505 	include(buf, "forwarding", user, sendq);
506 }
507