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