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