xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 24945)
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 # ifdef DBM
13 static char	SccsId[] = "@(#)alias.c	5.4 (Berkeley) 09/19/85	(with DBM)";
14 # else DBM
15 static char	SccsId[] = "@(#)alias.c	5.4 (Berkeley) 09/19/85	(without DBM)";
16 # endif DBM
17 #endif not lint
18 
19 # include <pwd.h>
20 # include <sys/types.h>
21 # include <sys/stat.h>
22 # include <signal.h>
23 # include <errno.h>
24 # include "sendmail.h"
25 # ifdef FLOCK
26 # include <sys/file.h>
27 # endif FLOCK
28 
29 
30 /*
31 **  ALIAS -- Compute aliases.
32 **
33 **	Scans the alias file for an alias for the given address.
34 **	If found, it arranges to deliver to the alias list instead.
35 **	Uses libdbm database if -DDBM.
36 **
37 **	Parameters:
38 **		a -- address to alias.
39 **		sendq -- a pointer to the head of the send queue
40 **			to put the aliases in.
41 **
42 **	Returns:
43 **		none
44 **
45 **	Side Effects:
46 **		Aliases found are expanded.
47 **
48 **	Notes:
49 **		If NoAlias (the "-n" flag) is set, no aliasing is
50 **			done.
51 **
52 **	Deficiencies:
53 **		It should complain about names that are aliased to
54 **			nothing.
55 */
56 
57 
58 #ifdef DBM
59 typedef struct
60 {
61 	char	*dptr;
62 	int	dsize;
63 } DATUM;
64 extern DATUM fetch();
65 #endif DBM
66 
67 alias(a, sendq)
68 	register ADDRESS *a;
69 	ADDRESS **sendq;
70 {
71 	register char *p;
72 	extern char *aliaslookup();
73 
74 # ifdef DEBUG
75 	if (tTd(27, 1))
76 		printf("alias(%s)\n", a->q_paddr);
77 # endif
78 
79 	/* don't realias already aliased names */
80 	if (bitset(QDONTSEND, a->q_flags))
81 		return;
82 
83 	CurEnv->e_to = a->q_paddr;
84 
85 	/*
86 	**  Look up this name
87 	*/
88 
89 	if (NoAlias)
90 		p = NULL;
91 	else
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 #ifdef LOG
226 			if (LogLevel >= 7)
227 				syslog(LOG_INFO, "rebuilding alias database");
228 #endif LOG
229 		}
230 		else
231 		{
232 #ifdef LOG
233 			if (LogLevel >= 7)
234 				syslog(LOG_INFO, "alias database out of date");
235 #endif LOG
236 			message(Arpa_Info, "Warning: alias database out of date");
237 		}
238 	}
239 
240 
241 	/*
242 	**  If necessary, load the DBM file.
243 	**	If running without DBM, load the symbol table.
244 	*/
245 
246 	if (init)
247 	{
248 		readaliases(aliasfile, TRUE);
249 	}
250 # else DBM
251 	readaliases(aliasfile, init);
252 # endif DBM
253 }
254 /*
255 **  READALIASES -- read and process the alias file.
256 **
257 **	This routine implements the part of initaliases that occurs
258 **	when we are not going to use the DBM stuff.
259 **
260 **	Parameters:
261 **		aliasfile -- the pathname of the alias file master.
262 **		init -- if set, initialize the DBM stuff.
263 **
264 **	Returns:
265 **		none.
266 **
267 **	Side Effects:
268 **		Reads aliasfile into the symbol table.
269 **		Optionally, builds the .dir & .pag files.
270 */
271 
272 static
273 readaliases(aliasfile, init)
274 	char *aliasfile;
275 	bool init;
276 {
277 	register char *p;
278 	char *p2;
279 	char *rhs;
280 	bool skipping;
281 	int naliases, bytes, longest;
282 	FILE *af;
283 	int (*oldsigint)();
284 	ADDRESS al, bl;
285 	register STAB *s;
286 	char line[BUFSIZ];
287 
288 	if ((af = fopen(aliasfile, "r")) == NULL)
289 	{
290 # ifdef DEBUG
291 		if (tTd(27, 1))
292 			printf("Can't open %s\n", aliasfile);
293 # endif
294 		errno = 0;
295 		NoAlias++;
296 		return;
297 	}
298 
299 # ifdef DBM
300 # ifdef FLOCK
301 	/* see if someone else is rebuilding the alias file already */
302 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
303 	{
304 		/* yes, they are -- wait until done and then return */
305 		message(Arpa_Info, "Alias file is already being rebuilt");
306 		if (OpMode != MD_INITALIAS)
307 		{
308 			/* wait for other rebuild to complete */
309 			(void) flock(fileno(af), LOCK_EX);
310 		}
311 		(void) fclose(af);
312 		errno = 0;
313 		return;
314 	}
315 # endif FLOCK
316 # endif DBM
317 
318 	/*
319 	**  If initializing, create the new DBM files.
320 	*/
321 
322 	if (init)
323 	{
324 		oldsigint = signal(SIGINT, SIG_IGN);
325 		(void) strcpy(line, aliasfile);
326 		(void) strcat(line, ".dir");
327 		if (close(creat(line, DBMMODE)) < 0)
328 		{
329 			syserr("cannot make %s", line);
330 			(void) signal(SIGINT, oldsigint);
331 			return;
332 		}
333 		(void) strcpy(line, aliasfile);
334 		(void) strcat(line, ".pag");
335 		if (close(creat(line, DBMMODE)) < 0)
336 		{
337 			syserr("cannot make %s", line);
338 			(void) signal(SIGINT, oldsigint);
339 			return;
340 		}
341 	}
342 
343 	/*
344 	**  Read and interpret lines
345 	*/
346 
347 	FileName = aliasfile;
348 	LineNumber = 0;
349 	naliases = bytes = longest = 0;
350 	skipping = FALSE;
351 	while (fgets(line, sizeof (line), af) != NULL)
352 	{
353 		int lhssize, rhssize;
354 
355 		LineNumber++;
356 		switch (line[0])
357 		{
358 		  case '#':
359 		  case '\n':
360 		  case '\0':
361 			skipping = FALSE;
362 			continue;
363 
364 		  case ' ':
365 		  case '\t':
366 			if (!skipping)
367 				syserr("Non-continuation line starts with space");
368 			skipping = TRUE;
369 			continue;
370 		}
371 		skipping = FALSE;
372 
373 		/*
374 		**  Process the LHS
375 		**	Find the final colon, and parse the address.
376 		**	It should resolve to a local name -- this will
377 		**	be checked later (we want to optionally do
378 		**	parsing of the RHS first to maximize error
379 		**	detection).
380 		*/
381 
382 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
383 			continue;
384 		if (*p++ != ':')
385 		{
386 			syserr("missing colon");
387 			continue;
388 		}
389 		if (parseaddr(line, &al, 1, ':') == NULL)
390 		{
391 			syserr("illegal alias name");
392 			continue;
393 		}
394 		loweraddr(&al);
395 
396 		/*
397 		**  Process the RHS.
398 		**	'al' is the internal form of the LHS address.
399 		**	'p' points to the text of the RHS.
400 		*/
401 
402 		rhs = p;
403 		for (;;)
404 		{
405 			register char c;
406 
407 			if (init)
408 			{
409 				/* do parsing & compression of addresses */
410 				c = *p;
411 				while (c != '\0')
412 				{
413 					p2 = p;
414 					while (*p != '\n' && *p != ',' && *p != '\0')
415 						p++;
416 					c = *p;
417 					if (c == '\n')
418 						c = '\0';
419 					*p = '\0';
420 					if (*p2 != '\0' &&
421 					    parseaddr(p2, &bl, -1, ',') == NULL)
422 					{
423 						usrerr("%s... bad address");
424 					}
425 					if (c != '\0')
426 						*p++ = c;
427 				}
428 			}
429 			else
430 			{
431 				p = &p[strlen(p)];
432 				if (p[-1] == '\n')
433 					*--p = '\0';
434 			}
435 
436 			/* see if there should be a continuation line */
437 			c = fgetc(af);
438 			if (!feof(af))
439 				(void) ungetc(c, af);
440 			if (c != ' ' && c != '\t')
441 				break;
442 
443 			/* read continuation line */
444 			if (fgets(p, sizeof line - (p - line), af) == NULL)
445 				break;
446 			LineNumber++;
447 		}
448 		if (al.q_mailer != LocalMailer)
449 		{
450 			syserr("cannot alias non-local names");
451 			continue;
452 		}
453 
454 		/*
455 		**  Insert alias into symbol table or DBM file
456 		*/
457 
458 		lhssize = strlen(al.q_user) + 1;
459 		rhssize = strlen(rhs) + 1;
460 
461 # ifdef DBM
462 		if (init)
463 		{
464 			DATUM key, content;
465 
466 			key.dsize = lhssize;
467 			key.dptr = al.q_user;
468 			content.dsize = rhssize;
469 			content.dptr = rhs;
470 			store(key, content);
471 		}
472 		else
473 # endif DBM
474 		{
475 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
476 			s->s_alias = newstr(rhs);
477 		}
478 
479 		/* statistics */
480 		naliases++;
481 		bytes += lhssize + rhssize;
482 		if (rhssize > longest)
483 			longest = rhssize;
484 	}
485 
486 # ifdef DBM
487 	if (init)
488 	{
489 		/* add the distinquished alias "@" */
490 		DATUM key;
491 
492 		key.dsize = 2;
493 		key.dptr = "@";
494 		store(key, key);
495 
496 		/* restore the old signal */
497 		(void) signal(SIGINT, oldsigint);
498 	}
499 # endif DBM
500 
501 	/* closing the alias file drops the lock */
502 	(void) fclose(af);
503 	CurEnv->e_to = NULL;
504 	FileName = NULL;
505 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
506 			naliases, longest, bytes);
507 # ifdef LOG
508 	if (LogLevel >= 8)
509 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
510 			naliases, longest, bytes);
511 # endif LOG
512 }
513 /*
514 **  FORWARD -- Try to forward mail
515 **
516 **	This is similar but not identical to aliasing.
517 **
518 **	Parameters:
519 **		user -- the name of the user who's mail we would like
520 **			to forward to.  It must have been verified --
521 **			i.e., the q_home field must have been filled
522 **			in.
523 **		sendq -- a pointer to the head of the send queue to
524 **			put this user's aliases in.
525 **
526 **	Returns:
527 **		none.
528 **
529 **	Side Effects:
530 **		New names are added to send queues.
531 */
532 
533 forward(user, sendq)
534 	ADDRESS *user;
535 	ADDRESS **sendq;
536 {
537 	char buf[60];
538 	extern bool safefile();
539 
540 # ifdef DEBUG
541 	if (tTd(27, 1))
542 		printf("forward(%s)\n", user->q_paddr);
543 # endif DEBUG
544 
545 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
546 		return;
547 # ifdef DEBUG
548 	if (user->q_home == NULL)
549 		syserr("forward: no home");
550 # endif DEBUG
551 
552 	/* good address -- look for .forward file in home */
553 	define('z', user->q_home, CurEnv);
554 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
555 	if (!safefile(buf, user->q_uid, S_IREAD))
556 		return;
557 
558 	/* we do have an address to forward to -- do it */
559 	include(buf, "forwarding", user, sendq);
560 }
561