xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 50575)
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.23 (Berkeley) 07/26/91 (with DBM)";
12 #else
13 #ifdef NEWDB
14 static char sccsid[] = "@(#)alias.c	5.23 (Berkeley) 07/26/91 (with NEWDB)";
15 #else
16 static char sccsid[] = "@(#)alias.c	5.23 (Berkeley) 07/26/91 (without DBM)";
17 #endif
18 #endif
19 #endif /* not lint */
20 
21 # ifdef DBM
22 # ifdef NEWDB
23 #   ERROR: must choose one of DBM or NEWDB compilation flags
24 # endif
25 # endif
26 
27 # include <sys/types.h>
28 # include <sys/stat.h>
29 # include <signal.h>
30 # include <errno.h>
31 # include "sendmail.h"
32 # include <sys/file.h>
33 # include <pwd.h>
34 
35 # ifdef NEWDB
36 # include <db.h>
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 = hash_open(buf, O_RDONLY, DBMMODE, NULL);
225 # else
226 		dbminit(aliasfile);
227 # endif
228 	}
229 	atcnt = SafeAlias * 2;
230 	if (atcnt > 0)
231 	{
232 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
233 		{
234 			/*
235 			**  Reinitialize alias file in case the new
236 			**  one is mv'ed in instead of cp'ed in.
237 			**
238 			**	Only works with new DBM -- old one will
239 			**	just consume file descriptors forever.
240 			**	If you have a dbmclose() it can be
241 			**	added before the sleep(30).
242 			*/
243 
244 # ifdef NEWDB
245 			AliasDBptr->close(AliasDBptr);
246 # endif
247 
248 			sleep(30);
249 # ifdef NEWDB
250 			(void) strcpy(buf, aliasfile);
251 			(void) strcat(buf, ".db");
252 			AliasDBptr = hash_open(buf, O_RDONLY, DBMMODE, NULL);
253 # else
254 # ifdef NDBM
255 			dbminit(aliasfile);
256 # endif
257 # endif
258 		}
259 	}
260 	else
261 		atcnt = 1;
262 
263 	/*
264 	**  See if the DBM version of the file is out of date with
265 	**  the text version.  If so, go into 'init' mode automatically.
266 	**	This only happens if our effective userid owns the DBM.
267 	**	Note the unpalatable hack to see if the stat succeeded.
268 	*/
269 
270 	modtime = stb.st_mtime;
271 	(void) strcpy(buf, aliasfile);
272 # ifdef NEWDB
273 	(void) strcat(buf, ".db");
274 # else
275 	(void) strcat(buf, ".pag");
276 # endif
277 	stb.st_ino = 0;
278 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
279 	{
280 		errno = 0;
281 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
282 		{
283 			init = TRUE;
284 			automatic = TRUE;
285 			message(Arpa_Info, "rebuilding alias database");
286 #ifdef LOG
287 			if (LogLevel >= 7)
288 				syslog(LOG_INFO, "rebuilding alias database");
289 #endif LOG
290 		}
291 		else
292 		{
293 #ifdef LOG
294 			if (LogLevel >= 7)
295 				syslog(LOG_INFO, "alias database out of date");
296 #endif LOG
297 			message(Arpa_Info, "Warning: alias database out of date");
298 		}
299 	}
300 
301 
302 	/*
303 	**  If necessary, load the DBM file.
304 	**	If running without DBM, load the symbol table.
305 	*/
306 
307 	if (init)
308 	{
309 #ifdef LOG
310 		if (LogLevel >= 6)
311 		{
312 			extern char *username();
313 
314 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
315 				automatic ? "auto" : "", username());
316 		}
317 #endif LOG
318 		readaliases(aliasfile, TRUE);
319 	}
320 # else DBM
321 	readaliases(aliasfile, init);
322 # endif DBM
323 }
324 /*
325 **  READALIASES -- read and process the alias file.
326 **
327 **	This routine implements the part of initaliases that occurs
328 **	when we are not going to use the DBM stuff.
329 **
330 **	Parameters:
331 **		aliasfile -- the pathname of the alias file master.
332 **		init -- if set, initialize the DBM stuff.
333 **
334 **	Returns:
335 **		none.
336 **
337 **	Side Effects:
338 **		Reads aliasfile into the symbol table.
339 **		Optionally, builds the .dir & .pag files.
340 */
341 
342 static
343 readaliases(aliasfile, init)
344 	char *aliasfile;
345 	bool init;
346 {
347 	register char *p;
348 	char *rhs;
349 	bool skipping;
350 	int naliases, bytes, longest;
351 	FILE *af;
352 	void (*oldsigint)();
353 	ADDRESS al, bl;
354 	register STAB *s;
355 # ifdef NEWDB
356 	DB *dbp;
357 # endif
358 	char line[BUFSIZ];
359 
360 	if ((af = fopen(aliasfile, "r")) == NULL)
361 	{
362 		if (tTd(27, 1))
363 			printf("Can't open %s\n", aliasfile);
364 		errno = 0;
365 		NoAlias++;
366 		return;
367 	}
368 
369 # if defined(DBM) || defined(NEWDB)
370 	/* see if someone else is rebuilding the alias file already */
371 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
372 	{
373 		/* yes, they are -- wait until done and then return */
374 		message(Arpa_Info, "Alias file is already being rebuilt");
375 		if (OpMode != MD_INITALIAS)
376 		{
377 			/* wait for other rebuild to complete */
378 			(void) flock(fileno(af), LOCK_EX);
379 		}
380 		(void) fclose(af);
381 		errno = 0;
382 		return;
383 	}
384 # endif DBM
385 
386 	/*
387 	**  If initializing, create the new DBM files.
388 	*/
389 
390 	if (init)
391 	{
392 		oldsigint = signal(SIGINT, SIG_IGN);
393 # ifdef DBM
394 		(void) strcpy(line, aliasfile);
395 		(void) strcat(line, ".dir");
396 		if (close(creat(line, DBMMODE)) < 0)
397 		{
398 			syserr("cannot make %s", line);
399 			(void) signal(SIGINT, oldsigint);
400 			return;
401 		}
402 		(void) strcpy(line, aliasfile);
403 		(void) strcat(line, ".pag");
404 		if (close(creat(line, DBMMODE)) < 0)
405 		{
406 			syserr("cannot make %s", line);
407 			(void) signal(SIGINT, oldsigint);
408 			return;
409 		}
410 		dbminit(aliasfile);
411 # endif
412 # ifdef NEWDB
413 		(void) strcpy(line, aliasfile);
414 		(void) strcat(line, ".db");
415 		dbp = hash_open(line, O_RDWR|O_CREAT|O_TRUNC, DBMMODE, NULL);
416 # endif
417 	}
418 
419 	/*
420 	**  Read and interpret lines
421 	*/
422 
423 	FileName = aliasfile;
424 	LineNumber = 0;
425 	naliases = bytes = longest = 0;
426 	skipping = FALSE;
427 	while (fgets(line, sizeof (line), af) != NULL)
428 	{
429 		int lhssize, rhssize;
430 
431 		LineNumber++;
432 		p = index(line, '\n');
433 		if (p != NULL)
434 			*p = '\0';
435 		switch (line[0])
436 		{
437 		  case '#':
438 		  case '\0':
439 			skipping = FALSE;
440 			continue;
441 
442 		  case ' ':
443 		  case '\t':
444 			if (!skipping)
445 				syserr("Non-continuation line starts with space");
446 			skipping = TRUE;
447 			continue;
448 		}
449 		skipping = FALSE;
450 
451 		/*
452 		**  Process the LHS
453 		**	Find the final colon, and parse the address.
454 		**	It should resolve to a local name -- this will
455 		**	be checked later (we want to optionally do
456 		**	parsing of the RHS first to maximize error
457 		**	detection).
458 		*/
459 
460 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
461 			continue;
462 		if (*p++ != ':')
463 		{
464 			syserr("missing colon");
465 			continue;
466 		}
467 		if (parseaddr(line, &al, 1, ':') == NULL)
468 		{
469 			syserr("illegal alias name");
470 			continue;
471 		}
472 		loweraddr(&al);
473 
474 		/*
475 		**  Process the RHS.
476 		**	'al' is the internal form of the LHS address.
477 		**	'p' points to the text of the RHS.
478 		*/
479 
480 		rhs = p;
481 		for (;;)
482 		{
483 			register char c;
484 
485 			if (init && CheckAliases)
486 			{
487 				/* do parsing & compression of addresses */
488 				while (*p != '\0')
489 				{
490 					extern char *DelimChar;
491 
492 					while (isspace(*p) || *p == ',')
493 						p++;
494 					if (*p == '\0')
495 						break;
496 					if (parseaddr(p, &bl, -1, ',') == NULL)
497 						usrerr("%s... bad address", p);
498 					p = DelimChar;
499 				}
500 			}
501 			else
502 			{
503 				p = &p[strlen(p)];
504 				if (p[-1] == '\n')
505 					*--p = '\0';
506 			}
507 
508 			/* see if there should be a continuation line */
509 			c = fgetc(af);
510 			if (!feof(af))
511 				(void) ungetc(c, af);
512 			if (c != ' ' && c != '\t')
513 				break;
514 
515 			/* read continuation line */
516 			if (fgets(p, sizeof line - (p - line), af) == NULL)
517 				break;
518 			LineNumber++;
519 		}
520 		if (al.q_mailer != LocalMailer)
521 		{
522 			syserr("cannot alias non-local names");
523 			continue;
524 		}
525 
526 		/*
527 		**  Insert alias into symbol table or DBM file
528 		*/
529 
530 		lhssize = strlen(al.q_user) + 1;
531 		rhssize = strlen(rhs) + 1;
532 
533 # if defined(DBM) || defined(NEWDB)
534 		if (init)
535 		{
536 			DBT key, content;
537 
538 			key.size = lhssize;
539 			key.data = al.q_user;
540 			content.size = rhssize;
541 			content.data = rhs;
542 # ifdef DBM
543 			store(key, content);
544 # else
545 			if (dbp->put(dbp, &key, &content, R_PUT) != 0)
546 			{
547 				syserr("cannot put alias %s", al.q_user);
548 			}
549 # endif
550 		}
551 		else
552 # endif DBM
553 		{
554 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
555 			s->s_alias = newstr(rhs);
556 		}
557 
558 		/* statistics */
559 		naliases++;
560 		bytes += lhssize + rhssize;
561 		if (rhssize > longest)
562 			longest = rhssize;
563 	}
564 
565 # if defined(DBM) || defined(NEWDB)
566 	if (init)
567 	{
568 		/* add the distinquished alias "@" */
569 		DBT key;
570 
571 		key.size = 2;
572 		key.data = "@";
573 # ifdef NEWDB
574 		dbp->put(dbp, &key, &key, R_PUT);
575 		dbp->close(dbp);
576 # else
577 		store(key, key);
578 # endif
579 
580 		/* restore the old signal */
581 		(void) signal(SIGINT, oldsigint);
582 	}
583 # endif DBM
584 
585 	/* closing the alias file drops the lock */
586 	(void) fclose(af);
587 	CurEnv->e_to = NULL;
588 	FileName = NULL;
589 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
590 			naliases, longest, bytes);
591 # ifdef LOG
592 	if (LogLevel >= 8)
593 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
594 			naliases, longest, bytes);
595 # endif LOG
596 }
597 /*
598 **  FORWARD -- Try to forward mail
599 **
600 **	This is similar but not identical to aliasing.
601 **
602 **	Parameters:
603 **		user -- the name of the user who's mail we would like
604 **			to forward to.  It must have been verified --
605 **			i.e., the q_home field must have been filled
606 **			in.
607 **		sendq -- a pointer to the head of the send queue to
608 **			put this user's aliases in.
609 **
610 **	Returns:
611 **		none.
612 **
613 **	Side Effects:
614 **		New names are added to send queues.
615 */
616 
617 forward(user, sendq)
618 	ADDRESS *user;
619 	ADDRESS **sendq;
620 {
621 	char buf[60];
622 	extern bool safefile();
623 
624 	if (tTd(27, 1))
625 		printf("forward(%s)\n", user->q_paddr);
626 
627 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
628 		return;
629 	if (user->q_home == NULL)
630 		syserr("forward: no home");
631 
632 	/* good address -- look for .forward file in home */
633 	define('z', user->q_home, CurEnv);
634 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
635 	if (!safefile(buf, user->q_uid, S_IREAD))
636 		return;
637 
638 	/* we do have an address to forward to -- do it */
639 	include(buf, "forwarding", user, sendq);
640 }
641