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