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