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