1 # include <pwd.h>
2 # include <sys/types.h>
3 # include <sys/stat.h>
4 # include "sendmail.h"
5 
6 # ifdef DBM
7 SCCSID(@(#)alias.c	3.33.1.1		06/06/82	(with DBM));
8 # else DBM
9 SCCSID(@(#)alias.c	3.33.1.1		06/06/82	(without DBM));
10 # endif DBM
11 
12 /*
13 **  ALIAS -- Compute aliases.
14 **
15 **	Scans the file /usr/lib/aliases for a set of aliases.
16 **	If found, it arranges to deliver to them.  Uses libdbm
17 **	database if -DDBM.
18 **
19 **	Parameters:
20 **		a -- address to alias.
21 **		sendq -- a pointer to the head of the send queue
22 **			to put the aliases in.
23 **
24 **	Returns:
25 **		none
26 **
27 **	Side Effects:
28 **		Aliases found are expanded.
29 **
30 **	Files:
31 **		/usr/lib/aliases -- the mail aliases.  The format is
32 **			a series of lines of the form:
33 **				alias:name1,name2,name3,...
34 **			where 'alias' expands to all of
35 **			'name[i]'.  Continuations begin with
36 **			space or tab.
37 **		/usr/lib/aliases.pag, /usr/lib/aliases.dir: libdbm version
38 **			of alias file.  Keys are aliases, datums
39 **			(data?) are name1,name2, ...
40 **
41 **	Notes:
42 **		If NoAlias (the "-n" flag) is set, no aliasing is
43 **			done.
44 **
45 **	Deficiencies:
46 **		It should complain about names that are aliased to
47 **			nothing.
48 **		It is unsophisticated about line overflows.
49 */
50 
51 
52 #ifdef DBM
53 typedef struct
54 {
55 	char	*dptr;
56 	int	dsize;
57 } DATUM;
58 extern DATUM fetch();
59 #endif DBM
60 
61 alias(a, sendq)
62 	register ADDRESS *a;
63 	ADDRESS **sendq;
64 {
65 	register char *p;
66 	extern char *aliaslookup();
67 
68 	if (NoAlias)
69 		return;
70 # ifdef DEBUG
71 	if (Debug)
72 		printf("alias(%s)\n", a->q_paddr);
73 # endif
74 
75 	/* don't realias already aliased names */
76 	if (bitset(QDONTSEND, a->q_flags))
77 		return;
78 
79 	CurEnv->e_to = a->q_paddr;
80 
81 	/*
82 	**  Look up this name
83 	*/
84 
85 	p = aliaslookup(a->q_user);
86 	if (p == NULL)
87 		return;
88 
89 	/*
90 	**  Match on Alias.
91 	**	Deliver to the target list.
92 	*/
93 
94 # ifdef DEBUG
95 	if (Debug)
96 		printf("%s (%s, %s) aliased to %s\n",
97 		    a->q_paddr, a->q_host, a->q_user, p);
98 # endif
99 	if (Verbose)
100 		message(Arpa_Info, "aliased to %s", p);
101 	AliasLevel++;
102 	sendto(p, 1, 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 	char buf[MAXNAME];
168 	struct stat stb;
169 	time_t modtime;
170 
171 	/*
172 	**  See if the DBM version of the file is out of date with
173 	**  the text version.  If so, go into 'init' mode automatically.
174 	**	This only happens if our effective userid owns the DBM
175 	**	version or if the mode of the database is 666 -- this
176 	**	is an attempt to avoid protection problems.  Note the
177 	**	unpalatable hack to see if the stat succeeded.
178 	*/
179 
180 	if (stat(aliasfile, &stb) < 0)
181 		return;
182 # ifdef DBM
183 	modtime = stb.st_mtime;
184 	(void) strcpy(buf, aliasfile);
185 	(void) strcat(buf, ".pag");
186 	stb.st_ino = 0;
187 	if ((stat(buf, &stb) < 0 || stb.st_mtime < modtime) && !init)
188 	{
189 		if (stb.st_ino != 0 &&
190 		    ((stb.st_mode & 0666) == 0666 || stb.st_uid == geteuid()))
191 		{
192 			init = TRUE;
193 			if (Verbose)
194 				message(Arpa_Info, "rebuilding alias database");
195 		}
196 		else
197 		{
198 			message(Arpa_Info, "Warning: alias database out of date");
199 		}
200 	}
201 # endif DBM
202 
203 	/*
204 	**  If initializing, create the new files.
205 	**	We should lock the alias file here to prevent other
206 	**	instantiations of sendmail from reading an incomplete
207 	**	file -- or worse yet, doing a concurrent initialize.
208 	*/
209 
210 # ifdef DBM
211 	if (init)
212 	{
213 		(void) strcpy(buf, aliasfile);
214 		(void) strcat(buf, ".dir");
215 		if (close(creat(buf, DBMMODE)) < 0)
216 		{
217 			syserr("cannot make %s", buf);
218 			return;
219 		}
220 		(void) strcpy(buf, aliasfile);
221 		(void) strcat(buf, ".pag");
222 		if (close(creat(buf, DBMMODE)) < 0)
223 		{
224 			syserr("cannot make %s", buf);
225 			return;
226 		}
227 	}
228 
229 	/*
230 	**  Open and, if necessary, load the DBM file.
231 	**	If running without DBM, load the symbol table.
232 	*/
233 
234 	dbminit(aliasfile);
235 	if (init)
236 		readaliases(aliasfile, TRUE);
237 # else DBM
238 	readaliases(aliasfile, init);
239 # endif DBM
240 }
241 /*
242 **  READALIASES -- read and process the alias file.
243 **
244 **	This routine implements the part of initaliases that occurs
245 **	when we are not going to use the DBM stuff.
246 **
247 **	Parameters:
248 **		aliasfile -- the pathname of the alias file master.
249 **		init -- if set, initialize the DBM stuff.
250 **
251 **	Returns:
252 **		none.
253 **
254 **	Side Effects:
255 **		Reads aliasfile into the symbol table.
256 **		Optionally, builds the .dir & .pag files.
257 */
258 
259 static
260 readaliases(aliasfile, init)
261 	char *aliasfile;
262 	bool init;
263 {
264 	char line[BUFSIZ];
265 	register char *p;
266 	char *p2;
267 	char *rhs;
268 	bool skipping;
269 	ADDRESS al, bl;
270 	FILE *af;
271 	int lineno;
272 	register STAB *s;
273 	int naliases, bytes, longest;
274 
275 	if ((af = fopen(aliasfile, "r")) == NULL)
276 	{
277 # ifdef DEBUG
278 		if (Debug)
279 			printf("Can't open %s\n", aliasfile);
280 # endif
281 		errno = 0;
282 		NoAlias++;
283 		return;
284 	}
285 
286 	/*
287 	**  Read and interpret lines
288 	*/
289 
290 	lineno = 0;
291 	naliases = bytes = longest = 0;
292 	skipping = FALSE;
293 	while (fgets(line, sizeof (line), af) != NULL)
294 	{
295 		int lhssize, rhssize;
296 
297 		lineno++;
298 		switch (line[0])
299 		{
300 		  case '#':
301 		  case '\n':
302 		  case '\0':
303 			skipping = FALSE;
304 			continue;
305 
306 		  case ' ':
307 		  case '\t':
308 			if (!skipping)
309 				syserr("aliases: %d: Non-continuation line starts with space", lineno);
310 			skipping = TRUE;
311 			continue;
312 		}
313 		skipping = FALSE;
314 
315 		/*
316 		**  Process the LHS
317 		**	Find the final colon, and parse the address.
318 		**	It should resolve to a local name -- this will
319 		**	be checked later (we want to optionally do
320 		**	parsing of the RHS first to maximize error
321 		**	detection).
322 		*/
323 
324 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
325 			continue;
326 		if (*p == '\0' || *p == '\n')
327 		{
328 		 syntaxerr:
329 			syserr("%s, line %d: syntax error", aliasfile, lineno);
330 			continue;
331 		}
332 		*p++ = '\0';
333 		if (parse(line, &al, 1) == NULL)
334 		{
335 			*--p = ':';
336 			goto syntaxerr;
337 		}
338 
339 		/*
340 		**  Process the RHS.
341 		**	'al' is the internal form of the LHS address.
342 		**	'p' points to the text of the RHS.
343 		**		'p' may begin with a colon (i.e., the
344 		**		separator was "::") which will use the
345 		**		first address as the person to send
346 		**		errors to -- i.e., designates the
347 		**		list maintainer.
348 		*/
349 
350 		if (*p == ':')
351 		{
352 			ADDRESS *maint;
353 
354 			while (isspace(*++p))
355 				continue;
356 			rhs = p;
357 			while (*p != '\0' && *p != ',')
358 				p++;
359 			if (*p != ',')
360 				goto syntaxerr;
361 			*p++ = '\0';
362 			maint = parse(p, (ADDRESS *) NULL, 1);
363 			if (maint == NULL)
364 				syserr("Illegal list maintainer for list %s", al.q_user);
365 			else if (CurEnv->e_returnto == &CurEnv->e_from)
366 			{
367 				CurEnv->e_returnto = maint;
368 				MailBack++;
369 			}
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 	if (Verbose)
457 		message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
458 			naliases, longest, bytes);
459 }
460 /*
461 **  FORWARD -- Try to forward mail
462 **
463 **	This is similar but not identical to aliasing.
464 **
465 **	Parameters:
466 **		user -- the name of the user who's mail we would like
467 **			to forward to.  It must have been verified --
468 **			i.e., the q_home field must have been filled
469 **			in.
470 **		sendq -- a pointer to the head of the send queue to
471 **			put this user's aliases in.
472 **
473 **	Returns:
474 **		none.
475 **
476 **	Side Effects:
477 **		New names are added to send queues.
478 */
479 
480 forward(user, sendq)
481 	ADDRESS *user;
482 	ADDRESS **sendq;
483 {
484 	char buf[60];
485 	extern bool safefile();
486 
487 # ifdef DEBUG
488 	if (Debug)
489 		printf("forward(%s)\n", user->q_paddr);
490 # endif DEBUG
491 
492 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
493 		return;
494 # ifdef DEBUG
495 	if (user->q_home == NULL)
496 		syserr("forward: no home");
497 # endif DEBUG
498 
499 	/* good address -- look for .forward file in home */
500 	define('z', user->q_home);
501 	expand("$z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
502 	if (!safefile(buf, user->q_uid, S_IREAD))
503 		return;
504 
505 	/* we do have an address to forward to -- do it */
506 	include(buf, "forwarding", user, sendq);
507 }
508 /*
509 **  HOSTALIAS -- alias a host name
510 **
511 **	Given a host name, look it up in the host alias table
512 **	and return it's value.  If nothing, return NULL.
513 **
514 **	Parameters:
515 **		a -- address to alias.
516 **
517 **	Returns:
518 **		text result of aliasing.
519 **		NULL if none.
520 **
521 **	Side Effects:
522 **		none.
523 */
524 
525 char *
526 hostalias(a)
527 	register ADDRESS *a;
528 {
529 	char buf[MAXNAME+2];
530 	register char *p;
531 
532 	(void) strcpy(buf, "/");
533 	(void) strcat(buf, a->q_host);
534 	makelower(buf);
535 
536 	p = aliaslookup(buf);
537 	if (p == NULL)
538 		return (NULL);
539 	(void) sprintf(buf, p, a->q_user);
540 	return (newstr(buf));
541 }
542