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