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