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