xref: /original-bsd/usr.sbin/sendmail/src/alias.c (revision 74d2fb93)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * Redistribution and use in source and binary forms are permitted
7  * provided that the above copyright notice and this paragraph are
8  * duplicated in all such forms and that any documentation,
9  * advertising materials, and other materials related to such
10  * distribution and use acknowledge that the software was developed
11  * by the University of California, Berkeley.  The name of the
12  * University may not be used to endorse or promote products derived
13  * from this software without specific prior written permission.
14  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND WITHOUT ANY EXPRESS OR
15  * IMPLIED WARRANTIES, INCLUDING, WITHOUT LIMITATION, THE IMPLIED
16  * WARRANTIES OF MERCHANTIBILITY AND FITNESS FOR A PARTICULAR PURPOSE.
17  */
18 
19 #ifndef lint
20 #ifdef DBM
21 static char sccsid[] = "@(#)alias.c	5.16 (Berkeley) 07/14/88 (with DBM)";
22 #else
23 static char sccsid[] = "@(#)alias.c	5.16 (Berkeley) 07/14/88 (without DBM)";
24 #endif
25 #endif /* not lint */
26 
27 # include <pwd.h>
28 # include <sys/types.h>
29 # include <sys/stat.h>
30 # include <signal.h>
31 # include <errno.h>
32 # include "sendmail.h"
33 # include <sys/file.h>
34 
35 /*
36 **  ALIAS -- Compute aliases.
37 **
38 **	Scans the alias file for an alias for the given address.
39 **	If found, it arranges to deliver to the alias list instead.
40 **	Uses libdbm database if -DDBM.
41 **
42 **	Parameters:
43 **		a -- address to alias.
44 **		sendq -- a pointer to the head of the send queue
45 **			to put the aliases in.
46 **
47 **	Returns:
48 **		none
49 **
50 **	Side Effects:
51 **		Aliases found are expanded.
52 **
53 **	Notes:
54 **		If NoAlias (the "-n" flag) is set, no aliasing is
55 **			done.
56 **
57 **	Deficiencies:
58 **		It should complain about names that are aliased to
59 **			nothing.
60 */
61 
62 
63 #ifdef DBM
64 typedef struct
65 {
66 	char	*dptr;
67 	int	dsize;
68 } DATUM;
69 extern DATUM fetch();
70 #endif DBM
71 
72 alias(a, sendq)
73 	register ADDRESS *a;
74 	ADDRESS **sendq;
75 {
76 	register char *p;
77 	extern char *aliaslookup();
78 
79 # ifdef DEBUG
80 	if (tTd(27, 1))
81 		printf("alias(%s)\n", a->q_paddr);
82 # endif
83 
84 	/* don't realias already aliased names */
85 	if (bitset(QDONTSEND, a->q_flags))
86 		return;
87 
88 	CurEnv->e_to = a->q_paddr;
89 
90 	/*
91 	**  Look up this name
92 	*/
93 
94 	if (NoAlias)
95 		p = NULL;
96 	else
97 		p = aliaslookup(a->q_user);
98 	if (p == NULL)
99 		return;
100 
101 	/*
102 	**  Match on Alias.
103 	**	Deliver to the target list.
104 	*/
105 
106 # ifdef DEBUG
107 	if (tTd(27, 1))
108 		printf("%s (%s, %s) aliased to %s\n",
109 		    a->q_paddr, a->q_host, a->q_user, p);
110 # endif
111 	message(Arpa_Info, "aliased to %s", p);
112 	AliasLevel++;
113 	sendtolist(p, a, sendq);
114 	AliasLevel--;
115 }
116 /*
117 **  ALIASLOOKUP -- look up a name in the alias file.
118 **
119 **	Parameters:
120 **		name -- the name to look up.
121 **
122 **	Returns:
123 **		the value of name.
124 **		NULL if unknown.
125 **
126 **	Side Effects:
127 **		none.
128 **
129 **	Warnings:
130 **		The return value will be trashed across calls.
131 */
132 
133 char *
134 aliaslookup(name)
135 	char *name;
136 {
137 # ifdef DBM
138 	DATUM rhs, lhs;
139 
140 	/* create a key for fetch */
141 	lhs.dptr = name;
142 	lhs.dsize = strlen(name) + 1;
143 	rhs = fetch(lhs);
144 	return (rhs.dptr);
145 # else DBM
146 	register STAB *s;
147 
148 	s = stab(name, ST_ALIAS, ST_FIND);
149 	if (s == NULL)
150 		return (NULL);
151 	return (s->s_alias);
152 # endif DBM
153 }
154 /*
155 **  INITALIASES -- initialize for aliasing
156 **
157 **	Very different depending on whether we are running DBM or not.
158 **
159 **	Parameters:
160 **		aliasfile -- location of aliases.
161 **		init -- if set and if DBM, initialize the DBM files.
162 **
163 **	Returns:
164 **		none.
165 **
166 **	Side Effects:
167 **		initializes aliases:
168 **		if DBM:  opens the database.
169 **		if ~DBM: reads the aliases into the symbol table.
170 */
171 
172 # define DBMMODE	0666
173 
174 initaliases(aliasfile, init)
175 	char *aliasfile;
176 	bool init;
177 {
178 #ifdef DBM
179 	int atcnt;
180 	time_t modtime;
181 	bool automatic = FALSE;
182 	char buf[MAXNAME];
183 #endif DBM
184 	struct stat stb;
185 	static bool initialized = FALSE;
186 
187 	if (initialized)
188 		return;
189 	initialized = TRUE;
190 
191 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
192 	{
193 		if (aliasfile != NULL && init)
194 			syserr("Cannot open %s", aliasfile);
195 		NoAlias = TRUE;
196 		errno = 0;
197 		return;
198 	}
199 
200 # ifdef DBM
201 	/*
202 	**  Check to see that the alias file is complete.
203 	**	If not, we will assume that someone died, and it is up
204 	**	to us to rebuild it.
205 	*/
206 
207 	if (!init)
208 		dbminit(aliasfile);
209 	atcnt = SafeAlias * 2;
210 	if (atcnt > 0)
211 	{
212 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
213 		{
214 			/*
215 			**  Reinitialize alias file in case the new
216 			**  one is mv'ed in instead of cp'ed in.
217 			**
218 			**	Only works with new DBM -- old one will
219 			**	just consume file descriptors forever.
220 			**	If you have a dbmclose() it can be
221 			**	added before the sleep(30).
222 			*/
223 
224 			sleep(30);
225 # ifdef NDBM
226 			dbminit(aliasfile);
227 # endif NDBM
228 		}
229 	}
230 	else
231 		atcnt = 1;
232 
233 	/*
234 	**  See if the DBM version of the file is out of date with
235 	**  the text version.  If so, go into 'init' mode automatically.
236 	**	This only happens if our effective userid owns the DBM
237 	**	version or if the mode of the database is 666 -- this
238 	**	is an attempt to avoid protection problems.  Note the
239 	**	unpalatable hack to see if the stat succeeded.
240 	*/
241 
242 	modtime = stb.st_mtime;
243 	(void) strcpy(buf, aliasfile);
244 	(void) strcat(buf, ".pag");
245 	stb.st_ino = 0;
246 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
247 	{
248 		errno = 0;
249 		if (AutoRebuild && stb.st_ino != 0 &&
250 		    ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
251 		{
252 			init = TRUE;
253 			automatic = TRUE;
254 			message(Arpa_Info, "rebuilding alias database");
255 #ifdef LOG
256 			if (LogLevel >= 7)
257 				syslog(LOG_INFO, "rebuilding alias database");
258 #endif LOG
259 		}
260 		else
261 		{
262 #ifdef LOG
263 			if (LogLevel >= 7)
264 				syslog(LOG_INFO, "alias database out of date");
265 #endif LOG
266 			message(Arpa_Info, "Warning: alias database out of date");
267 		}
268 	}
269 
270 
271 	/*
272 	**  If necessary, load the DBM file.
273 	**	If running without DBM, load the symbol table.
274 	*/
275 
276 	if (init)
277 	{
278 #ifdef LOG
279 		if (LogLevel >= 6)
280 		{
281 			extern char *username();
282 
283 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
284 				automatic ? "auto" : "", username());
285 		}
286 #endif LOG
287 		readaliases(aliasfile, TRUE);
288 	}
289 # else DBM
290 	readaliases(aliasfile, init);
291 # endif DBM
292 }
293 /*
294 **  READALIASES -- read and process the alias file.
295 **
296 **	This routine implements the part of initaliases that occurs
297 **	when we are not going to use the DBM stuff.
298 **
299 **	Parameters:
300 **		aliasfile -- the pathname of the alias file master.
301 **		init -- if set, initialize the DBM stuff.
302 **
303 **	Returns:
304 **		none.
305 **
306 **	Side Effects:
307 **		Reads aliasfile into the symbol table.
308 **		Optionally, builds the .dir & .pag files.
309 */
310 
311 static
312 readaliases(aliasfile, init)
313 	char *aliasfile;
314 	bool init;
315 {
316 	register char *p;
317 	char *rhs;
318 	bool skipping;
319 	int naliases, bytes, longest;
320 	FILE *af;
321 	int (*oldsigint)();
322 	ADDRESS al, bl;
323 	register STAB *s;
324 	char line[BUFSIZ];
325 
326 	if ((af = fopen(aliasfile, "r")) == NULL)
327 	{
328 # ifdef DEBUG
329 		if (tTd(27, 1))
330 			printf("Can't open %s\n", aliasfile);
331 # endif
332 		errno = 0;
333 		NoAlias++;
334 		return;
335 	}
336 
337 # ifdef DBM
338 	/* see if someone else is rebuilding the alias file already */
339 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
340 	{
341 		/* yes, they are -- wait until done and then return */
342 		message(Arpa_Info, "Alias file is already being rebuilt");
343 		if (OpMode != MD_INITALIAS)
344 		{
345 			/* wait for other rebuild to complete */
346 			(void) flock(fileno(af), LOCK_EX);
347 		}
348 		(void) fclose(af);
349 		errno = 0;
350 		return;
351 	}
352 # endif DBM
353 
354 	/*
355 	**  If initializing, create the new DBM files.
356 	*/
357 
358 	if (init)
359 	{
360 		oldsigint = signal(SIGINT, SIG_IGN);
361 		(void) strcpy(line, aliasfile);
362 		(void) strcat(line, ".dir");
363 		if (close(creat(line, DBMMODE)) < 0)
364 		{
365 			syserr("cannot make %s", line);
366 			(void) signal(SIGINT, oldsigint);
367 			return;
368 		}
369 		(void) strcpy(line, aliasfile);
370 		(void) strcat(line, ".pag");
371 		if (close(creat(line, DBMMODE)) < 0)
372 		{
373 			syserr("cannot make %s", line);
374 			(void) signal(SIGINT, oldsigint);
375 			return;
376 		}
377 		dbminit(aliasfile);
378 	}
379 
380 	/*
381 	**  Read and interpret lines
382 	*/
383 
384 	FileName = aliasfile;
385 	LineNumber = 0;
386 	naliases = bytes = longest = 0;
387 	skipping = FALSE;
388 	while (fgets(line, sizeof (line), af) != NULL)
389 	{
390 		int lhssize, rhssize;
391 
392 		LineNumber++;
393 		p = index(line, '\n');
394 		if (p != NULL)
395 			*p = '\0';
396 		switch (line[0])
397 		{
398 		  case '#':
399 		  case '\0':
400 			skipping = FALSE;
401 			continue;
402 
403 		  case ' ':
404 		  case '\t':
405 			if (!skipping)
406 				syserr("Non-continuation line starts with space");
407 			skipping = TRUE;
408 			continue;
409 		}
410 		skipping = FALSE;
411 
412 		/*
413 		**  Process the LHS
414 		**	Find the final colon, and parse the address.
415 		**	It should resolve to a local name -- this will
416 		**	be checked later (we want to optionally do
417 		**	parsing of the RHS first to maximize error
418 		**	detection).
419 		*/
420 
421 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
422 			continue;
423 		if (*p++ != ':')
424 		{
425 			syserr("missing colon");
426 			continue;
427 		}
428 		if (parseaddr(line, &al, 1, ':') == NULL)
429 		{
430 			syserr("illegal alias name");
431 			continue;
432 		}
433 		loweraddr(&al);
434 
435 		/*
436 		**  Process the RHS.
437 		**	'al' is the internal form of the LHS address.
438 		**	'p' points to the text of the RHS.
439 		*/
440 
441 		rhs = p;
442 		for (;;)
443 		{
444 			register char c;
445 
446 			if (init && CheckAliases)
447 			{
448 				/* do parsing & compression of addresses */
449 				while (*p != '\0')
450 				{
451 					extern char *DelimChar;
452 
453 					while (isspace(*p) || *p == ',')
454 						p++;
455 					if (*p == '\0')
456 						break;
457 					if (parseaddr(p, &bl, -1, ',') == NULL)
458 						usrerr("%s... bad address", p);
459 					p = DelimChar;
460 				}
461 			}
462 			else
463 			{
464 				p = &p[strlen(p)];
465 				if (p[-1] == '\n')
466 					*--p = '\0';
467 			}
468 
469 			/* see if there should be a continuation line */
470 			c = fgetc(af);
471 			if (!feof(af))
472 				(void) ungetc(c, af);
473 			if (c != ' ' && c != '\t')
474 				break;
475 
476 			/* read continuation line */
477 			if (fgets(p, sizeof line - (p - line), af) == NULL)
478 				break;
479 			LineNumber++;
480 		}
481 		if (al.q_mailer != LocalMailer)
482 		{
483 			syserr("cannot alias non-local names");
484 			continue;
485 		}
486 
487 		/*
488 		**  Insert alias into symbol table or DBM file
489 		*/
490 
491 		lhssize = strlen(al.q_user) + 1;
492 		rhssize = strlen(rhs) + 1;
493 
494 # ifdef DBM
495 		if (init)
496 		{
497 			DATUM key, content;
498 
499 			key.dsize = lhssize;
500 			key.dptr = al.q_user;
501 			content.dsize = rhssize;
502 			content.dptr = rhs;
503 			store(key, content);
504 		}
505 		else
506 # endif DBM
507 		{
508 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
509 			s->s_alias = newstr(rhs);
510 		}
511 
512 		/* statistics */
513 		naliases++;
514 		bytes += lhssize + rhssize;
515 		if (rhssize > longest)
516 			longest = rhssize;
517 	}
518 
519 # ifdef DBM
520 	if (init)
521 	{
522 		/* add the distinquished alias "@" */
523 		DATUM key;
524 
525 		key.dsize = 2;
526 		key.dptr = "@";
527 		store(key, key);
528 
529 		/* restore the old signal */
530 		(void) signal(SIGINT, oldsigint);
531 	}
532 # endif DBM
533 
534 	/* closing the alias file drops the lock */
535 	(void) fclose(af);
536 	CurEnv->e_to = NULL;
537 	FileName = NULL;
538 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
539 			naliases, longest, bytes);
540 # ifdef LOG
541 	if (LogLevel >= 8)
542 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
543 			naliases, longest, bytes);
544 # endif LOG
545 }
546 /*
547 **  FORWARD -- Try to forward mail
548 **
549 **	This is similar but not identical to aliasing.
550 **
551 **	Parameters:
552 **		user -- the name of the user who's mail we would like
553 **			to forward to.  It must have been verified --
554 **			i.e., the q_home field must have been filled
555 **			in.
556 **		sendq -- a pointer to the head of the send queue to
557 **			put this user's aliases in.
558 **
559 **	Returns:
560 **		none.
561 **
562 **	Side Effects:
563 **		New names are added to send queues.
564 */
565 
566 forward(user, sendq)
567 	ADDRESS *user;
568 	ADDRESS **sendq;
569 {
570 	char buf[60];
571 	extern bool safefile();
572 
573 # ifdef DEBUG
574 	if (tTd(27, 1))
575 		printf("forward(%s)\n", user->q_paddr);
576 # endif DEBUG
577 
578 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
579 		return;
580 # ifdef DEBUG
581 	if (user->q_home == NULL)
582 		syserr("forward: no home");
583 # endif DEBUG
584 
585 	/* good address -- look for .forward file in home */
586 	define('z', user->q_home, CurEnv);
587 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
588 	if (!safefile(buf, user->q_uid, S_IREAD))
589 		return;
590 
591 	/* we do have an address to forward to -- do it */
592 	include(buf, "forwarding", user, sendq);
593 }
594