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