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