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