xref: /original-bsd/usr.sbin/sendmail/src/alias.c (revision 3f839ad3)
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.42		11/28/82	(with DBM));
9 # else DBM
10 SCCSID(@(#)alias.c	3.42		11/28/82	(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 	sendto(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 		*p++ = '\0';
351 		if (parse(line, &al, 1) == NULL)
352 		{
353 			*--p = ':';
354 			syserr("illegal alias name");
355 			continue;
356 		}
357 
358 		/*
359 		**  Process the RHS.
360 		**	'al' is the internal form of the LHS address.
361 		**	'p' points to the text of the RHS.
362 		*/
363 
364 		rhs = p;
365 		for (;;)
366 		{
367 			register char c;
368 
369 			if (init)
370 			{
371 				/* do parsing & compression of addresses */
372 				c = *p;
373 				while (c != '\0')
374 				{
375 					p2 = p;
376 					while (*p != '\n' && *p != ',' && *p != '\0')
377 						p++;
378 					c = *p;
379 					*p++ = '\0';
380 					if (c == '\n')
381 						c = '\0';
382 					if (*p2 == '\0')
383 					{
384 						p[-1] = c;
385 						continue;
386 					}
387 					(void) parse(p2, &bl, -1);
388 					p[-1] = c;
389 					while (isspace(*p))
390 						p++;
391 				}
392 			}
393 			else
394 				p = &p[strlen(p)];
395 
396 			/* see if there should be a continuation line */
397 			c = fgetc(af);
398 			if (!feof(af))
399 				(void) ungetc(c, af);
400 			if (c != ' ' && c != '\t')
401 				break;
402 
403 			/* read continuation line */
404 			p--;
405 			if (fgets(p, sizeof line - (p - line), af) == NULL)
406 				break;
407 			LineNumber++;
408 		}
409 		if (al.q_mailer != LocalMailer)
410 		{
411 			syserr("cannot alias non-local names");
412 			continue;
413 		}
414 
415 		/*
416 		**  Insert alias into symbol table or DBM file
417 		*/
418 
419 		lhssize = strlen(al.q_user) + 1;
420 		rhssize = strlen(rhs) + 1;
421 
422 # ifdef DBM
423 		if (init)
424 		{
425 			DATUM key, content;
426 
427 			key.dsize = lhssize;
428 			key.dptr = al.q_user;
429 			content.dsize = rhssize;
430 			content.dptr = rhs;
431 			store(key, content);
432 		}
433 		else
434 # endif DBM
435 		{
436 			s = stab(al.q_user, ST_ALIAS, ST_ENTER);
437 			s->s_alias = newstr(rhs);
438 		}
439 
440 		/* statistics */
441 		naliases++;
442 		bytes += lhssize + rhssize;
443 		if (rhssize > longest)
444 			longest = rhssize;
445 	}
446 	(void) fclose(af);
447 	CurEnv->e_to = NULL;
448 	FileName = NULL;
449 	message(Arpa_Info, "%d aliases, longest %d bytes, %d bytes total",
450 			naliases, longest, bytes);
451 }
452 /*
453 **  FORWARD -- Try to forward mail
454 **
455 **	This is similar but not identical to aliasing.
456 **
457 **	Parameters:
458 **		user -- the name of the user who's mail we would like
459 **			to forward to.  It must have been verified --
460 **			i.e., the q_home field must have been filled
461 **			in.
462 **		sendq -- a pointer to the head of the send queue to
463 **			put this user's aliases in.
464 **
465 **	Returns:
466 **		none.
467 **
468 **	Side Effects:
469 **		New names are added to send queues.
470 */
471 
472 forward(user, sendq)
473 	ADDRESS *user;
474 	ADDRESS **sendq;
475 {
476 	char buf[60];
477 	extern bool safefile();
478 
479 # ifdef DEBUG
480 	if (tTd(27, 1))
481 		printf("forward(%s)\n", user->q_paddr);
482 # endif DEBUG
483 
484 	if (user->q_mailer != LocalMailer || bitset(QBADADDR, user->q_flags))
485 		return;
486 # ifdef DEBUG
487 	if (user->q_home == NULL)
488 		syserr("forward: no home");
489 # endif DEBUG
490 
491 	/* good address -- look for .forward file in home */
492 	define('z', user->q_home, CurEnv);
493 	expand("$z/.forward", buf, &buf[sizeof buf - 1], CurEnv);
494 	if (!safefile(buf, user->q_uid, S_IREAD))
495 		return;
496 
497 	/* we do have an address to forward to -- do it */
498 	include(buf, "forwarding", user, sendq);
499 }
500