xref: /original-bsd/usr.sbin/sendmail/src/alias.c (revision 7a7a259e)
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 #ifndef lint
12 # ifdef DBM
13 static char	SccsId[] = "@(#)alias.c	5.10 (Berkeley) 01/11/86	(with DBM)";
14 # else DBM
15 static char	SccsId[] = "@(#)alias.c	5.10 (Berkeley) 01/11/86	(without DBM)";
16 # endif DBM
17 #endif not lint
18 
19 # include <pwd.h>
20 # include <sys/types.h>
21 # include <sys/stat.h>
22 # include <signal.h>
23 # include <errno.h>
24 # include "sendmail.h"
25 # ifdef FLOCK
26 # include <sys/file.h>
27 # endif FLOCK
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 
181 	if (aliasfile == NULL || stat(aliasfile, &stb) < 0)
182 	{
183 		if (aliasfile != NULL && init)
184 			syserr("Cannot open %s", aliasfile);
185 		NoAlias = TRUE;
186 		errno = 0;
187 		return;
188 	}
189 
190 # ifdef DBM
191 	/*
192 	**  Check to see that the alias file is complete.
193 	**	If not, we will assume that someone died, and it is up
194 	**	to us to rebuild it.
195 	*/
196 
197 	if (!init)
198 		dbminit(aliasfile);
199 	atcnt = SafeAlias * 2;
200 	if (atcnt > 0)
201 	{
202 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
203 		{
204 			/*
205 			**  Reinitialize alias file in case the new
206 			**  one is mv'ed in instead of cp'ed in.
207 			**
208 			**	Only works with new DBM -- old one will
209 			**	just consume file descriptors forever.
210 			**	If you have a dbmclose() it can be
211 			**	added before the sleep(30).
212 			*/
213 
214 			sleep(30);
215 # ifdef NDBM
216 			dbminit(aliasfile);
217 # endif NDBM
218 		}
219 	}
220 	else
221 		atcnt = 1;
222 
223 	/*
224 	**  See if the DBM version of the file is out of date with
225 	**  the text version.  If so, go into 'init' mode automatically.
226 	**	This only happens if our effective userid owns the DBM
227 	**	version or if the mode of the database is 666 -- this
228 	**	is an attempt to avoid protection problems.  Note the
229 	**	unpalatable hack to see if the stat succeeded.
230 	*/
231 
232 	modtime = stb.st_mtime;
233 	(void) strcpy(buf, aliasfile);
234 	(void) strcat(buf, ".pag");
235 	stb.st_ino = 0;
236 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
237 	{
238 		errno = 0;
239 		if (AutoRebuild && stb.st_ino != 0 &&
240 		    ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
241 		{
242 			init = TRUE;
243 			automatic = TRUE;
244 			message(Arpa_Info, "rebuilding alias database");
245 #ifdef LOG
246 			if (LogLevel >= 7)
247 				syslog(LOG_INFO, "rebuilding alias database");
248 #endif LOG
249 		}
250 		else
251 		{
252 #ifdef LOG
253 			if (LogLevel >= 7)
254 				syslog(LOG_INFO, "alias database out of date");
255 #endif LOG
256 			message(Arpa_Info, "Warning: alias database out of date");
257 		}
258 	}
259 
260 
261 	/*
262 	**  If necessary, load the DBM file.
263 	**	If running without DBM, load the symbol table.
264 	*/
265 
266 	if (init)
267 	{
268 #ifdef LOG
269 		if (LogLevel >= 6)
270 		{
271 			extern char *username();
272 
273 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
274 				automatic ? "auto" : "", username());
275 		}
276 #endif LOG
277 		readaliases(aliasfile, TRUE);
278 	}
279 # else DBM
280 	readaliases(aliasfile, init);
281 # endif DBM
282 }
283 /*
284 **  READALIASES -- read and process the alias file.
285 **
286 **	This routine implements the part of initaliases that occurs
287 **	when we are not going to use the DBM stuff.
288 **
289 **	Parameters:
290 **		aliasfile -- the pathname of the alias file master.
291 **		init -- if set, initialize the DBM stuff.
292 **
293 **	Returns:
294 **		none.
295 **
296 **	Side Effects:
297 **		Reads aliasfile into the symbol table.
298 **		Optionally, builds the .dir & .pag files.
299 */
300 
301 static
302 readaliases(aliasfile, init)
303 	char *aliasfile;
304 	bool init;
305 {
306 	register char *p;
307 	char *rhs;
308 	bool skipping;
309 	int naliases, bytes, longest;
310 	FILE *af;
311 	int (*oldsigint)();
312 	ADDRESS al, bl;
313 	register STAB *s;
314 	char line[BUFSIZ];
315 
316 	if ((af = fopen(aliasfile, "r")) == NULL)
317 	{
318 # ifdef DEBUG
319 		if (tTd(27, 1))
320 			printf("Can't open %s\n", aliasfile);
321 # endif
322 		errno = 0;
323 		NoAlias++;
324 		return;
325 	}
326 
327 # ifdef DBM
328 # ifdef FLOCK
329 	/* see if someone else is rebuilding the alias file already */
330 	if (flock(fileno(af), LOCK_EX | LOCK_NB) < 0 && errno == EWOULDBLOCK)
331 	{
332 		/* yes, they are -- wait until done and then return */
333 		message(Arpa_Info, "Alias file is already being rebuilt");
334 		if (OpMode != MD_INITALIAS)
335 		{
336 			/* wait for other rebuild to complete */
337 			(void) flock(fileno(af), LOCK_EX);
338 		}
339 		(void) fclose(af);
340 		errno = 0;
341 		return;
342 	}
343 # endif FLOCK
344 # endif DBM
345 
346 	/*
347 	**  If initializing, create the new DBM files.
348 	*/
349 
350 	if (init)
351 	{
352 		oldsigint = signal(SIGINT, SIG_IGN);
353 		(void) strcpy(line, aliasfile);
354 		(void) strcat(line, ".dir");
355 		if (close(creat(line, DBMMODE)) < 0)
356 		{
357 			syserr("cannot make %s", line);
358 			(void) signal(SIGINT, oldsigint);
359 			return;
360 		}
361 		dbminit(aliasfile);
362 		(void) strcpy(line, aliasfile);
363 		(void) strcat(line, ".pag");
364 		if (close(creat(line, DBMMODE)) < 0)
365 		{
366 			syserr("cannot make %s", line);
367 			(void) signal(SIGINT, oldsigint);
368 			return;
369 		}
370 	}
371 
372 	/*
373 	**  Read and interpret lines
374 	*/
375 
376 	FileName = aliasfile;
377 	LineNumber = 0;
378 	naliases = bytes = longest = 0;
379 	skipping = FALSE;
380 	while (fgets(line, sizeof (line), af) != NULL)
381 	{
382 		int lhssize, rhssize;
383 
384 		LineNumber++;
385 		p = index(line, '\n');
386 		if (p != NULL)
387 			*p = '\0';
388 		switch (line[0])
389 		{
390 		  case '#':
391 		  case '\0':
392 			skipping = FALSE;
393 			continue;
394 
395 		  case ' ':
396 		  case '\t':
397 			if (!skipping)
398 				syserr("Non-continuation line starts with space");
399 			skipping = TRUE;
400 			continue;
401 		}
402 		skipping = FALSE;
403 
404 		/*
405 		**  Process the LHS
406 		**	Find the final colon, and parse the address.
407 		**	It should resolve to a local name -- this will
408 		**	be checked later (we want to optionally do
409 		**	parsing of the RHS first to maximize error
410 		**	detection).
411 		*/
412 
413 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
414 			continue;
415 		if (*p++ != ':')
416 		{
417 			syserr("missing colon");
418 			continue;
419 		}
420 		if (parseaddr(line, &al, 1, ':') == NULL)
421 		{
422 			syserr("illegal alias name");
423 			continue;
424 		}
425 		loweraddr(&al);
426 
427 		/*
428 		**  Process the RHS.
429 		**	'al' is the internal form of the LHS address.
430 		**	'p' points to the text of the RHS.
431 		*/
432 
433 		rhs = p;
434 		for (;;)
435 		{
436 			register char c;
437 
438 			if (init && CheckAliases)
439 			{
440 				/* do parsing & compression of addresses */
441 				while (*p != '\0')
442 				{
443 					extern char *DelimChar;
444 
445 					while (isspace(*p) || *p == ',')
446 						p++;
447 					if (*p == '\0')
448 						break;
449 					if (parseaddr(p, &bl, -1, ',') == NULL)
450 						usrerr("%s... bad address", p);
451 					p = DelimChar;
452 				}
453 			}
454 			else
455 			{
456 				p = &p[strlen(p)];
457 				if (p[-1] == '\n')
458 					*--p = '\0';
459 			}
460 
461 			/* see if there should be a continuation line */
462 			c = fgetc(af);
463 			if (!feof(af))
464 				(void) ungetc(c, af);
465 			if (c != ' ' && c != '\t')
466 				break;
467 
468 			/* read continuation line */
469 			if (fgets(p, sizeof line - (p - line), af) == NULL)
470 				break;
471 			LineNumber++;
472 		}
473 		if (al.q_mailer != LocalMailer)
474 		{
475 			syserr("cannot alias non-local names");
476 			continue;
477 		}
478 
479 		/*
480 		**  Insert alias into symbol table or DBM file
481 		*/
482 
483 		lhssize = strlen(al.q_user) + 1;
484 		rhssize = strlen(rhs) + 1;
485 
486 # ifdef DBM
487 		if (init)
488 		{
489 			DATUM key, content;
490 
491 			key.dsize = lhssize;
492 			key.dptr = al.q_user;
493 			content.dsize = rhssize;
494 			content.dptr = rhs;
495 			store(key, content);
496 		}
497 		else
498 # endif DBM
499 		{
500 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
501 			s->s_alias = newstr(rhs);
502 		}
503 
504 		/* statistics */
505 		naliases++;
506 		bytes += lhssize + rhssize;
507 		if (rhssize > longest)
508 			longest = rhssize;
509 	}
510 
511 # ifdef DBM
512 	if (init)
513 	{
514 		/* add the distinquished alias "@" */
515 		DATUM key;
516 
517 		key.dsize = 2;
518 		key.dptr = "@";
519 		store(key, key);
520 
521 		/* restore the old signal */
522 		(void) signal(SIGINT, oldsigint);
523 	}
524 # endif DBM
525 
526 	/* closing the alias file drops the lock */
527 	(void) fclose(af);
528 	CurEnv->e_to = NULL;
529 	FileName = NULL;
530 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
531 			naliases, longest, bytes);
532 # ifdef LOG
533 	if (LogLevel >= 8)
534 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
535 			naliases, longest, bytes);
536 # endif LOG
537 }
538 /*
539 **  FORWARD -- Try to forward mail
540 **
541 **	This is similar but not identical to aliasing.
542 **
543 **	Parameters:
544 **		user -- the name of the user who's mail we would like
545 **			to forward to.  It must have been verified --
546 **			i.e., the q_home field must have been filled
547 **			in.
548 **		sendq -- a pointer to the head of the send queue to
549 **			put this user's aliases in.
550 **
551 **	Returns:
552 **		none.
553 **
554 **	Side Effects:
555 **		New names are added to send queues.
556 */
557 
558 forward(user, sendq)
559 	ADDRESS *user;
560 	ADDRESS **sendq;
561 {
562 	char buf[60];
563 	extern bool safefile();
564 
565 # ifdef DEBUG
566 	if (tTd(27, 1))
567 		printf("forward(%s)\n", user->q_paddr);
568 # endif DEBUG
569 
570 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
571 		return;
572 # ifdef DEBUG
573 	if (user->q_home == NULL)
574 		syserr("forward: no home");
575 # endif DEBUG
576 
577 	/* good address -- look for .forward file in home */
578 	define('z', user->q_home, CurEnv);
579 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
580 	if (!safefile(buf, user->q_uid, S_IREAD))
581 		return;
582 
583 	/* we do have an address to forward to -- do it */
584 	include(buf, "forwarding", user, sendq);
585 }
586