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