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