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