xref: /original-bsd/usr.sbin/sendmail/src/alias.c (revision f7be149a)
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	4.8		02/15/85	(with DBM));
9 # else DBM
10 SCCSID(@(#)alias.c	4.8		02/15/85	(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 (aliasfile == NULL || stat(aliasfile, &stb) < 0)
164 	{
165 		NoAlias = TRUE;
166 		errno = 0;
167 		return;
168 	}
169 
170 # ifdef DBM
171 	/*
172 	**  Check to see that the alias file is complete.
173 	**	If not, we will assume that someone died, and it is up
174 	**	to us to rebuild it.
175 	*/
176 
177 	dbminit(aliasfile);
178 	atcnt = SafeAlias * 2;
179 	if (atcnt > 0)
180 	{
181 		while (!init && atcnt-- >= 0 && aliaslookup("@") == NULL)
182 			sleep(30);
183 	}
184 	else
185 		atcnt = 1;
186 
187 	/*
188 	**  See if the DBM version of the file is out of date with
189 	**  the text version.  If so, go into 'init' mode automatically.
190 	**	This only happens if our effective userid owns the DBM
191 	**	version or if the mode of the database is 666 -- this
192 	**	is an attempt to avoid protection problems.  Note the
193 	**	unpalatable hack to see if the stat succeeded.
194 	*/
195 
196 	modtime = stb.st_mtime;
197 	(void) strcpy(buf, aliasfile);
198 	(void) strcat(buf, ".pag");
199 	stb.st_ino = 0;
200 	if (!init && (atcnt < 0 || stat(buf, &stb) < 0 || stb.st_mtime < modtime))
201 	{
202 		errno = 0;
203 		if (AutoRebuild && stb.st_ino != 0 &&
204 		    ((stb.st_mode & 0777) == 0666 || stb.st_uid == geteuid()))
205 		{
206 			init = TRUE;
207 			message(Arpa_Info, "rebuilding alias database");
208 		}
209 		else
210 		{
211 			bool oldverb = Verbose;
212 
213 			Verbose = TRUE;
214 			message(Arpa_Info, "Warning: alias database out of date");
215 			Verbose = oldverb;
216 		}
217 	}
218 
219 	/*
220 	**  If initializing, create the new files.
221 	**	We should lock the alias file here to prevent other
222 	**	instantiations of sendmail from reading an incomplete
223 	**	file -- or worse yet, doing a concurrent initialize.
224 	*/
225 
226 	if (init)
227 	{
228 		oldsigint = signal(SIGINT, SIG_IGN);
229 		(void) strcpy(buf, aliasfile);
230 		(void) strcat(buf, ".dir");
231 		if (close(creat(buf, DBMMODE)) < 0)
232 		{
233 			syserr("cannot make %s", buf);
234 			(void) signal(SIGINT, oldsigint);
235 			return;
236 		}
237 		(void) strcpy(buf, aliasfile);
238 		(void) strcat(buf, ".pag");
239 		if (close(creat(buf, DBMMODE)) < 0)
240 		{
241 			syserr("cannot make %s", buf);
242 			(void) signal(SIGINT, oldsigint);
243 			return;
244 		}
245 	}
246 
247 	/*
248 	**  If necessary, load the DBM file.
249 	**	If running without DBM, load the symbol table.
250 	**	After loading the DBM file, add the distinquished alias "@".
251 	*/
252 
253 	if (init)
254 	{
255 		DATUM key;
256 
257 		readaliases(aliasfile, TRUE);
258 		key.dsize = 2;
259 		key.dptr = "@";
260 		store(key, key);
261 		(void) signal(SIGINT, oldsigint);
262 	}
263 # else DBM
264 	readaliases(aliasfile, init);
265 # endif DBM
266 }
267 /*
268 **  READALIASES -- read and process the alias file.
269 **
270 **	This routine implements the part of initaliases that occurs
271 **	when we are not going to use the DBM stuff.
272 **
273 **	Parameters:
274 **		aliasfile -- the pathname of the alias file master.
275 **		init -- if set, initialize the DBM stuff.
276 **
277 **	Returns:
278 **		none.
279 **
280 **	Side Effects:
281 **		Reads aliasfile into the symbol table.
282 **		Optionally, builds the .dir & .pag files.
283 */
284 
285 static
286 readaliases(aliasfile, init)
287 	char *aliasfile;
288 	bool init;
289 {
290 	register char *p;
291 	char *p2;
292 	char *rhs;
293 	bool skipping;
294 	int naliases, bytes, longest;
295 	FILE *af;
296 	ADDRESS al, bl;
297 	register STAB *s;
298 	char line[BUFSIZ];
299 
300 	if ((af = fopen(aliasfile, "r")) == NULL)
301 	{
302 # ifdef DEBUG
303 		if (tTd(27, 1))
304 			printf("Can't open %s\n", aliasfile);
305 # endif
306 		errno = 0;
307 		NoAlias++;
308 		return;
309 	}
310 
311 	/*
312 	**  Read and interpret lines
313 	*/
314 
315 	FileName = aliasfile;
316 	LineNumber = 0;
317 	naliases = bytes = longest = 0;
318 	skipping = FALSE;
319 	while (fgets(line, sizeof (line), af) != NULL)
320 	{
321 		int lhssize, rhssize;
322 
323 		LineNumber++;
324 		switch (line[0])
325 		{
326 		  case '#':
327 		  case '\n':
328 		  case '\0':
329 			skipping = FALSE;
330 			continue;
331 
332 		  case ' ':
333 		  case '\t':
334 			if (!skipping)
335 				syserr("Non-continuation line starts with space");
336 			skipping = TRUE;
337 			continue;
338 		}
339 		skipping = FALSE;
340 
341 		/*
342 		**  Process the LHS
343 		**	Find the final colon, and parse the address.
344 		**	It should resolve to a local name -- this will
345 		**	be checked later (we want to optionally do
346 		**	parsing of the RHS first to maximize error
347 		**	detection).
348 		*/
349 
350 		for (p = line; *p != '\0' && *p != ':' && *p != '\n'; p++)
351 			continue;
352 		if (*p++ != ':')
353 		{
354 			syserr("missing colon");
355 			continue;
356 		}
357 		if (parseaddr(line, &al, 1, ':') == NULL)
358 		{
359 			syserr("illegal alias name");
360 			continue;
361 		}
362 		loweraddr(&al);
363 
364 		/*
365 		**  Process the RHS.
366 		**	'al' is the internal form of the LHS address.
367 		**	'p' points to the text of the RHS.
368 		*/
369 
370 		rhs = p;
371 		for (;;)
372 		{
373 			register char c;
374 
375 			if (init)
376 			{
377 				/* do parsing & compression of addresses */
378 				c = *p;
379 				while (c != '\0')
380 				{
381 					p2 = p;
382 					while (*p != '\n' && *p != ',' && *p != '\0')
383 						p++;
384 					c = *p;
385 					if (c == '\n')
386 						c = '\0';
387 					*p = '\0';
388 					if (*p2 != '\0')
389 						(void) parseaddr(p2, &bl, -1, ',');
390 					if (c != '\0')
391 						*p++ = c;
392 				}
393 			}
394 			else
395 			{
396 				p = &p[strlen(p)];
397 				if (p[-1] == '\n')
398 					*--p = '\0';
399 			}
400 
401 			/* see if there should be a continuation line */
402 			c = fgetc(af);
403 			if (!feof(af))
404 				(void) ungetc(c, af);
405 			if (c != ' ' && c != '\t')
406 				break;
407 
408 			/* read continuation line */
409 			if (fgets(p, sizeof line - (p - line), af) == NULL)
410 				break;
411 			LineNumber++;
412 		}
413 		if (al.q_mailer != LocalMailer)
414 		{
415 			syserr("cannot alias non-local names");
416 			continue;
417 		}
418 
419 		/*
420 		**  Insert alias into symbol table or DBM file
421 		*/
422 
423 		lhssize = strlen(al.q_user) + 1;
424 		rhssize = strlen(rhs) + 1;
425 
426 # ifdef DBM
427 		if (init)
428 		{
429 			DATUM key, content;
430 
431 			key.dsize = lhssize;
432 			key.dptr = al.q_user;
433 			content.dsize = rhssize;
434 			content.dptr = rhs;
435 			store(key, content);
436 		}
437 		else
438 # endif DBM
439 		{
440 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
441 			s->s_alias = newstr(rhs);
442 		}
443 
444 		/* statistics */
445 		naliases++;
446 		bytes += lhssize + rhssize;
447 		if (rhssize > longest)
448 			longest = rhssize;
449 	}
450 	(void) fclose(af);
451 	CurEnv->e_to = NULL;
452 	FileName = NULL;
453 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
454 			naliases, longest, bytes);
455 }
456 /*
457 **  FORWARD -- Try to forward mail
458 **
459 **	This is similar but not identical to aliasing.
460 **
461 **	Parameters:
462 **		user -- the name of the user who's mail we would like
463 **			to forward to.  It must have been verified --
464 **			i.e., the q_home field must have been filled
465 **			in.
466 **		sendq -- a pointer to the head of the send queue to
467 **			put this user's aliases in.
468 **
469 **	Returns:
470 **		none.
471 **
472 **	Side Effects:
473 **		New names are added to send queues.
474 */
475 
476 forward(user, sendq)
477 	ADDRESS *user;
478 	ADDRESS **sendq;
479 {
480 	char buf[60];
481 	extern bool safefile();
482 
483 # ifdef DEBUG
484 	if (tTd(27, 1))
485 		printf("forward(%s)\n", user->q_paddr);
486 # endif DEBUG
487 
488 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
489 		return;
490 # ifdef DEBUG
491 	if (user->q_home == NULL)
492 		syserr("forward: no home");
493 # endif DEBUG
494 
495 	/* good address -- look for .forward file in home */
496 	define('z', user->q_home, CurEnv);
497 	expand("\001z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
498 	if (!safefile(buf, user->q_uid, S_IREAD))
499 		return;
500 
501 	/* we do have an address to forward to -- do it */
502 	include(buf, "forwarding", user, sendq);
503 }
504