xref: /csrg-svn/usr.sbin/sendmail/src/alias.c (revision 68693)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988, 1993
4  *	The Regents of the University of California.  All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 # include "sendmail.h"
10 
11 #ifndef lint
12 static char sccsid[] = "@(#)alias.c	8.41 (Berkeley) 03/31/95";
13 #endif /* not lint */
14 
15 
16 MAP	*AliasDB[MAXALIASDB + 1];	/* actual database list */
17 int	NAliasDBs;			/* number of alias databases */
18 /*
19 **  ALIAS -- Compute aliases.
20 **
21 **	Scans the alias file for an alias for the given address.
22 **	If found, it arranges to deliver to the alias list instead.
23 **	Uses libdbm database if -DDBM.
24 **
25 **	Parameters:
26 **		a -- address to alias.
27 **		sendq -- a pointer to the head of the send queue
28 **			to put the aliases in.
29 **		aliaslevel -- the current alias nesting depth.
30 **		e -- the current envelope.
31 **
32 **	Returns:
33 **		none
34 **
35 **	Side Effects:
36 **		Aliases found are expanded.
37 **
38 **	Deficiencies:
39 **		It should complain about names that are aliased to
40 **			nothing.
41 */
42 
43 void
44 alias(a, sendq, aliaslevel, e)
45 	register ADDRESS *a;
46 	ADDRESS **sendq;
47 	int aliaslevel;
48 	register ENVELOPE *e;
49 {
50 	register char *p;
51 	int naliases;
52 	char *owner;
53 	char obuf[MAXNAME + 6];
54 	extern char *aliaslookup();
55 
56 	if (tTd(27, 1))
57 		printf("alias(%s)\n", a->q_user);
58 
59 	/* don't realias already aliased names */
60 	if (bitset(QDONTSEND|QBADADDR|QVERIFIED, a->q_flags))
61 		return;
62 
63 	if (NoAlias)
64 		return;
65 
66 	e->e_to = a->q_paddr;
67 
68 	/*
69 	**  Look up this name
70 	*/
71 
72 	p = aliaslookup(a->q_user, e);
73 	if (p == NULL)
74 		return;
75 
76 	/*
77 	**  Match on Alias.
78 	**	Deliver to the target list.
79 	*/
80 
81 	if (tTd(27, 1))
82 		printf("%s (%s, %s) aliased to %s\n",
83 		    a->q_paddr, a->q_host, a->q_user, p);
84 	if (bitset(EF_VRFYONLY, e->e_flags))
85 	{
86 		a->q_flags |= QVERIFIED;
87 		e->e_nrcpts++;
88 		return;
89 	}
90 	message("aliased to %s", p);
91 #ifdef LOG
92 	if (LogLevel > 9)
93 		syslog(LOG_INFO, "%s: alias %s => %s",
94 			e->e_id == NULL ? "NOQUEUE" : e->e_id,
95 			a->q_paddr, p);
96 #endif
97 	a->q_flags &= ~QSELFREF;
98 	if (tTd(27, 5))
99 	{
100 		printf("alias: QDONTSEND ");
101 		printaddr(a, FALSE);
102 	}
103 	a->q_flags |= QDONTSEND;
104 	naliases = sendtolist(p, a, sendq, aliaslevel + 1, e);
105 	if (bitset(QSELFREF, a->q_flags))
106 		a->q_flags &= ~QDONTSEND;
107 
108 	/*
109 	**  Look for owner of alias
110 	*/
111 
112 	(void) strcpy(obuf, "owner-");
113 	if (strncmp(a->q_user, "owner-", 6) == 0)
114 		(void) strcat(obuf, "owner");
115 	else
116 		(void) strcat(obuf, a->q_user);
117 	if (!bitnset(M_USR_UPPER, a->q_mailer->m_flags))
118 		makelower(obuf);
119 	owner = aliaslookup(obuf, e);
120 	if (owner == NULL)
121 		return;
122 
123 	/* reflect owner into envelope sender */
124 	if (strpbrk(owner, ",:/|\"") != NULL)
125 		owner = obuf;
126 	a->q_owner = newstr(owner);
127 
128 	/* announce delivery to this alias; NORECEIPT bit set later */
129 	if (e->e_xfp != NULL)
130 		fprintf(e->e_xfp, "Message delivered to mailing list %s\n",
131 			a->q_paddr);
132 	e->e_flags |= EF_SENDRECEIPT;
133 	a->q_flags |= QREPORT|QEXPLODED;
134 }
135 /*
136 **  ALIASLOOKUP -- look up a name in the alias file.
137 **
138 **	Parameters:
139 **		name -- the name to look up.
140 **
141 **	Returns:
142 **		the value of name.
143 **		NULL if unknown.
144 **
145 **	Side Effects:
146 **		none.
147 **
148 **	Warnings:
149 **		The return value will be trashed across calls.
150 */
151 
152 char *
153 aliaslookup(name, e)
154 	char *name;
155 	ENVELOPE *e;
156 {
157 	register int dbno;
158 	register MAP *map;
159 	register char *p;
160 
161 	for (dbno = 0; dbno < NAliasDBs; dbno++)
162 	{
163 		auto int stat;
164 
165 		map = AliasDB[dbno];
166 		if (!bitset(MF_OPEN, map->map_mflags))
167 			continue;
168 		p = (*map->map_class->map_lookup)(map, name, NULL, &stat);
169 		if (p != NULL)
170 			return p;
171 	}
172 	return NULL;
173 }
174 /*
175 **  SETALIAS -- set up an alias map
176 **
177 **	Called when reading configuration file.
178 **
179 **	Parameters:
180 **		spec -- the alias specification
181 **
182 **	Returns:
183 **		none.
184 */
185 
186 void
187 setalias(spec)
188 	char *spec;
189 {
190 	register char *p;
191 	register MAP *map;
192 	char *class;
193 	STAB *s;
194 	static bool first_unqual = TRUE;
195 
196 	if (tTd(27, 8))
197 		printf("setalias(%s)\n", spec);
198 
199 	for (p = spec; p != NULL; )
200 	{
201 		while (isspace(*p))
202 			p++;
203 		if (*p == '\0')
204 			break;
205 		spec = p;
206 
207 		/*
208 		**  Treat simple filename specially -- this is the file name
209 		**  for the files implementation, not necessarily in order.
210 		*/
211 
212 		if (spec[0] == '/' && first_unqual)
213 		{
214 			s = stab("aliases.files", ST_MAP, ST_ENTER);
215 			map = &s->s_map;
216 			first_unqual = FALSE;
217 		}
218 		else
219 		{
220 			char aname[50];
221 
222 			if (NAliasDBs >= MAXALIASDB)
223 			{
224 				syserr("Too many alias databases defined, %d max",
225 					MAXALIASDB);
226 				return;
227 			}
228 			(void) sprintf(aname, "Alias%d", NAliasDBs);
229 			s = stab(aname, ST_MAP, ST_ENTER);
230 			map = &s->s_map;
231 			AliasDB[NAliasDBs] = map;
232 		}
233 		bzero(map, sizeof *map);
234 		map->map_mname = s->s_name;
235 
236 		p = strpbrk(p, " ,/:");
237 		if (p != NULL && *p == ':')
238 		{
239 			/* map name */
240 			*p++ = '\0';
241 			class = spec;
242 			spec = p;
243 		}
244 		else
245 		{
246 			class = "implicit";
247 			map->map_mflags = MF_OPTIONAL|MF_INCLNULL;
248 		}
249 
250 		/* find end of spec */
251 		if (p != NULL)
252 			p = strchr(p, ',');
253 		if (p != NULL)
254 			*p++ = '\0';
255 
256 		if (tTd(27, 20))
257 			printf("  map %s:%s %s\n", class, s->s_name, spec);
258 
259 		/* look up class */
260 		s = stab(class, ST_MAPCLASS, ST_FIND);
261 		if (s == NULL)
262 		{
263 			if (tTd(27, 1))
264 				printf("Unknown alias class %s\n", class);
265 		}
266 		else if (!bitset(MCF_ALIASOK, s->s_mapclass.map_cflags))
267 		{
268 			syserr("setalias: map class %s can't handle aliases",
269 				class);
270 		}
271 		else
272 		{
273 			map->map_class = &s->s_mapclass;
274 			if (map->map_class->map_parse(map, spec))
275 			{
276 				map->map_mflags |= MF_VALID|MF_ALIAS;
277 				if (AliasDB[NAliasDBs] == map)
278 					NAliasDBs++;
279 			}
280 		}
281 	}
282 }
283 /*
284 **  ALIASWAIT -- wait for distinguished @:@ token to appear.
285 **
286 **	This can decide to reopen or rebuild the alias file
287 **
288 **	Parameters:
289 **		map -- a pointer to the map descriptor for this alias file.
290 **		ext -- the filename extension (e.g., ".db") for the
291 **			database file.
292 **		isopen -- if set, the database is already open, and we
293 **			should check for validity; otherwise, we are
294 **			just checking to see if it should be created.
295 **
296 **	Returns:
297 **		TRUE -- if the database is open when we return.
298 **		FALSE -- if the database is closed when we return.
299 */
300 
301 bool
302 aliaswait(map, ext, isopen)
303 	MAP *map;
304 	char *ext;
305 	int isopen;
306 {
307 	bool attimeout = FALSE;
308 	time_t mtime;
309 	struct stat stb;
310 	char buf[MAXNAME + 1];
311 
312 	if (tTd(27, 3))
313 		printf("aliaswait(%s:%s)\n",
314 			map->map_class->map_cname, map->map_file);
315 	if (bitset(MF_ALIASWAIT, map->map_mflags))
316 		return isopen;
317 	map->map_mflags |= MF_ALIASWAIT;
318 
319 	if (SafeAlias > 0)
320 	{
321 		auto int st;
322 		time_t toolong = curtime() + SafeAlias;
323 		unsigned int sleeptime = 2;
324 
325 		while (isopen &&
326 		       map->map_class->map_lookup(map, "@", NULL, &st) == NULL)
327 		{
328 			if (curtime() > toolong)
329 			{
330 				/* we timed out */
331 				attimeout = TRUE;
332 				break;
333 			}
334 
335 			/*
336 			**  Close and re-open the alias database in case
337 			**  the one is mv'ed instead of cp'ed in.
338 			*/
339 
340 			if (tTd(27, 2))
341 				printf("aliaswait: sleeping for %d seconds\n",
342 					sleeptime);
343 
344 			map->map_class->map_close(map);
345 			sleep(sleeptime);
346 			sleeptime *= 2;
347 			if (sleeptime > 60)
348 				sleeptime = 60;
349 			isopen = map->map_class->map_open(map, O_RDONLY);
350 		}
351 	}
352 
353 	/* see if we need to go into auto-rebuild mode */
354 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
355 	{
356 		if (tTd(27, 3))
357 			printf("aliaswait: not rebuildable\n");
358 		map->map_mflags &= ~MF_ALIASWAIT;
359 		return isopen;
360 	}
361 	if (stat(map->map_file, &stb) < 0)
362 	{
363 		if (tTd(27, 3))
364 			printf("aliaswait: no source file\n");
365 		map->map_mflags &= ~MF_ALIASWAIT;
366 		return isopen;
367 	}
368 	mtime = stb.st_mtime;
369 	(void) strcpy(buf, map->map_file);
370 	if (ext != NULL)
371 		(void) strcat(buf, ext);
372 	if (stat(buf, &stb) < 0 || stb.st_mtime < mtime || attimeout)
373 	{
374 		/* database is out of date */
375 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
376 		{
377 			bool oldSuprErrs;
378 
379 			message("auto-rebuilding alias database %s", buf);
380 			oldSuprErrs = SuprErrs;
381 			SuprErrs = TRUE;
382 			if (isopen)
383 				map->map_class->map_close(map);
384 			rebuildaliases(map, TRUE);
385 			isopen = map->map_class->map_open(map, O_RDONLY);
386 			SuprErrs = oldSuprErrs;
387 		}
388 		else
389 		{
390 #ifdef LOG
391 			if (LogLevel > 3)
392 				syslog(LOG_INFO, "alias database %s out of date",
393 					buf);
394 #endif /* LOG */
395 			message("Warning: alias database %s out of date", buf);
396 		}
397 	}
398 	map->map_mflags &= ~MF_ALIASWAIT;
399 	return isopen;
400 }
401 /*
402 **  REBUILDALIASES -- rebuild the alias database.
403 **
404 **	Parameters:
405 **		map -- the database to rebuild.
406 **		automatic -- set if this was automatically generated.
407 **
408 **	Returns:
409 **		none.
410 **
411 **	Side Effects:
412 **		Reads the text version of the database, builds the
413 **		DBM or DB version.
414 */
415 
416 void
417 rebuildaliases(map, automatic)
418 	register MAP *map;
419 	bool automatic;
420 {
421 	FILE *af;
422 	bool nolock = FALSE;
423 	sigfunc_t oldsigint, oldsigquit;
424 #ifdef SIGTSTP
425 	sigfunc_t oldsigtstp;
426 #endif
427 
428 	if (!bitset(MCF_REBUILDABLE, map->map_class->map_cflags))
429 		return;
430 
431 	/* try to lock the source file */
432 	if ((af = fopen(map->map_file, "r+")) == NULL)
433 	{
434 		if ((errno != EACCES && errno != EROFS) || automatic ||
435 		    (af = fopen(map->map_file, "r")) == NULL)
436 		{
437 			int saveerr = errno;
438 
439 			if (tTd(27, 1))
440 				printf("Can't open %s: %s\n",
441 					map->map_file, errstring(saveerr));
442 			if (!automatic && !bitset(MF_OPTIONAL, map->map_mflags))
443 				message("newaliases: cannot open %s: %s",
444 					map->map_file, errstring(saveerr));
445 			errno = 0;
446 			return;
447 		}
448 		nolock = TRUE;
449 		message("warning: cannot lock %s: %s",
450 			map->map_file, errstring(errno));
451 	}
452 
453 	/* see if someone else is rebuilding the alias file */
454 	if (!nolock &&
455 	    !lockfile(fileno(af), map->map_file, NULL, LOCK_EX|LOCK_NB))
456 	{
457 		/* yes, they are -- wait until done */
458 		message("Alias file %s is already being rebuilt",
459 			map->map_file);
460 		if (OpMode != MD_INITALIAS)
461 		{
462 			/* wait for other rebuild to complete */
463 			(void) lockfile(fileno(af), map->map_file, NULL,
464 					LOCK_EX);
465 		}
466 		(void) xfclose(af, "rebuildaliases1", map->map_file);
467 		errno = 0;
468 		return;
469 	}
470 
471 	/* avoid denial-of-service attacks */
472 	resetlimits();
473 	oldsigint = setsignal(SIGINT, SIG_IGN);
474 	oldsigquit = setsignal(SIGQUIT, SIG_IGN);
475 #ifdef SIGTSTP
476 	oldsigtstp = setsignal(SIGTSTP, SIG_IGN);
477 #endif
478 
479 	if (map->map_class->map_open(map, O_RDWR))
480 	{
481 #ifdef LOG
482 		if (LogLevel > 7)
483 		{
484 			syslog(LOG_NOTICE, "alias database %s %srebuilt by %s",
485 				map->map_file, automatic ? "auto" : "",
486 				username());
487 		}
488 #endif /* LOG */
489 		map->map_mflags |= MF_OPEN|MF_WRITABLE;
490 		readaliases(map, af, !automatic, TRUE);
491 	}
492 	else
493 	{
494 		if (tTd(27, 1))
495 			printf("Can't create database for %s: %s\n",
496 				map->map_file, errstring(errno));
497 		if (!automatic)
498 			syserr("Cannot create database for alias file %s",
499 				map->map_file);
500 	}
501 
502 	/* close the file, thus releasing locks */
503 	xfclose(af, "rebuildaliases2", map->map_file);
504 
505 	/* add distinguished entries and close the database */
506 	if (bitset(MF_OPEN, map->map_mflags))
507 		map->map_class->map_close(map);
508 
509 	/* restore the old signals */
510 	(void) setsignal(SIGINT, oldsigint);
511 	(void) setsignal(SIGQUIT, oldsigquit);
512 #ifdef SIGTSTP
513 	(void) setsignal(SIGTSTP, oldsigtstp);
514 #endif
515 }
516 /*
517 **  READALIASES -- read and process the alias file.
518 **
519 **	This routine implements the part of initaliases that occurs
520 **	when we are not going to use the DBM stuff.
521 **
522 **	Parameters:
523 **		map -- the alias database descriptor.
524 **		af -- file to read the aliases from.
525 **		announcestats -- anounce statistics regarding number of
526 **			aliases, longest alias, etc.
527 **		logstats -- lot the same info.
528 **
529 **	Returns:
530 **		none.
531 **
532 **	Side Effects:
533 **		Reads aliasfile into the symbol table.
534 **		Optionally, builds the .dir & .pag files.
535 */
536 
537 void
538 readaliases(map, af, announcestats, logstats)
539 	register MAP *map;
540 	FILE *af;
541 	bool announcestats;
542 	bool logstats;
543 {
544 	register char *p;
545 	char *rhs;
546 	bool skipping;
547 	long naliases, bytes, longest;
548 	ADDRESS al, bl;
549 	char line[BUFSIZ];
550 
551 	/*
552 	**  Read and interpret lines
553 	*/
554 
555 	FileName = map->map_file;
556 	LineNumber = 0;
557 	naliases = bytes = longest = 0;
558 	skipping = FALSE;
559 	while (fgets(line, sizeof (line), af) != NULL)
560 	{
561 		int lhssize, rhssize;
562 
563 		LineNumber++;
564 		p = strchr(line, '\n');
565 		if (p != NULL)
566 			*p = '\0';
567 		switch (line[0])
568 		{
569 		  case '#':
570 		  case '\0':
571 			skipping = FALSE;
572 			continue;
573 
574 		  case ' ':
575 		  case '\t':
576 			if (!skipping)
577 				syserr("554 Non-continuation line starts with space");
578 			skipping = TRUE;
579 			continue;
580 		}
581 		skipping = FALSE;
582 
583 		/*
584 		**  Process the LHS
585 		**	Find the colon separator, and parse the address.
586 		**	It should resolve to a local name -- this will
587 		**	be checked later (we want to optionally do
588 		**	parsing of the RHS first to maximize error
589 		**	detection).
590 		*/
591 
592 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
593 			continue;
594 		if (*p++ != ':')
595 		{
596 			syserr("554 missing colon");
597 			continue;
598 		}
599 		if (parseaddr(line, &al, RF_COPYALL, ':', NULL, CurEnv) == NULL)
600 		{
601 			syserr("554 %.40s... illegal alias name", line);
602 			continue;
603 		}
604 
605 		/*
606 		**  Process the RHS.
607 		**	'al' is the internal form of the LHS address.
608 		**	'p' points to the text of the RHS.
609 		*/
610 
611 		while (isascii(*p) && isspace(*p))
612 			p++;
613 		rhs = p;
614 		for (;;)
615 		{
616 			register char c;
617 			register char *nlp;
618 
619 			nlp = &p[strlen(p)];
620 			if (nlp[-1] == '\n')
621 				*--nlp = '\0';
622 
623 			if (CheckAliases)
624 			{
625 				/* do parsing & compression of addresses */
626 				while (*p != '\0')
627 				{
628 					auto char *delimptr;
629 
630 					while ((isascii(*p) && isspace(*p)) ||
631 								*p == ',')
632 						p++;
633 					if (*p == '\0')
634 						break;
635 					if (parseaddr(p, &bl, RF_COPYNONE, ',',
636 						      &delimptr, CurEnv) == NULL)
637 						usrerr("553 %s... bad address", p);
638 					p = delimptr;
639 				}
640 			}
641 			else
642 			{
643 				p = nlp;
644 			}
645 
646 			/* see if there should be a continuation line */
647 			c = fgetc(af);
648 			if (!feof(af))
649 				(void) ungetc(c, af);
650 			if (c != ' ' && c != '\t')
651 				break;
652 
653 			/* read continuation line */
654 			if (fgets(p, sizeof line - (p - line), af) == NULL)
655 				break;
656 			LineNumber++;
657 
658 			/* check for line overflow */
659 			if (strchr(p, '\n') == NULL)
660 			{
661 				usrerr("554 alias too long");
662 				break;
663 			}
664 		}
665 		if (!bitnset(M_ALIASABLE, al.q_mailer->m_flags))
666 		{
667 			syserr("554 %s... cannot alias non-local names",
668 				al.q_paddr);
669 			continue;
670 		}
671 
672 		/*
673 		**  Insert alias into symbol table or DBM file
674 		*/
675 
676 		if (!bitnset(M_USR_UPPER, al.q_mailer->m_flags))
677 			makelower(al.q_user);
678 
679 		lhssize = strlen(al.q_user);
680 		rhssize = strlen(rhs);
681 		map->map_class->map_store(map, al.q_user, rhs);
682 
683 		if (al.q_paddr != NULL)
684 			free(al.q_paddr);
685 		if (al.q_host != NULL)
686 			free(al.q_host);
687 		if (al.q_user != NULL)
688 			free(al.q_user);
689 
690 		/* statistics */
691 		naliases++;
692 		bytes += lhssize + rhssize;
693 		if (rhssize > longest)
694 			longest = rhssize;
695 	}
696 
697 	CurEnv->e_to = NULL;
698 	FileName = NULL;
699 	if (Verbose || announcestats)
700 		message("%s: %d aliases, longest %d bytes, %d bytes total",
701 			map->map_file, naliases, longest, bytes);
702 # ifdef LOG
703 	if (LogLevel > 7 && logstats)
704 		syslog(LOG_INFO, "%s: %d aliases, longest %d bytes, %d bytes total",
705 			map->map_file, naliases, longest, bytes);
706 # endif /* LOG */
707 }
708 /*
709 **  FORWARD -- Try to forward mail
710 **
711 **	This is similar but not identical to aliasing.
712 **
713 **	Parameters:
714 **		user -- the name of the user who's mail we would like
715 **			to forward to.  It must have been verified --
716 **			i.e., the q_home field must have been filled
717 **			in.
718 **		sendq -- a pointer to the head of the send queue to
719 **			put this user's aliases in.
720 **		aliaslevel -- the current alias nesting depth.
721 **		e -- the current envelope.
722 **
723 **	Returns:
724 **		none.
725 **
726 **	Side Effects:
727 **		New names are added to send queues.
728 */
729 
730 void
731 forward(user, sendq, aliaslevel, e)
732 	ADDRESS *user;
733 	ADDRESS **sendq;
734 	int aliaslevel;
735 	register ENVELOPE *e;
736 {
737 	char *pp;
738 	char *ep;
739 
740 	if (tTd(27, 1))
741 		printf("forward(%s)\n", user->q_paddr);
742 
743 	if (!bitnset(M_HASPWENT, user->q_mailer->m_flags) ||
744 	    bitset(QBADADDR, user->q_flags))
745 		return;
746 	if (user->q_home == NULL)
747 	{
748 		syserr("554 forward: no home");
749 		user->q_home = "/nosuchdirectory";
750 	}
751 
752 	/* good address -- look for .forward file in home */
753 	define('z', user->q_home, e);
754 	define('u', user->q_user, e);
755 	define('h', user->q_host, e);
756 	if (ForwardPath == NULL)
757 		ForwardPath = newstr("\201z/.forward");
758 
759 	for (pp = ForwardPath; pp != NULL; pp = ep)
760 	{
761 		int err;
762 		char buf[MAXPATHLEN+1];
763 		extern int include();
764 
765 		ep = strchr(pp, ':');
766 		if (ep != NULL)
767 			*ep = '\0';
768 		expand(pp, buf, sizeof buf, e);
769 		if (ep != NULL)
770 			*ep++ = ':';
771 		if (tTd(27, 3))
772 			printf("forward: trying %s\n", buf);
773 
774 		err = include(buf, TRUE, user, sendq, aliaslevel, e);
775 		if (err == 0)
776 			break;
777 		else if (transienterror(err))
778 		{
779 			/* we have to suspend this message */
780 			if (tTd(27, 2))
781 				printf("forward: transient error on %s\n", buf);
782 #ifdef LOG
783 			if (LogLevel > 2)
784 				syslog(LOG_ERR, "%s: forward %s: transient error: %s",
785 					e->e_id == NULL ? "NOQUEUE" : e->e_id,
786 					buf, errstring(err));
787 #endif
788 			message("%s: %s: message queued", buf, errstring(err));
789 			user->q_flags |= QQUEUEUP;
790 			return;
791 		}
792 	}
793 }
794