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