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