xref: /original-bsd/usr.sbin/sendmail/src/alias.c (revision c74f8e57)
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.19 (Berkeley) 03/21/90 (with DBM)";
22 #else
23 static char sccsid[] = "@(#)alias.c	5.19 (Berkeley) 03/21/90 (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	0644
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 	**	Note the unpalatable hack to see if the stat succeeded.
234 	*/
235 
236 	modtime = stb.st_mtime;
237 	(void) strcpy(buf, aliasfile);
238 	(void) strcat(buf, ".pag");
239 	stb.st_ino = 0;
240 	if (!init && (stat(buf, &stb) < 0 || stb.st_mtime < modtime || atcnt < 0))
241 	{
242 		errno = 0;
243 		if (AutoRebuild && stb.st_ino != 0 && stb.st_uid == geteuid())
244 		{
245 			init = TRUE;
246 			automatic = TRUE;
247 			message(Arpa_Info, "rebuilding alias database");
248 #ifdef LOG
249 			if (LogLevel >= 7)
250 				syslog(LOG_INFO, "rebuilding alias database");
251 #endif LOG
252 		}
253 		else
254 		{
255 #ifdef LOG
256 			if (LogLevel >= 7)
257 				syslog(LOG_INFO, "alias database out of date");
258 #endif LOG
259 			message(Arpa_Info, "Warning: alias database out of date");
260 		}
261 	}
262 
263 
264 	/*
265 	**  If necessary, load the DBM file.
266 	**	If running without DBM, load the symbol table.
267 	*/
268 
269 	if (init)
270 	{
271 #ifdef LOG
272 		if (LogLevel >= 6)
273 		{
274 			extern char *username();
275 
276 			syslog(LOG_NOTICE, "alias database %srebuilt by %s",
277 				automatic ? "auto" : "", username());
278 		}
279 #endif LOG
280 		readaliases(aliasfile, TRUE);
281 	}
282 # else DBM
283 	readaliases(aliasfile, init);
284 # endif DBM
285 }
286 /*
287 **  READALIASES -- read and process the alias file.
288 **
289 **	This routine implements the part of initaliases that occurs
290 **	when we are not going to use the DBM stuff.
291 **
292 **	Parameters:
293 **		aliasfile -- the pathname of the alias file master.
294 **		init -- if set, initialize the DBM stuff.
295 **
296 **	Returns:
297 **		none.
298 **
299 **	Side Effects:
300 **		Reads aliasfile into the symbol table.
301 **		Optionally, builds the .dir & .pag files.
302 */
303 
304 static
305 readaliases(aliasfile, init)
306 	char *aliasfile;
307 	bool init;
308 {
309 	register char *p;
310 	char *rhs;
311 	bool skipping;
312 	int naliases, bytes, longest;
313 	FILE *af;
314 	int (*oldsigint)();
315 	ADDRESS al, bl;
316 	register STAB *s;
317 	char line[BUFSIZ];
318 
319 	if ((af = fopen(aliasfile, "r")) == NULL)
320 	{
321 		if (tTd(27, 1))
322 			printf("Can't open %s\n", aliasfile);
323 		errno = 0;
324 		NoAlias++;
325 		return;
326 	}
327 
328 # ifdef DBM
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 DBM
344 
345 	/*
346 	**  If initializing, create the new DBM files.
347 	*/
348 
349 	if (init)
350 	{
351 		oldsigint = signal(SIGINT, SIG_IGN);
352 		(void) strcpy(line, aliasfile);
353 		(void) strcat(line, ".dir");
354 		if (close(creat(line, DBMMODE)) < 0)
355 		{
356 			syserr("cannot make %s", line);
357 			(void) signal(SIGINT, oldsigint);
358 			return;
359 		}
360 		(void) strcpy(line, aliasfile);
361 		(void) strcat(line, ".pag");
362 		if (close(creat(line, DBMMODE)) < 0)
363 		{
364 			syserr("cannot make %s", line);
365 			(void) signal(SIGINT, oldsigint);
366 			return;
367 		}
368 		dbminit(aliasfile);
369 	}
370 
371 	/*
372 	**  Read and interpret lines
373 	*/
374 
375 	FileName = aliasfile;
376 	LineNumber = 0;
377 	naliases = bytes = longest = 0;
378 	skipping = FALSE;
379 	while (fgets(line, sizeof (line), af) != NULL)
380 	{
381 		int lhssize, rhssize;
382 
383 		LineNumber++;
384 		p = index(line, '\n');
385 		if (p != NULL)
386 			*p = '\0';
387 		switch (line[0])
388 		{
389 		  case '#':
390 		  case '\0':
391 			skipping = FALSE;
392 			continue;
393 
394 		  case ' ':
395 		  case '\t':
396 			if (!skipping)
397 				syserr("Non-continuation line starts with space");
398 			skipping = TRUE;
399 			continue;
400 		}
401 		skipping = FALSE;
402 
403 		/*
404 		**  Process the LHS
405 		**	Find the final colon, and parse the address.
406 		**	It should resolve to a local name -- this will
407 		**	be checked later (we want to optionally do
408 		**	parsing of the RHS first to maximize error
409 		**	detection).
410 		*/
411 
412 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
413 			continue;
414 		if (*p++ != ':')
415 		{
416 			syserr("missing colon");
417 			continue;
418 		}
419 		if (parseaddr(line, &al, 1, ':') == NULL)
420 		{
421 			syserr("illegal alias name");
422 			continue;
423 		}
424 		loweraddr(&al);
425 
426 		/*
427 		**  Process the RHS.
428 		**	'al' is the internal form of the LHS address.
429 		**	'p' points to the text of the RHS.
430 		*/
431 
432 		rhs = p;
433 		for (;;)
434 		{
435 			register char c;
436 
437 			if (init && CheckAliases)
438 			{
439 				/* do parsing & compression of addresses */
440 				while (*p != '\0')
441 				{
442 					extern char *DelimChar;
443 
444 					while (isspace(*p) || *p == ',')
445 						p++;
446 					if (*p == '\0')
447 						break;
448 					if (parseaddr(p, &bl, -1, ',') == NULL)
449 						usrerr("%s... bad address", p);
450 					p = DelimChar;
451 				}
452 			}
453 			else
454 			{
455 				p = &p[strlen(p)];
456 				if (p[-1] == '\n')
457 					*--p = '\0';
458 			}
459 
460 			/* see if there should be a continuation line */
461 			c = fgetc(af);
462 			if (!feof(af))
463 				(void) ungetc(c, af);
464 			if (c != ' ' && c != '\t')
465 				break;
466 
467 			/* read continuation line */
468 			if (fgets(p, sizeof line - (p - line), af) == NULL)
469 				break;
470 			LineNumber++;
471 		}
472 		if (al.q_mailer != LocalMailer)
473 		{
474 			syserr("cannot alias non-local names");
475 			continue;
476 		}
477 
478 		/*
479 		**  Insert alias into symbol table or DBM file
480 		*/
481 
482 		lhssize = strlen(al.q_user) + 1;
483 		rhssize = strlen(rhs) + 1;
484 
485 # ifdef DBM
486 		if (init)
487 		{
488 			DATUM key, content;
489 
490 			key.dsize = lhssize;
491 			key.dptr = al.q_user;
492 			content.dsize = rhssize;
493 			content.dptr = rhs;
494 			store(key, content);
495 		}
496 		else
497 # endif DBM
498 		{
499 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
500 			s->s_alias = newstr(rhs);
501 		}
502 
503 		/* statistics */
504 		naliases++;
505 		bytes += lhssize + rhssize;
506 		if (rhssize > longest)
507 			longest = rhssize;
508 	}
509 
510 # ifdef DBM
511 	if (init)
512 	{
513 		/* add the distinquished alias "@" */
514 		DATUM key;
515 
516 		key.dsize = 2;
517 		key.dptr = "@";
518 		store(key, key);
519 
520 		/* restore the old signal */
521 		(void) signal(SIGINT, oldsigint);
522 	}
523 # endif DBM
524 
525 	/* closing the alias file drops the lock */
526 	(void) fclose(af);
527 	CurEnv->e_to = NULL;
528 	FileName = NULL;
529 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
530 			naliases, longest, bytes);
531 # ifdef LOG
532 	if (LogLevel >= 8)
533 		syslog(LOG_INFO, "%d aliases, longest %d bytes, %d bytes total",
534 			naliases, longest, bytes);
535 # endif LOG
536 }
537 /*
538 **  FORWARD -- Try to forward mail
539 **
540 **	This is similar but not identical to aliasing.
541 **
542 **	Parameters:
543 **		user -- the name of the user who's mail we would like
544 **			to forward to.  It must have been verified --
545 **			i.e., the q_home field must have been filled
546 **			in.
547 **		sendq -- a pointer to the head of the send queue to
548 **			put this user's aliases in.
549 **
550 **	Returns:
551 **		none.
552 **
553 **	Side Effects:
554 **		New names are added to send queues.
555 */
556 
557 forward(user, sendq)
558 	ADDRESS *user;
559 	ADDRESS **sendq;
560 {
561 	char buf[60];
562 	extern bool safefile();
563 
564 	if (tTd(27, 1))
565 		printf("forward(%s)\n", user->q_paddr);
566 
567 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
568 		return;
569 	if (user->q_home == NULL)
570 		syserr("forward: no home");
571 
572 	/* good address -- look for .forward file in home */
573 	define('z', user->q_home, CurEnv);
574 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
575 	if (!safefile(buf, user->q_uid, S_IREAD))
576 		return;
577 
578 	/* we do have an address to forward to -- do it */
579 	include(buf, "forwarding", user, sendq);
580 }
581