xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 25278)
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.6 (Berkeley) 10/24/85	(with DBM)";
14 # else DBM
15 static char	SccsId[] = "@(#)alias.c	5.6 (Berkeley) 10/24/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 		p = index(line, '\n');
357 		if (p != NULL)
358 			*p = '\0';
359 		switch (line[0])
360 		{
361 		  case '#':
362 		  case '\0':
363 			skipping = FALSE;
364 			continue;
365 
366 		  case ' ':
367 		  case '\t':
368 			if (!skipping)
369 				syserr("Non-continuation line starts with space");
370 			skipping = TRUE;
371 			continue;
372 		}
373 		skipping = FALSE;
374 
375 		/*
376 		**  Process the LHS
377 		**	Find the final colon, and parse the address.
378 		**	It should resolve to a local name -- this will
379 		**	be checked later (we want to optionally do
380 		**	parsing of the RHS first to maximize error
381 		**	detection).
382 		*/
383 
384 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
385 			continue;
386 		if (*p++ != ':')
387 		{
388 			syserr("missing colon");
389 			continue;
390 		}
391 		if (parseaddr(line, &al, 1, ':') == NULL)
392 		{
393 			syserr("illegal alias name");
394 			continue;
395 		}
396 		loweraddr(&al);
397 
398 		/*
399 		**  Process the RHS.
400 		**	'al' is the internal form of the LHS address.
401 		**	'p' points to the text of the RHS.
402 		*/
403 
404 		rhs = p;
405 		for (;;)
406 		{
407 			register char c;
408 
409 			if (init)
410 			{
411 				/* do parsing & compression of addresses */
412 				while (*p != '\0')
413 				{
414 					extern char *DelimChar;
415 
416 					while (isspace(*p) || *p == ',')
417 						p++;
418 					if (*p == '\0')
419 						break;
420 					if (parseaddr(p, &bl, -1, ',') == NULL)
421 						usrerr("%s... bad address", p);
422 					p = DelimChar;
423 				}
424 			}
425 			else
426 			{
427 				p = &p[strlen(p)];
428 				if (p[-1] == '\n')
429 					*--p = '\0';
430 			}
431 
432 			/* see if there should be a continuation line */
433 			c = fgetc(af);
434 			if (!feof(af))
435 				(void) ungetc(c, af);
436 			if (c != ' ' && c != '\t')
437 				break;
438 
439 			/* read continuation line */
440 			if (fgets(p, sizeof line - (p - line), af) == NULL)
441 				break;
442 			LineNumber++;
443 		}
444 		if (al.q_mailer != LocalMailer)
445 		{
446 			syserr("cannot alias non-local names");
447 			continue;
448 		}
449 
450 		/*
451 		**  Insert alias into symbol table or DBM file
452 		*/
453 
454 		lhssize = strlen(al.q_user) + 1;
455 		rhssize = strlen(rhs) + 1;
456 
457 # ifdef DBM
458 		if (init)
459 		{
460 			DATUM key, content;
461 
462 			key.dsize = lhssize;
463 			key.dptr = al.q_user;
464 			content.dsize = rhssize;
465 			content.dptr = rhs;
466 			store(key, content);
467 		}
468 		else
469 # endif DBM
470 		{
471 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
472 			s->s_alias = newstr(rhs);
473 		}
474 
475 		/* statistics */
476 		naliases++;
477 		bytes += lhssize + rhssize;
478 		if (rhssize > longest)
479 			longest = rhssize;
480 	}
481 
482 # ifdef DBM
483 	if (init)
484 	{
485 		/* add the distinquished alias "@" */
486 		DATUM key;
487 
488 		key.dsize = 2;
489 		key.dptr = "@";
490 		store(key, key);
491 
492 		/* restore the old signal */
493 		(void) signal(SIGINT, oldsigint);
494 	}
495 # endif DBM
496 
497 	/* closing the alias file drops the lock */
498 	(void) fclose(af);
499 	CurEnv->e_to = NULL;
500 	FileName = NULL;
501 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
502 			naliases, longest, bytes);
503 # ifdef LOG
504 	if (LogLevel >= 8)
505 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
506 			naliases, longest, bytes);
507 # endif LOG
508 }
509 /*
510 **  FORWARD -- Try to forward mail
511 **
512 **	This is similar but not identical to aliasing.
513 **
514 **	Parameters:
515 **		user -- the name of the user who's mail we would like
516 **			to forward to.  It must have been verified --
517 **			i.e., the q_home field must have been filled
518 **			in.
519 **		sendq -- a pointer to the head of the send queue to
520 **			put this user's aliases in.
521 **
522 **	Returns:
523 **		none.
524 **
525 **	Side Effects:
526 **		New names are added to send queues.
527 */
528 
529 forward(user, sendq)
530 	ADDRESS *user;
531 	ADDRESS **sendq;
532 {
533 	char buf[60];
534 	extern bool safefile();
535 
536 # ifdef DEBUG
537 	if (tTd(27, 1))
538 		printf("forward(%s)\n", user->q_paddr);
539 # endif DEBUG
540 
541 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
542 		return;
543 # ifdef DEBUG
544 	if (user->q_home == NULL)
545 		syserr("forward: no home");
546 # endif DEBUG
547 
548 	/* good address -- look for .forward file in home */
549 	define('z', user->q_home, CurEnv);
550 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
551 	if (!safefile(buf, user->q_uid, S_IREAD))
552 		return;
553 
554 	/* we do have an address to forward to -- do it */
555 	include(buf, "forwarding", user, sendq);
556 }
557