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