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