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