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