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