xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 16157)
1 # include "sendmail.h"
2 
3 SCCSID(@(#)readcf.c	4.4		03/11/84);
4 
5 /*
6 **  READCF -- read control file.
7 **
8 **	This routine reads the control file and builds the internal
9 **	form.
10 **
11 **	The file is formatted as a sequence of lines, each taken
12 **	atomically.  The first character of each line describes how
13 **	the line is to be interpreted.  The lines are:
14 **		Dxval		Define macro x to have value val.
15 **		Cxword		Put word into class x.
16 **		Fxfile [fmt]	Read file for lines to put into
17 **				class x.  Use scanf string 'fmt'
18 **				or "%s" if not present.  Fmt should
19 **				only produce one string-valued result.
20 **		Hname: value	Define header with field-name 'name'
21 **				and value as specified; this will be
22 **				macro expanded immediately before
23 **				use.
24 **		Sn		Use rewriting set n.
25 **		Rlhs rhs	Rewrite addresses that match lhs to
26 **				be rhs.
27 **		Mn p f s r a	Define mailer.  n - internal name,
28 **				p - pathname, f - flags, s - rewriting
29 **				ruleset for sender, s - rewriting ruleset
30 **				for recipients, a - argument vector.
31 **		Oxvalue		Set option x to value.
32 **		Pname=value	Set precedence name to value.
33 **
34 **	Parameters:
35 **		cfname -- control file name.
36 **		safe -- set if this is a system configuration file.
37 **			Non-system configuration files can not do
38 **			certain things (e.g., leave the SUID bit on
39 **			when executing mailers).
40 **
41 **	Returns:
42 **		none.
43 **
44 **	Side Effects:
45 **		Builds several internal tables.
46 */
47 
48 readcf(cfname, safe)
49 	char *cfname;
50 	bool safe;
51 {
52 	FILE *cf;
53 	int ruleset = 0;
54 	char *q;
55 	char **pv;
56 	struct rewrite *rwp = NULL;
57 	char buf[MAXLINE];
58 	register char *p;
59 	extern char **prescan();
60 	extern char **copyplist();
61 	char exbuf[MAXLINE];
62 	extern char *fgetfolded();
63 	extern char *munchstring();
64 
65 	cf = fopen(cfname, "r");
66 	if (cf == NULL)
67 	{
68 		syserr("cannot open %s", cfname);
69 		exit(EX_OSFILE);
70 	}
71 
72 	FileName = cfname;
73 	LineNumber = 0;
74 	while (fgetfolded(buf, sizeof buf, cf) != NULL)
75 	{
76 		/* map $ into \001 (ASCII SOH) for macro expansion */
77 		for (p = buf; *p != '\0'; p++)
78 		{
79 			if (*p != '$')
80 				continue;
81 
82 			if (p[1] == '$')
83 			{
84 				/* actual dollar sign.... */
85 				strcpy(p, p + 1);
86 				continue;
87 			}
88 
89 			/* convert to macro expansion character */
90 			*p = '\001';
91 		}
92 
93 		/* interpret this line */
94 		switch (buf[0])
95 		{
96 		  case '\0':
97 		  case '#':		/* comment */
98 			break;
99 
100 		  case 'R':		/* rewriting rule */
101 			for (p = &buf[1]; *p != '\0' && *p != '\t'; p++)
102 				continue;
103 
104 			if (*p == '\0')
105 			{
106 				syserr("invalid rewrite line \"%s\"", buf);
107 				break;
108 			}
109 
110 			/* allocate space for the rule header */
111 			if (rwp == NULL)
112 			{
113 				RewriteRules[ruleset] = rwp =
114 					(struct rewrite *) xalloc(sizeof *rwp);
115 			}
116 			else
117 			{
118 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
119 				rwp = rwp->r_next;
120 			}
121 			rwp->r_next = NULL;
122 
123 			/* expand and save the LHS */
124 			*p = '\0';
125 			expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
126 			rwp->r_lhs = prescan(exbuf, '\t');
127 			if (rwp->r_lhs != NULL)
128 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
129 
130 			/* expand and save the RHS */
131 			while (*++p == '\t')
132 				continue;
133 			q = p;
134 			while (*p != '\0' && *p != '\t')
135 				p++;
136 			*p = '\0';
137 			expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
138 			rwp->r_rhs = prescan(exbuf, '\t');
139 			if (rwp->r_rhs != NULL)
140 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
141 			break;
142 
143 		  case 'S':		/* select rewriting set */
144 			ruleset = atoi(&buf[1]);
145 			if (ruleset >= MAXRWSETS || ruleset < 0)
146 			{
147 				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
148 				ruleset = 0;
149 			}
150 			rwp = NULL;
151 			break;
152 
153 		  case 'D':		/* macro definition */
154 			define(buf[1], newstr(munchstring(&buf[2])), CurEnv);
155 			break;
156 
157 		  case 'H':		/* required header line */
158 			(void) chompheader(&buf[1], TRUE);
159 			break;
160 
161 		  case 'C':		/* word class */
162 		  case 'F':		/* word class from file */
163 			/* read list of words from argument or file */
164 			if (buf[0] == 'F')
165 			{
166 				/* read from file */
167 				for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
168 					continue;
169 				if (*p == '\0')
170 					p = "%s";
171 				else
172 				{
173 					*p = '\0';
174 					while (isspace(*++p))
175 						continue;
176 				}
177 				fileclass(buf[1], &buf[2], p);
178 				break;
179 			}
180 
181 			/* scan the list of words and set class for all */
182 			for (p = &buf[2]; *p != '\0'; )
183 			{
184 				register char *wd;
185 				char delim;
186 
187 				while (*p != '\0' && isspace(*p))
188 					p++;
189 				wd = p;
190 				while (*p != '\0' && !isspace(*p))
191 					p++;
192 				delim = *p;
193 				*p = '\0';
194 				if (wd[0] != '\0')
195 					setclass(buf[1], wd);
196 				*p = delim;
197 			}
198 			break;
199 
200 		  case 'M':		/* define mailer */
201 			makemailer(&buf[1], safe);
202 			break;
203 
204 		  case 'O':		/* set option */
205 			setoption(buf[1], &buf[2], safe, FALSE);
206 			break;
207 
208 		  case 'P':		/* set precedence */
209 			if (NumPriorities >= MAXPRIORITIES)
210 			{
211 				toomany('P', MAXPRIORITIES);
212 				break;
213 			}
214 			for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
215 				continue;
216 			if (*p == '\0')
217 				goto badline;
218 			*p = '\0';
219 			Priorities[NumPriorities].pri_name = newstr(&buf[1]);
220 			Priorities[NumPriorities].pri_val = atoi(++p);
221 			NumPriorities++;
222 			break;
223 
224 		  case 'T':		/* trusted user(s) */
225 			p = &buf[1];
226 			while (*p != '\0')
227 			{
228 				while (isspace(*p))
229 					p++;
230 				q = p;
231 				while (*p != '\0' && !isspace(*p))
232 					p++;
233 				if (*p != '\0')
234 					*p++ = '\0';
235 				if (*q == '\0')
236 					continue;
237 				for (pv = TrustedUsers; *pv != NULL; pv++)
238 					continue;
239 				if (pv >= &TrustedUsers[MAXTRUST])
240 				{
241 					toomany('T', MAXTRUST);
242 					break;
243 				}
244 				*pv = newstr(q);
245 			}
246 			break;
247 
248 		  default:
249 		  badline:
250 			syserr("unknown control line \"%s\"", buf);
251 		}
252 	}
253 	FileName = NULL;
254 }
255 /*
256 **  TOOMANY -- signal too many of some option
257 **
258 **	Parameters:
259 **		id -- the id of the error line
260 **		maxcnt -- the maximum possible values
261 **
262 **	Returns:
263 **		none.
264 **
265 **	Side Effects:
266 **		gives a syserr.
267 */
268 
269 toomany(id, maxcnt)
270 	char id;
271 	int maxcnt;
272 {
273 	syserr("too many %c lines, %d max", id, maxcnt);
274 }
275 /*
276 **  FILECLASS -- read members of a class from a file
277 **
278 **	Parameters:
279 **		class -- class to define.
280 **		filename -- name of file to read.
281 **		fmt -- scanf string to use for match.
282 **
283 **	Returns:
284 **		none
285 **
286 **	Side Effects:
287 **
288 **		puts all lines in filename that match a scanf into
289 **			the named class.
290 */
291 
292 fileclass(class, filename, fmt)
293 	int class;
294 	char *filename;
295 	char *fmt;
296 {
297 	register FILE *f;
298 	char buf[MAXLINE];
299 
300 	f = fopen(filename, "r");
301 	if (f == NULL)
302 	{
303 		syserr("cannot open %s", filename);
304 		return;
305 	}
306 
307 	while (fgets(buf, sizeof buf, f) != NULL)
308 	{
309 		register STAB *s;
310 		char wordbuf[MAXNAME+1];
311 
312 		if (sscanf(buf, fmt, wordbuf) != 1)
313 			continue;
314 		s = stab(wordbuf, ST_CLASS, ST_ENTER);
315 		setbitn(class, s->s_class);
316 	}
317 
318 	(void) fclose(f);
319 }
320 /*
321 **  MAKEMAILER -- define a new mailer.
322 **
323 **	Parameters:
324 **		line -- description of mailer.  This is in labeled
325 **			fields.  The fields are:
326 **			   P -- the path to the mailer
327 **			   F -- the flags associated with the mailer
328 **			   A -- the argv for this mailer
329 **			   S -- the sender rewriting set
330 **			   R -- the recipient rewriting set
331 **			   E -- the eol string
332 **			The first word is the canonical name of the mailer.
333 **		safe -- set if this is a safe configuration file.
334 **
335 **	Returns:
336 **		none.
337 **
338 **	Side Effects:
339 **		enters the mailer into the mailer table.
340 */
341 
342 makemailer(line, safe)
343 	char *line;
344 	bool safe;
345 {
346 	register char *p;
347 	register struct mailer *m;
348 	register STAB *s;
349 	int i;
350 	char fcode;
351 	extern int NextMailer;
352 	extern char **makeargv();
353 	extern char *munchstring();
354 	extern char *DelimChar;
355 	extern long atol();
356 
357 	/* allocate a mailer and set up defaults */
358 	m = (struct mailer *) xalloc(sizeof *m);
359 	bzero((char *) m, sizeof *m);
360 	m->m_mno = NextMailer;
361 	m->m_eol = "\n";
362 
363 	/* collect the mailer name */
364 	for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++)
365 		continue;
366 	if (*p != '\0')
367 		*p++ = '\0';
368 	m->m_name = newstr(line);
369 
370 	/* now scan through and assign info from the fields */
371 	while (*p != '\0')
372 	{
373 		while (*p != '\0' && (*p == ',' || isspace(*p)))
374 			p++;
375 
376 		/* p now points to field code */
377 		fcode = *p;
378 		while (*p != '\0' && *p != '=' && *p != ',')
379 			p++;
380 		if (*p++ != '=')
381 		{
382 			syserr("`=' expected");
383 			return;
384 		}
385 		while (isspace(*p))
386 			p++;
387 
388 		/* p now points to the field body */
389 		p = munchstring(p);
390 
391 		/* install the field into the mailer struct */
392 		switch (fcode)
393 		{
394 		  case 'P':		/* pathname */
395 			m->m_mailer = newstr(p);
396 			break;
397 
398 		  case 'F':		/* flags */
399 			for (; *p != '\0'; p++)
400 				setbitn(*p, m->m_flags);
401 			if (!safe)
402 				clrbitn(M_RESTR, m->m_flags);
403 			break;
404 
405 		  case 'S':		/* sender rewriting ruleset */
406 		  case 'R':		/* recipient rewriting ruleset */
407 			i = atoi(p);
408 			if (i < 0 || i >= MAXRWSETS)
409 			{
410 				syserr("invalid rewrite set, %d max", MAXRWSETS);
411 				return;
412 			}
413 			if (fcode == 'S')
414 				m->m_s_rwset = i;
415 			else
416 				m->m_r_rwset = i;
417 			break;
418 
419 		  case 'E':		/* end of line string */
420 			m->m_eol = newstr(p);
421 			break;
422 
423 		  case 'A':		/* argument vector */
424 			m->m_argv = makeargv(p);
425 			break;
426 
427 		  case 'M':		/* maximum message size */
428 			m->m_maxsize = atol(p);
429 			break;
430 		}
431 
432 		p = DelimChar;
433 	}
434 
435 	/* now store the mailer away */
436 	if (NextMailer >= MAXMAILERS)
437 	{
438 		syserr("too many mailers defined (%d max)", MAXMAILERS);
439 		return;
440 	}
441 	Mailer[NextMailer++] = m;
442 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
443 	s->s_mailer = m;
444 }
445 /*
446 **  MUNCHSTRING -- translate a string into internal form.
447 **
448 **	Parameters:
449 **		p -- the string to munch.
450 **
451 **	Returns:
452 **		the munched string.
453 **
454 **	Side Effects:
455 **		Sets "DelimChar" to point to the string that caused us
456 **		to stop.
457 */
458 
459 char *
460 munchstring(p)
461 	register char *p;
462 {
463 	register char *q;
464 	bool backslash = FALSE;
465 	bool quotemode = FALSE;
466 	static char buf[MAXLINE];
467 	extern char *DelimChar;
468 
469 	for (q = buf; *p != '\0'; p++)
470 	{
471 		if (backslash)
472 		{
473 			/* everything is roughly literal */
474 			backslash = FALSE;
475 			switch (*p)
476 			{
477 			  case 'r':		/* carriage return */
478 				*q++ = '\r';
479 				continue;
480 
481 			  case 'n':		/* newline */
482 				*q++ = '\n';
483 				continue;
484 
485 			  case 'f':		/* form feed */
486 				*q++ = '\f';
487 				continue;
488 
489 			  case 'b':		/* backspace */
490 				*q++ = '\b';
491 				continue;
492 			}
493 			*q++ = *p;
494 		}
495 		else
496 		{
497 			if (*p == '\\')
498 				backslash = TRUE;
499 			else if (*p == '"')
500 				quotemode = !quotemode;
501 			else if (quotemode || *p != ',')
502 				*q++ = *p;
503 			else
504 				break;
505 		}
506 	}
507 
508 	DelimChar = p;
509 	*q++ = '\0';
510 	return (buf);
511 }
512 /*
513 **  MAKEARGV -- break up a string into words
514 **
515 **	Parameters:
516 **		p -- the string to break up.
517 **
518 **	Returns:
519 **		a char **argv (dynamically allocated)
520 **
521 **	Side Effects:
522 **		munges p.
523 */
524 
525 char **
526 makeargv(p)
527 	register char *p;
528 {
529 	char *q;
530 	int i;
531 	char **avp;
532 	char *argv[MAXPV + 1];
533 
534 	/* take apart the words */
535 	i = 0;
536 	while (*p != '\0' && i < MAXPV)
537 	{
538 		q = p;
539 		while (*p != '\0' && !isspace(*p))
540 			p++;
541 		while (isspace(*p))
542 			*p++ = '\0';
543 		argv[i++] = newstr(q);
544 	}
545 	argv[i++] = NULL;
546 
547 	/* now make a copy of the argv */
548 	avp = (char **) xalloc(sizeof *avp * i);
549 	bmove((char *) argv, (char *) avp, sizeof *avp * i);
550 
551 	return (avp);
552 }
553 /*
554 **  PRINTRULES -- print rewrite rules (for debugging)
555 **
556 **	Parameters:
557 **		none.
558 **
559 **	Returns:
560 **		none.
561 **
562 **	Side Effects:
563 **		prints rewrite rules.
564 */
565 
566 # ifdef DEBUG
567 
568 printrules()
569 {
570 	register struct rewrite *rwp;
571 	register int ruleset;
572 
573 	for (ruleset = 0; ruleset < 10; ruleset++)
574 	{
575 		if (RewriteRules[ruleset] == NULL)
576 			continue;
577 		printf("\n----Rule Set %d:", ruleset);
578 
579 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
580 		{
581 			printf("\nLHS:");
582 			printav(rwp->r_lhs);
583 			printf("RHS:");
584 			printav(rwp->r_rhs);
585 		}
586 	}
587 }
588 
589 # endif DEBUG
590 /*
591 **  SETOPTION -- set global processing option
592 **
593 **	Parameters:
594 **		opt -- option name.
595 **		val -- option value (as a text string).
596 **		safe -- if set, this came from a system configuration file.
597 **		sticky -- if set, don't let other setoptions override
598 **			this value.
599 **
600 **	Returns:
601 **		none.
602 **
603 **	Side Effects:
604 **		Sets options as implied by the arguments.
605 */
606 
607 static BITMAP	StickyOpt;		/* set if option is stuck */
608 extern char	*WizWord;		/* the stored wizard password */
609 extern char	*NetName;		/* name of home (local) network */
610 
611 setoption(opt, val, safe, sticky)
612 	char opt;
613 	char *val;
614 	bool safe;
615 	bool sticky;
616 {
617 	extern bool atobool();
618 	extern time_t convtime();
619 	extern int QueueLA;
620 	extern int RefuseLA;
621 
622 # ifdef DEBUG
623 	if (tTd(37, 1))
624 		printf("setoption %c=%s", opt, val);
625 # endif DEBUG
626 
627 	/*
628 	**  See if this option is preset for us.
629 	*/
630 
631 	if (bitnset(opt, StickyOpt))
632 	{
633 # ifdef DEBUG
634 		if (tTd(37, 1))
635 			printf(" (ignored)\n");
636 # endif DEBUG
637 		return;
638 	}
639 #ifdef DEBUG
640 	else if (tTd(37, 1))
641 		printf("\n");
642 #endif DEBUG
643 	if (sticky)
644 		setbitn(opt, StickyOpt);
645 
646 	if (getruid() == 0)
647 		safe = TRUE;
648 
649 	switch (opt)
650 	{
651 	  case 'A':		/* set default alias file */
652 		if (val[0] == '\0')
653 			AliasFile = "aliases";
654 		else
655 			AliasFile = newstr(val);
656 		break;
657 
658 	  case 'a':		/* look for "@:@" in alias file */
659 		SafeAlias = atobool(val);
660 		break;
661 
662 	  case 'c':		/* don't connect to "expensive" mailers */
663 		NoConnect = atobool(val);
664 		break;
665 
666 	  case 'd':		/* delivery mode */
667 		switch (*val)
668 		{
669 		  case '\0':
670 			SendMode = SM_DELIVER;
671 			break;
672 
673 		  case SM_QUEUE:	/* queue only */
674 #ifndef QUEUE
675 			syserr("need QUEUE to set -odqueue");
676 #endif QUEUE
677 			/* fall through..... */
678 
679 		  case SM_DELIVER:	/* do everything */
680 		  case SM_FORK:		/* fork after verification */
681 			SendMode = *val;
682 			break;
683 
684 		  default:
685 			syserr("Unknown delivery mode %c", *val);
686 			exit(EX_USAGE);
687 		}
688 		break;
689 
690 	  case 'D':		/* rebuild alias database as needed */
691 		AutoRebuild = atobool(val);
692 		break;
693 
694 	  case 'e':		/* set error processing mode */
695 		switch (*val)
696 		{
697 		  case EM_QUIET:	/* be silent about it */
698 		  case EM_MAIL:		/* mail back */
699 		  case EM_BERKNET:	/* do berknet error processing */
700 		  case EM_WRITE:	/* write back (or mail) */
701 			HoldErrs = TRUE;
702 			/* fall through... */
703 
704 		  case EM_PRINT:	/* print errors normally (default) */
705 			ErrorMode = *val;
706 			break;
707 		}
708 		break;
709 
710 	  case 'F':		/* file mode */
711 		FileMode = atooct(val);
712 		break;
713 
714 	  case 'f':		/* save Unix-style From lines on front */
715 		SaveFrom = atobool(val);
716 		break;
717 
718 	  case 'g':		/* default gid */
719 		if (safe)
720 			DefGid = atoi(val);
721 		break;
722 
723 	  case 'H':		/* help file */
724 		if (val[0] == '\0')
725 			HelpFile = "sendmail.hf";
726 		else
727 			HelpFile = newstr(val);
728 		break;
729 
730 	  case 'i':		/* ignore dot lines in message */
731 		IgnrDot = atobool(val);
732 		break;
733 
734 	  case 'L':		/* log level */
735 		LogLevel = atoi(val);
736 		break;
737 
738 	  case 'M':		/* define macro */
739 		define(val[0], newstr(&val[1]), CurEnv);
740 		break;
741 
742 	  case 'm':		/* send to me too */
743 		MeToo = atobool(val);
744 		break;
745 
746 # ifdef DAEMON
747 	  case 'N':		/* home (local?) network name */
748 		NetName = newstr(val);
749 		break;
750 # endif DAEMON
751 
752 	  case 'o':		/* assume old style headers */
753 		if (atobool(val))
754 			CurEnv->e_flags |= EF_OLDSTYLE;
755 		else
756 			CurEnv->e_flags &= ~EF_OLDSTYLE;
757 		break;
758 
759 	  case 'Q':		/* queue directory */
760 		if (val[0] == '\0')
761 			QueueDir = "mqueue";
762 		else
763 			QueueDir = newstr(val);
764 		break;
765 
766 	  case 'r':		/* read timeout */
767 		ReadTimeout = convtime(val);
768 		break;
769 
770 	  case 'S':		/* status file */
771 		if (val[0] == '\0')
772 			StatFile = "sendmail.st";
773 		else
774 			StatFile = newstr(val);
775 		break;
776 
777 	  case 's':		/* be super safe, even if expensive */
778 		SuperSafe = atobool(val);
779 		break;
780 
781 	  case 'T':		/* queue timeout */
782 		TimeOut = convtime(val);
783 		break;
784 
785 	  case 't':		/* time zone name */
786 # ifdef V6
787 		StdTimezone = newstr(val);
788 		DstTimezone = index(StdTimeZone, ',');
789 		if (DstTimezone == NULL)
790 			syserr("bad time zone spec");
791 		else
792 			*DstTimezone++ = '\0';
793 # endif V6
794 		break;
795 
796 	  case 'u':		/* set default uid */
797 		if (safe)
798 			DefUid = atoi(val);
799 		break;
800 
801 	  case 'v':		/* run in verbose mode */
802 		Verbose = atobool(val);
803 		break;
804 
805 # ifdef DEBUG
806 	  case 'W':		/* set the wizards password */
807 		if (safe)
808 			WizWord = newstr(val);
809 		break;
810 # endif DEBUG
811 
812 	  case 'x':		/* load avg at which to auto-queue msgs */
813 		QueueLA = atoi(val);
814 		break;
815 
816 	  case 'X':		/* load avg at which to auto-reject connections */
817 		RefuseLA = atoi(val);
818 		break;
819 
820 	  default:
821 		break;
822 	}
823 	return;
824 }
825 /*
826 **  SETCLASS -- set a word into a class
827 **
828 **	Parameters:
829 **		class -- the class to put the word in.
830 **		word -- the word to enter
831 **
832 **	Returns:
833 **		none.
834 **
835 **	Side Effects:
836 **		puts the word into the symbol table.
837 */
838 
839 setclass(class, word)
840 	int class;
841 	char *word;
842 {
843 	register STAB *s;
844 
845 	s = stab(word, ST_CLASS, ST_ENTER);
846 	setbitn(class, s->s_class);
847 }
848