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