xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 58050)
1 /*
2  * Copyright (c) 1983 Eric P. Allman
3  * Copyright (c) 1988 Regents of the University of California.
4  * All rights reserved.
5  *
6  * %sccs.include.redist.c%
7  */
8 
9 #ifndef lint
10 static char sccsid[] = "@(#)readcf.c	6.8 (Berkeley) 02/18/93";
11 #endif /* not lint */
12 
13 # include "sendmail.h"
14 # include <sys/stat.h>
15 # include <unistd.h>
16 #ifdef NAMED_BIND
17 # include <arpa/nameser.h>
18 # include <resolv.h>
19 #endif
20 
21 /* System 5 compatibility */
22 #ifndef S_ISREG
23 #define S_ISREG(foo)	((foo & S_IFREG) == S_IFREG)
24 #endif
25 #ifndef S_IWGRP
26 #define S_IWGRP		020
27 #endif
28 #ifndef S_IWOTH
29 #define S_IWOTH		002
30 #endif
31 
32 /*
33 **  READCF -- read control file.
34 **
35 **	This routine reads the control file and builds the internal
36 **	form.
37 **
38 **	The file is formatted as a sequence of lines, each taken
39 **	atomically.  The first character of each line describes how
40 **	the line is to be interpreted.  The lines are:
41 **		Dxval		Define macro x to have value val.
42 **		Cxword		Put word into class x.
43 **		Fxfile [fmt]	Read file for lines to put into
44 **				class x.  Use scanf string 'fmt'
45 **				or "%s" if not present.  Fmt should
46 **				only produce one string-valued result.
47 **		Hname: value	Define header with field-name 'name'
48 **				and value as specified; this will be
49 **				macro expanded immediately before
50 **				use.
51 **		Sn		Use rewriting set n.
52 **		Rlhs rhs	Rewrite addresses that match lhs to
53 **				be rhs.
54 **		Mn arg=val...	Define mailer.  n is the internal name.
55 **				Args specify mailer parameters.
56 **		Oxvalue		Set option x to value.
57 **		Pname=value	Set precedence name to value.
58 **		Vversioncode	Version level of configuration syntax.
59 **		Kmapname mapclass arguments....
60 **				Define keyed lookup of a given class.
61 **				Arguments are class dependent.
62 **
63 **	Parameters:
64 **		cfname -- control file name.
65 **		safe -- TRUE if this is the system config file;
66 **			FALSE otherwise.
67 **		e -- the main envelope.
68 **
69 **	Returns:
70 **		none.
71 **
72 **	Side Effects:
73 **		Builds several internal tables.
74 */
75 
76 readcf(cfname, safe, e)
77 	char *cfname;
78 	bool safe;
79 	register ENVELOPE *e;
80 {
81 	FILE *cf;
82 	int ruleset = 0;
83 	char *q;
84 	char **pv;
85 	struct rewrite *rwp = NULL;
86 	char *bp;
87 	int nfuzzy;
88 	char buf[MAXLINE];
89 	register char *p;
90 	extern char **prescan();
91 	extern char **copyplist();
92 	struct stat statb;
93 	char exbuf[MAXLINE];
94 	char pvpbuf[PSBUFSIZE];
95 	extern char *fgetfolded();
96 	extern char *munchstring();
97 	extern void makemapentry();
98 
99 	FileName = cfname;
100 	LineNumber = 0;
101 
102 	cf = fopen(cfname, "r");
103 	if (cf == NULL)
104 	{
105 		syserr("cannot open");
106 		exit(EX_OSFILE);
107 	}
108 
109 	if (fstat(fileno(cf), &statb) < 0)
110 	{
111 		syserr("cannot fstat");
112 		exit(EX_OSFILE);
113 	}
114 
115 	if (!S_ISREG(statb.st_mode))
116 	{
117 		syserr("not a plain file");
118 		exit(EX_OSFILE);
119 	}
120 
121 	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
122 	{
123 		if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
124 			fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
125 				FileName);
126 #ifdef LOG
127 		if (LogLevel > 0)
128 			syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
129 				FileName);
130 #endif
131 	}
132 
133 	while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
134 	{
135 		if (bp[0] == '#')
136 		{
137 			if (bp != buf)
138 				free(bp);
139 			continue;
140 		}
141 
142 		/* map $ into \201 for macro expansion */
143 		for (p = bp; *p != '\0'; p++)
144 		{
145 			if (*p == '#' && p > bp && ConfigLevel >= 3)
146 			{
147 				/* this is an on-line comment */
148 				register char *e;
149 
150 				switch (*--p & 0377)
151 				{
152 				  case MACROEXPAND:
153 					/* it's from $# -- let it go through */
154 					p++;
155 					break;
156 
157 				  case '\\':
158 					/* it's backslash escaped */
159 					(void) strcpy(p, p + 1);
160 					break;
161 
162 				  default:
163 					/* delete preceeding white space */
164 					while (isascii(*p) && isspace(*p) && p > bp)
165 						p--;
166 					if ((e = strchr(++p, '\n')) != NULL)
167 						(void) strcpy(p, e);
168 					else
169 						p[0] = p[1] = '\0';
170 					break;
171 				}
172 				continue;
173 			}
174 
175 			if (*p != '$')
176 				continue;
177 
178 			if (p[1] == '$')
179 			{
180 				/* actual dollar sign.... */
181 				(void) strcpy(p, p + 1);
182 				continue;
183 			}
184 
185 			/* convert to macro expansion character */
186 			*p = MACROEXPAND;
187 		}
188 
189 		/* interpret this line */
190 		switch (bp[0])
191 		{
192 		  case '\0':
193 		  case '#':		/* comment */
194 			break;
195 
196 		  case 'R':		/* rewriting rule */
197 			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
198 				continue;
199 
200 			if (*p == '\0')
201 			{
202 				syserr("invalid rewrite line \"%s\"", bp);
203 				break;
204 			}
205 
206 			/* allocate space for the rule header */
207 			if (rwp == NULL)
208 			{
209 				RewriteRules[ruleset] = rwp =
210 					(struct rewrite *) xalloc(sizeof *rwp);
211 			}
212 			else
213 			{
214 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
215 				rwp = rwp->r_next;
216 			}
217 			rwp->r_next = NULL;
218 
219 			/* expand and save the LHS */
220 			*p = '\0';
221 			expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
222 			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf);
223 			nfuzzy = 0;
224 			if (rwp->r_lhs != NULL)
225 			{
226 				register char **ap;
227 
228 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
229 
230 				/* count the number of fuzzy matches in LHS */
231 				for (ap = rwp->r_lhs; *ap != NULL; ap++)
232 				{
233 					switch (**ap & 0377)
234 					{
235 					  case MATCHZANY:
236 					  case MATCHANY:
237 					  case MATCHONE:
238 					  case MATCHCLASS:
239 					  case MATCHNCLASS:
240 						nfuzzy++;
241 					}
242 				}
243 			}
244 			else
245 				syserr("R line: null LHS");
246 
247 			/* expand and save the RHS */
248 			while (*++p == '\t')
249 				continue;
250 			q = p;
251 			while (*p != '\0' && *p != '\t')
252 				p++;
253 			*p = '\0';
254 			expand(q, exbuf, &exbuf[sizeof exbuf], e);
255 			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf);
256 			if (rwp->r_rhs != NULL)
257 			{
258 				register char **ap;
259 
260 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
261 
262 				/* check no out-of-bounds replacements */
263 				nfuzzy += '0';
264 				for (ap = rwp->r_rhs; *ap != NULL; ap++)
265 				{
266 					if ((**ap & 0377) != MATCHREPL)
267 						continue;
268 					if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
269 					{
270 						syserr("replacement $%c out of bounds",
271 							(*ap)[1]);
272 					}
273 				}
274 			}
275 			else
276 				syserr("R line: null RHS");
277 			break;
278 
279 		  case 'S':		/* select rewriting set */
280 			ruleset = atoi(&bp[1]);
281 			if (ruleset >= MAXRWSETS || ruleset < 0)
282 			{
283 				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
284 				ruleset = 0;
285 			}
286 			rwp = NULL;
287 			break;
288 
289 		  case 'D':		/* macro definition */
290 			define(bp[1], newstr(munchstring(&bp[2])), e);
291 			break;
292 
293 		  case 'H':		/* required header line */
294 			(void) chompheader(&bp[1], TRUE, e);
295 			break;
296 
297 		  case 'C':		/* word class */
298 		  case 'F':		/* word class from file */
299 			/* read list of words from argument or file */
300 			if (bp[0] == 'F')
301 			{
302 				/* read from file */
303 				for (p = &bp[2];
304 				     *p != '\0' && !(isascii(*p) && isspace(*p));
305 				     p++)
306 					continue;
307 				if (*p == '\0')
308 					p = "%s";
309 				else
310 				{
311 					*p = '\0';
312 					while (isascii(*++p) && isspace(*p))
313 						continue;
314 				}
315 				fileclass(bp[1], &bp[2], p, safe);
316 				break;
317 			}
318 
319 			/* scan the list of words and set class for all */
320 			for (p = &bp[2]; *p != '\0'; )
321 			{
322 				register char *wd;
323 				char delim;
324 
325 				while (*p != '\0' && isascii(*p) && isspace(*p))
326 					p++;
327 				wd = p;
328 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
329 					p++;
330 				delim = *p;
331 				*p = '\0';
332 				if (wd[0] != '\0')
333 					setclass(bp[1], wd);
334 				*p = delim;
335 			}
336 			break;
337 
338 		  case 'M':		/* define mailer */
339 			makemailer(&bp[1]);
340 			break;
341 
342 		  case 'O':		/* set option */
343 			setoption(bp[1], &bp[2], safe, FALSE);
344 			break;
345 
346 		  case 'P':		/* set precedence */
347 			if (NumPriorities >= MAXPRIORITIES)
348 			{
349 				toomany('P', MAXPRIORITIES);
350 				break;
351 			}
352 			for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
353 				continue;
354 			if (*p == '\0')
355 				goto badline;
356 			*p = '\0';
357 			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
358 			Priorities[NumPriorities].pri_val = atoi(++p);
359 			NumPriorities++;
360 			break;
361 
362 		  case 'T':		/* trusted user(s) */
363 			p = &bp[1];
364 			while (*p != '\0')
365 			{
366 				while (isascii(*p) && isspace(*p))
367 					p++;
368 				q = p;
369 				while (*p != '\0' && !(isascii(*p) && isspace(*p)))
370 					p++;
371 				if (*p != '\0')
372 					*p++ = '\0';
373 				if (*q == '\0')
374 					continue;
375 				for (pv = TrustedUsers; *pv != NULL; pv++)
376 					continue;
377 				if (pv >= &TrustedUsers[MAXTRUST])
378 				{
379 					toomany('T', MAXTRUST);
380 					break;
381 				}
382 				*pv = newstr(q);
383 			}
384 			break;
385 
386 		  case 'V':		/* configuration syntax version */
387 			ConfigLevel = atoi(&bp[1]);
388 			break;
389 
390 		  case 'K':
391 			makemapentry(&bp[1]);
392 			break;
393 
394 		  default:
395 		  badline:
396 			syserr("unknown control line \"%s\"", bp);
397 		}
398 		if (bp != buf)
399 			free(bp);
400 	}
401 	if (ferror(cf))
402 	{
403 		syserr("I/O read error", cfname);
404 		exit(EX_OSFILE);
405 	}
406 	fclose(cf);
407 	FileName = NULL;
408 
409 	if (stab("host", ST_MAP, ST_FIND) == NULL)
410 	{
411 		/* user didn't initialize: set up host map */
412 		strcpy(buf, "host host");
413 		if (ConfigLevel >= 2)
414 			strcat(buf, " -a.");
415 		makemapentry(buf);
416 	}
417 }
418 /*
419 **  TOOMANY -- signal too many of some option
420 **
421 **	Parameters:
422 **		id -- the id of the error line
423 **		maxcnt -- the maximum possible values
424 **
425 **	Returns:
426 **		none.
427 **
428 **	Side Effects:
429 **		gives a syserr.
430 */
431 
432 toomany(id, maxcnt)
433 	char id;
434 	int maxcnt;
435 {
436 	syserr("too many %c lines, %d max", id, maxcnt);
437 }
438 /*
439 **  FILECLASS -- read members of a class from a file
440 **
441 **	Parameters:
442 **		class -- class to define.
443 **		filename -- name of file to read.
444 **		fmt -- scanf string to use for match.
445 **
446 **	Returns:
447 **		none
448 **
449 **	Side Effects:
450 **
451 **		puts all lines in filename that match a scanf into
452 **			the named class.
453 */
454 
455 fileclass(class, filename, fmt, safe)
456 	int class;
457 	char *filename;
458 	char *fmt;
459 	bool safe;
460 {
461 	FILE *f;
462 	struct stat stbuf;
463 	char buf[MAXLINE];
464 
465 	if (stat(filename, &stbuf) < 0)
466 	{
467 		syserr("fileclass: cannot stat %s", filename);
468 		return;
469 	}
470 	if (!S_ISREG(stbuf.st_mode))
471 	{
472 		syserr("fileclass: %s not a regular file", filename);
473 		return;
474 	}
475 	if (!safe && access(filename, R_OK) < 0)
476 	{
477 		syserr("fileclass: access denied on %s", filename);
478 		return;
479 	}
480 	f = fopen(filename, "r");
481 	if (f == NULL)
482 	{
483 		syserr("fileclass: cannot open %s", filename);
484 		return;
485 	}
486 
487 	while (fgets(buf, sizeof buf, f) != NULL)
488 	{
489 		register STAB *s;
490 		register char *p;
491 # ifdef SCANF
492 		char wordbuf[MAXNAME+1];
493 
494 		if (sscanf(buf, fmt, wordbuf) != 1)
495 			continue;
496 		p = wordbuf;
497 # else /* SCANF */
498 		p = buf;
499 # endif /* SCANF */
500 
501 		/*
502 		**  Break up the match into words.
503 		*/
504 
505 		while (*p != '\0')
506 		{
507 			register char *q;
508 
509 			/* strip leading spaces */
510 			while (isascii(*p) && isspace(*p))
511 				p++;
512 			if (*p == '\0')
513 				break;
514 
515 			/* find the end of the word */
516 			q = p;
517 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
518 				p++;
519 			if (*p != '\0')
520 				*p++ = '\0';
521 
522 			/* enter the word in the symbol table */
523 			s = stab(q, ST_CLASS, ST_ENTER);
524 			setbitn(class, s->s_class);
525 		}
526 	}
527 
528 	(void) fclose(f);
529 }
530 /*
531 **  MAKEMAILER -- define a new mailer.
532 **
533 **	Parameters:
534 **		line -- description of mailer.  This is in labeled
535 **			fields.  The fields are:
536 **			   P -- the path to the mailer
537 **			   F -- the flags associated with the mailer
538 **			   A -- the argv for this mailer
539 **			   S -- the sender rewriting set
540 **			   R -- the recipient rewriting set
541 **			   E -- the eol string
542 **			The first word is the canonical name of the mailer.
543 **
544 **	Returns:
545 **		none.
546 **
547 **	Side Effects:
548 **		enters the mailer into the mailer table.
549 */
550 
551 makemailer(line)
552 	char *line;
553 {
554 	register char *p;
555 	register struct mailer *m;
556 	register STAB *s;
557 	int i;
558 	char fcode;
559 	auto char *endp;
560 	extern int NextMailer;
561 	extern char **makeargv();
562 	extern char *munchstring();
563 	extern char *DelimChar;
564 	extern long atol();
565 
566 	/* allocate a mailer and set up defaults */
567 	m = (struct mailer *) xalloc(sizeof *m);
568 	bzero((char *) m, sizeof *m);
569 	m->m_eol = "\n";
570 
571 	/* collect the mailer name */
572 	for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
573 		continue;
574 	if (*p != '\0')
575 		*p++ = '\0';
576 	m->m_name = newstr(line);
577 
578 	/* now scan through and assign info from the fields */
579 	while (*p != '\0')
580 	{
581 		while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
582 			p++;
583 
584 		/* p now points to field code */
585 		fcode = *p;
586 		while (*p != '\0' && *p != '=' && *p != ',')
587 			p++;
588 		if (*p++ != '=')
589 		{
590 			syserr("mailer %s: `=' expected", m->m_name);
591 			return;
592 		}
593 		while (isascii(*p) && isspace(*p))
594 			p++;
595 
596 		/* p now points to the field body */
597 		p = munchstring(p);
598 
599 		/* install the field into the mailer struct */
600 		switch (fcode)
601 		{
602 		  case 'P':		/* pathname */
603 			m->m_mailer = newstr(p);
604 			break;
605 
606 		  case 'F':		/* flags */
607 			for (; *p != '\0'; p++)
608 				if (!(isascii(*p) && isspace(*p)))
609 					setbitn(*p, m->m_flags);
610 			break;
611 
612 		  case 'S':		/* sender rewriting ruleset */
613 		  case 'R':		/* recipient rewriting ruleset */
614 			i = strtol(p, &endp, 10);
615 			if (i < 0 || i >= MAXRWSETS)
616 			{
617 				syserr("invalid rewrite set, %d max", MAXRWSETS);
618 				return;
619 			}
620 			if (fcode == 'S')
621 				m->m_sh_rwset = m->m_se_rwset = i;
622 			else
623 				m->m_rh_rwset = m->m_re_rwset = i;
624 
625 			p = endp;
626 			if (*p == '/')
627 			{
628 				i = strtol(p, NULL, 10);
629 				if (i < 0 || i >= MAXRWSETS)
630 				{
631 					syserr("invalid rewrite set, %d max",
632 						MAXRWSETS);
633 					return;
634 				}
635 				if (fcode == 'S')
636 					m->m_sh_rwset = i;
637 				else
638 					m->m_rh_rwset = i;
639 			}
640 			break;
641 
642 		  case 'E':		/* end of line string */
643 			m->m_eol = newstr(p);
644 			break;
645 
646 		  case 'A':		/* argument vector */
647 			m->m_argv = makeargv(p);
648 			break;
649 
650 		  case 'M':		/* maximum message size */
651 			m->m_maxsize = atol(p);
652 			break;
653 
654 		  case 'L':		/* maximum line length */
655 			m->m_linelimit = atoi(p);
656 			break;
657 		}
658 
659 		p = DelimChar;
660 	}
661 
662 	/* do some heuristic cleanup for back compatibility */
663 	if (bitnset(M_LIMITS, m->m_flags))
664 	{
665 		if (m->m_linelimit == 0)
666 			m->m_linelimit = SMTPLINELIM;
667 		if (ConfigLevel < 2)
668 			setbitn(M_7BITS, m->m_flags);
669 	}
670 
671 	if (NextMailer >= MAXMAILERS)
672 	{
673 		syserr("too many mailers defined (%d max)", MAXMAILERS);
674 		return;
675 	}
676 
677 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
678 	if (s->s_mailer != NULL)
679 	{
680 		i = s->s_mailer->m_mno;
681 		free(s->s_mailer);
682 	}
683 	else
684 	{
685 		i = NextMailer++;
686 	}
687 	Mailer[i] = s->s_mailer = m;
688 	m->m_mno = i;
689 }
690 /*
691 **  MUNCHSTRING -- translate a string into internal form.
692 **
693 **	Parameters:
694 **		p -- the string to munch.
695 **
696 **	Returns:
697 **		the munched string.
698 **
699 **	Side Effects:
700 **		Sets "DelimChar" to point to the string that caused us
701 **		to stop.
702 */
703 
704 char *
705 munchstring(p)
706 	register char *p;
707 {
708 	register char *q;
709 	bool backslash = FALSE;
710 	bool quotemode = FALSE;
711 	static char buf[MAXLINE];
712 	extern char *DelimChar;
713 
714 	for (q = buf; *p != '\0'; p++)
715 	{
716 		if (backslash)
717 		{
718 			/* everything is roughly literal */
719 			backslash = FALSE;
720 			switch (*p)
721 			{
722 			  case 'r':		/* carriage return */
723 				*q++ = '\r';
724 				continue;
725 
726 			  case 'n':		/* newline */
727 				*q++ = '\n';
728 				continue;
729 
730 			  case 'f':		/* form feed */
731 				*q++ = '\f';
732 				continue;
733 
734 			  case 'b':		/* backspace */
735 				*q++ = '\b';
736 				continue;
737 			}
738 			*q++ = *p;
739 		}
740 		else
741 		{
742 			if (*p == '\\')
743 				backslash = TRUE;
744 			else if (*p == '"')
745 				quotemode = !quotemode;
746 			else if (quotemode || *p != ',')
747 				*q++ = *p;
748 			else
749 				break;
750 		}
751 	}
752 
753 	DelimChar = p;
754 	*q++ = '\0';
755 	return (buf);
756 }
757 /*
758 **  MAKEARGV -- break up a string into words
759 **
760 **	Parameters:
761 **		p -- the string to break up.
762 **
763 **	Returns:
764 **		a char **argv (dynamically allocated)
765 **
766 **	Side Effects:
767 **		munges p.
768 */
769 
770 char **
771 makeargv(p)
772 	register char *p;
773 {
774 	char *q;
775 	int i;
776 	char **avp;
777 	char *argv[MAXPV + 1];
778 
779 	/* take apart the words */
780 	i = 0;
781 	while (*p != '\0' && i < MAXPV)
782 	{
783 		q = p;
784 		while (*p != '\0' && !(isascii(*p) && isspace(*p)))
785 			p++;
786 		while (isascii(*p) && isspace(*p))
787 			*p++ = '\0';
788 		argv[i++] = newstr(q);
789 	}
790 	argv[i++] = NULL;
791 
792 	/* now make a copy of the argv */
793 	avp = (char **) xalloc(sizeof *avp * i);
794 	bcopy((char *) argv, (char *) avp, sizeof *avp * i);
795 
796 	return (avp);
797 }
798 /*
799 **  PRINTRULES -- print rewrite rules (for debugging)
800 **
801 **	Parameters:
802 **		none.
803 **
804 **	Returns:
805 **		none.
806 **
807 **	Side Effects:
808 **		prints rewrite rules.
809 */
810 
811 printrules()
812 {
813 	register struct rewrite *rwp;
814 	register int ruleset;
815 
816 	for (ruleset = 0; ruleset < 10; ruleset++)
817 	{
818 		if (RewriteRules[ruleset] == NULL)
819 			continue;
820 		printf("\n----Rule Set %d:", ruleset);
821 
822 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
823 		{
824 			printf("\nLHS:");
825 			printav(rwp->r_lhs);
826 			printf("RHS:");
827 			printav(rwp->r_rhs);
828 		}
829 	}
830 }
831 
832 /*
833 **  SETOPTION -- set global processing option
834 **
835 **	Parameters:
836 **		opt -- option name.
837 **		val -- option value (as a text string).
838 **		safe -- set if this came from a configuration file.
839 **			Some options (if set from the command line) will
840 **			reset the user id to avoid security problems.
841 **		sticky -- if set, don't let other setoptions override
842 **			this value.
843 **
844 **	Returns:
845 **		none.
846 **
847 **	Side Effects:
848 **		Sets options as implied by the arguments.
849 */
850 
851 static BITMAP	StickyOpt;		/* set if option is stuck */
852 
853 
854 #ifdef NAMED_BIND
855 
856 struct resolverflags
857 {
858 	char	*rf_name;	/* name of the flag */
859 	long	rf_bits;	/* bits to set/clear */
860 } ResolverFlags[] =
861 {
862 	"debug",	RES_DEBUG,
863 	"aaonly",	RES_AAONLY,
864 	"usevc",	RES_USEVC,
865 	"primary",	RES_PRIMARY,
866 	"igntc",	RES_IGNTC,
867 	"recurse",	RES_RECURSE,
868 	"defnames",	RES_DEFNAMES,
869 	"stayopen",	RES_STAYOPEN,
870 	"dnsrch",	RES_DNSRCH,
871 	NULL,		0
872 };
873 
874 #endif
875 
876 setoption(opt, val, safe, sticky)
877 	char opt;
878 	char *val;
879 	bool safe;
880 	bool sticky;
881 {
882 	register char *p;
883 	extern bool atobool();
884 	extern time_t convtime();
885 	extern int QueueLA;
886 	extern int RefuseLA;
887 	extern bool trusteduser();
888 	extern char *username();
889 
890 	if (tTd(37, 1))
891 		printf("setoption %c=%s", opt, val);
892 
893 	/*
894 	**  See if this option is preset for us.
895 	*/
896 
897 	if (bitnset(opt, StickyOpt))
898 	{
899 		if (tTd(37, 1))
900 			printf(" (ignored)\n");
901 		return;
902 	}
903 
904 	/*
905 	**  Check to see if this option can be specified by this user.
906 	*/
907 
908 	if (!safe && getuid() == 0)
909 		safe = TRUE;
910 	if (!safe && strchr("deEiLmorsvC8", opt) == NULL)
911 	{
912 		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
913 		{
914 			if (tTd(37, 1))
915 				printf(" (unsafe)");
916 			if (getuid() != geteuid())
917 			{
918 				if (tTd(37, 1))
919 					printf("(Resetting uid)");
920 				(void) setgid(getgid());
921 				(void) setuid(getuid());
922 			}
923 		}
924 	}
925 	if (tTd(37, 1))
926 		printf("\n");
927 
928 	switch (opt)
929 	{
930 	  case '8':		/* allow eight-bit input */
931 		EightBit = atobool(val);
932 		break;
933 
934 	  case 'A':		/* set default alias file */
935 		if (val[0] == '\0')
936 			AliasFile = "aliases";
937 		else
938 			AliasFile = newstr(val);
939 		break;
940 
941 	  case 'a':		/* look N minutes for "@:@" in alias file */
942 		if (val[0] == '\0')
943 			SafeAlias = 5;
944 		else
945 			SafeAlias = atoi(val);
946 		break;
947 
948 	  case 'B':		/* substitution for blank character */
949 		SpaceSub = val[0];
950 		if (SpaceSub == '\0')
951 			SpaceSub = ' ';
952 		break;
953 
954 	  case 'c':		/* don't connect to "expensive" mailers */
955 		NoConnect = atobool(val);
956 		break;
957 
958 	  case 'C':		/* checkpoint every N addresses */
959 		CheckpointInterval = atoi(val);
960 		break;
961 
962 	  case 'd':		/* delivery mode */
963 		switch (*val)
964 		{
965 		  case '\0':
966 			SendMode = SM_DELIVER;
967 			break;
968 
969 		  case SM_QUEUE:	/* queue only */
970 #ifndef QUEUE
971 			syserr("need QUEUE to set -odqueue");
972 #endif /* QUEUE */
973 			/* fall through..... */
974 
975 		  case SM_DELIVER:	/* do everything */
976 		  case SM_FORK:		/* fork after verification */
977 			SendMode = *val;
978 			break;
979 
980 		  default:
981 			syserr("Unknown delivery mode %c", *val);
982 			exit(EX_USAGE);
983 		}
984 		break;
985 
986 	  case 'D':		/* rebuild alias database as needed */
987 		AutoRebuild = atobool(val);
988 		break;
989 
990 	  case 'E':		/* error message header/header file */
991 		if (*val != '\0')
992 			ErrMsgFile = newstr(val);
993 		break;
994 
995 	  case 'e':		/* set error processing mode */
996 		switch (*val)
997 		{
998 		  case EM_QUIET:	/* be silent about it */
999 		  case EM_MAIL:		/* mail back */
1000 		  case EM_BERKNET:	/* do berknet error processing */
1001 		  case EM_WRITE:	/* write back (or mail) */
1002 			HoldErrs = TRUE;
1003 			/* fall through... */
1004 
1005 		  case EM_PRINT:	/* print errors normally (default) */
1006 			ErrorMode = *val;
1007 			break;
1008 		}
1009 		break;
1010 
1011 	  case 'F':		/* file mode */
1012 		FileMode = atooct(val) & 0777;
1013 		break;
1014 
1015 	  case 'f':		/* save Unix-style From lines on front */
1016 		SaveFrom = atobool(val);
1017 		break;
1018 
1019 	  case 'G':		/* match recipients against GECOS field */
1020 		MatchGecos = atobool(val);
1021 		break;
1022 
1023 	  case 'g':		/* default gid */
1024 		DefGid = atoi(val);
1025 		break;
1026 
1027 	  case 'H':		/* help file */
1028 		if (val[0] == '\0')
1029 			HelpFile = "sendmail.hf";
1030 		else
1031 			HelpFile = newstr(val);
1032 		break;
1033 
1034 	  case 'h':		/* maximum hop count */
1035 		MaxHopCount = atoi(val);
1036 		break;
1037 
1038 	  case 'I':		/* use internet domain name server */
1039 #ifdef NAMED_BIND
1040 		UseNameServer = TRUE;
1041 		for (p = val; *p != 0; )
1042 		{
1043 			bool clearmode;
1044 			char *q;
1045 			struct resolverflags *rfp;
1046 
1047 			while (*p == ' ')
1048 				p++;
1049 			if (*p == '\0')
1050 				break;
1051 			clearmode = FALSE;
1052 			if (*p == '-')
1053 				clearmode = TRUE;
1054 			else if (*p != '+')
1055 				p--;
1056 			p++;
1057 			q = p;
1058 			while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1059 				p++;
1060 			if (*p != '\0')
1061 				*p++ = '\0';
1062 			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1063 			{
1064 				if (strcasecmp(q, rfp->rf_name) == 0)
1065 					break;
1066 			}
1067 			if (clearmode)
1068 				_res.options &= ~rfp->rf_bits;
1069 			else
1070 				_res.options |= rfp->rf_bits;
1071 		}
1072 		if (tTd(8, 2))
1073 			printf("_res.options = %x\n", _res.options);
1074 #else
1075 		usrerr("name server (I option) specified but BIND not compiled in");
1076 #endif
1077 		break;
1078 
1079 	  case 'i':		/* ignore dot lines in message */
1080 		IgnrDot = atobool(val);
1081 		break;
1082 
1083 	  case 'J':		/* .forward search path */
1084 		ForwardPath = newstr(val);
1085 		break;
1086 
1087 	  case 'k':		/* connection cache size */
1088 		MaxMciCache = atoi(val);
1089 		if (MaxMciCache < 0)
1090 			MaxMciCache = 0;
1091 		break;
1092 
1093 	  case 'K':		/* connection cache timeout */
1094 		MciCacheTimeout = convtime(val);
1095 		break;
1096 
1097 	  case 'L':		/* log level */
1098 		LogLevel = atoi(val);
1099 		break;
1100 
1101 	  case 'M':		/* define macro */
1102 		define(val[0], newstr(&val[1]), CurEnv);
1103 		sticky = FALSE;
1104 		break;
1105 
1106 	  case 'm':		/* send to me too */
1107 		MeToo = atobool(val);
1108 		break;
1109 
1110 	  case 'n':		/* validate RHS in newaliases */
1111 		CheckAliases = atobool(val);
1112 		break;
1113 
1114 	  case 'o':		/* assume old style headers */
1115 		if (atobool(val))
1116 			CurEnv->e_flags |= EF_OLDSTYLE;
1117 		else
1118 			CurEnv->e_flags &= ~EF_OLDSTYLE;
1119 		break;
1120 
1121 	  case 'P':		/* postmaster copy address for returned mail */
1122 		PostMasterCopy = newstr(val);
1123 		break;
1124 
1125 	  case 'q':		/* slope of queue only function */
1126 		QueueFactor = atoi(val);
1127 		break;
1128 
1129 	  case 'Q':		/* queue directory */
1130 		if (val[0] == '\0')
1131 			QueueDir = "mqueue";
1132 		else
1133 			QueueDir = newstr(val);
1134 		break;
1135 
1136 	  case 'r':		/* read timeout */
1137 		ReadTimeout = convtime(val);
1138 		break;
1139 
1140 	  case 'S':		/* status file */
1141 		if (val[0] == '\0')
1142 			StatFile = "sendmail.st";
1143 		else
1144 			StatFile = newstr(val);
1145 		break;
1146 
1147 	  case 's':		/* be super safe, even if expensive */
1148 		SuperSafe = atobool(val);
1149 		break;
1150 
1151 	  case 'T':		/* queue timeout */
1152 		TimeOut = convtime(val);
1153 		break;
1154 
1155 	  case 't':		/* time zone name */
1156 		TimeZoneSpec = newstr(val);
1157 		break;
1158 
1159 	  case 'U':		/* location of user database */
1160 		UdbSpec = newstr(val);
1161 		break;
1162 
1163 	  case 'u':		/* set default uid */
1164 		DefUid = atoi(val);
1165 		setdefuser();
1166 		break;
1167 
1168 	  case 'v':		/* run in verbose mode */
1169 		Verbose = atobool(val);
1170 		break;
1171 
1172 	  case 'x':		/* load avg at which to auto-queue msgs */
1173 		QueueLA = atoi(val);
1174 		break;
1175 
1176 	  case 'X':		/* load avg at which to auto-reject connections */
1177 		RefuseLA = atoi(val);
1178 		break;
1179 
1180 	  case 'y':		/* work recipient factor */
1181 		WkRecipFact = atoi(val);
1182 		break;
1183 
1184 	  case 'Y':		/* fork jobs during queue runs */
1185 		ForkQueueRuns = atobool(val);
1186 		break;
1187 
1188 	  case 'z':		/* work message class factor */
1189 		WkClassFact = atoi(val);
1190 		break;
1191 
1192 	  case 'Z':		/* work time factor */
1193 		WkTimeFact = atoi(val);
1194 		break;
1195 
1196 	  default:
1197 		break;
1198 	}
1199 	if (sticky)
1200 		setbitn(opt, StickyOpt);
1201 	return;
1202 }
1203 /*
1204 **  SETCLASS -- set a word into a class
1205 **
1206 **	Parameters:
1207 **		class -- the class to put the word in.
1208 **		word -- the word to enter
1209 **
1210 **	Returns:
1211 **		none.
1212 **
1213 **	Side Effects:
1214 **		puts the word into the symbol table.
1215 */
1216 
1217 setclass(class, word)
1218 	int class;
1219 	char *word;
1220 {
1221 	register STAB *s;
1222 
1223 	if (tTd(37, 8))
1224 		printf("%s added to class %c\n", word, class);
1225 	s = stab(word, ST_CLASS, ST_ENTER);
1226 	setbitn(class, s->s_class);
1227 }
1228 /*
1229 **  MAKEMAPENTRY -- create a map entry
1230 **
1231 **	Parameters:
1232 **		line -- the config file line
1233 **
1234 **	Returns:
1235 **		TRUE if it successfully entered the map entry.
1236 **		FALSE otherwise (usually syntax error).
1237 **
1238 **	Side Effects:
1239 **		Enters the map into the dictionary.
1240 */
1241 
1242 void
1243 makemapentry(line)
1244 	char *line;
1245 {
1246 	register char *p;
1247 	char *mapname;
1248 	char *classname;
1249 	register STAB *map;
1250 	STAB *class;
1251 
1252 	for (p = line; isascii(*p) && isspace(*p); p++)
1253 		continue;
1254 	if (!(isascii(*p) && isalnum(*p)))
1255 	{
1256 		syserr("readcf: config K line: no map name");
1257 		return;
1258 	}
1259 
1260 	mapname = p;
1261 	while (isascii(*++p) && isalnum(*p))
1262 		continue;
1263 	if (*p != '\0')
1264 		*p++ = '\0';
1265 	while (isascii(*p) && isspace(*p))
1266 		p++;
1267 	if (!(isascii(*p) && isalnum(*p)))
1268 	{
1269 		syserr("readcf: config K line, map %s: no map class", mapname);
1270 		return;
1271 	}
1272 	classname = p;
1273 	while (isascii(*++p) && isalnum(*p))
1274 		continue;
1275 	if (*p != '\0')
1276 		*p++ = '\0';
1277 	while (isascii(*p) && isspace(*p))
1278 		p++;
1279 
1280 	/* look up the class */
1281 	class = stab(classname, ST_MAPCLASS, ST_FIND);
1282 	if (class == NULL)
1283 	{
1284 		syserr("readcf: map %s: class %s not available", mapname, classname);
1285 		return;
1286 	}
1287 
1288 	/* enter the map */
1289 	map = stab(mapname, ST_MAP, ST_ENTER);
1290 	map->s_map.map_class = &class->s_mapclass;
1291 
1292 	if ((*class->s_mapclass.map_init)(&map->s_map, mapname, p))
1293 		map->s_map.map_flags |= MF_VALID;
1294 }
1295