xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 16893)
1 # include "sendmail.h"
2 
3 SCCSID(@(#)readcf.c	4.7		08/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 	bcopy((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 
644 	if (getruid() == 0)
645 		safe = TRUE;
646 
647 	switch (opt)
648 	{
649 	  case 'A':		/* set default alias file */
650 		if (val[0] == '\0')
651 			AliasFile = "aliases";
652 		else
653 			AliasFile = newstr(val);
654 		break;
655 
656 	  case 'B':		/* substitution for blank character */
657 		SpaceSub = val[0];
658 		if (SpaceSub == '\0')
659 			SpaceSub = ' ';
660 		break;
661 
662 	  case 'a':		/* look for "@:@" in alias file */
663 		SafeAlias = atobool(val);
664 		break;
665 
666 	  case 'c':		/* don't connect to "expensive" mailers */
667 		NoConnect = atobool(val);
668 		break;
669 
670 	  case 'd':		/* delivery mode */
671 		switch (*val)
672 		{
673 		  case '\0':
674 			SendMode = SM_DELIVER;
675 			break;
676 
677 		  case SM_QUEUE:	/* queue only */
678 #ifndef QUEUE
679 			syserr("need QUEUE to set -odqueue");
680 #endif QUEUE
681 			/* fall through..... */
682 
683 		  case SM_DELIVER:	/* do everything */
684 		  case SM_FORK:		/* fork after verification */
685 			SendMode = *val;
686 			break;
687 
688 		  default:
689 			syserr("Unknown delivery mode %c", *val);
690 			exit(EX_USAGE);
691 		}
692 		break;
693 
694 	  case 'D':		/* rebuild alias database as needed */
695 		AutoRebuild = atobool(val);
696 		break;
697 
698 	  case 'e':		/* set error processing mode */
699 		switch (*val)
700 		{
701 		  case EM_QUIET:	/* be silent about it */
702 		  case EM_MAIL:		/* mail back */
703 		  case EM_BERKNET:	/* do berknet error processing */
704 		  case EM_WRITE:	/* write back (or mail) */
705 			HoldErrs = TRUE;
706 			/* fall through... */
707 
708 		  case EM_PRINT:	/* print errors normally (default) */
709 			ErrorMode = *val;
710 			break;
711 		}
712 		break;
713 
714 	  case 'F':		/* file mode */
715 		FileMode = atooct(val);
716 		break;
717 
718 	  case 'f':		/* save Unix-style From lines on front */
719 		SaveFrom = atobool(val);
720 		break;
721 
722 	  case 'g':		/* default gid */
723 		if (safe)
724 			DefGid = atoi(val);
725 		break;
726 
727 	  case 'H':		/* help file */
728 		if (val[0] == '\0')
729 			HelpFile = "sendmail.hf";
730 		else
731 			HelpFile = newstr(val);
732 		break;
733 
734 	  case 'i':		/* ignore dot lines in message */
735 		IgnrDot = atobool(val);
736 		break;
737 
738 	  case 'L':		/* log level */
739 		LogLevel = atoi(val);
740 		break;
741 
742 	  case 'M':		/* define macro */
743 		define(val[0], newstr(&val[1]), CurEnv);
744 		sticky = FALSE;
745 		break;
746 
747 	  case 'm':		/* send to me too */
748 		MeToo = atobool(val);
749 		break;
750 
751 # ifdef DAEMON
752 	  case 'N':		/* home (local?) network name */
753 		NetName = newstr(val);
754 		break;
755 # endif DAEMON
756 
757 	  case 'o':		/* assume old style headers */
758 		if (atobool(val))
759 			CurEnv->e_flags |= EF_OLDSTYLE;
760 		else
761 			CurEnv->e_flags &= ~EF_OLDSTYLE;
762 		break;
763 
764 	  case 'Q':		/* queue directory */
765 		if (val[0] == '\0')
766 			QueueDir = "mqueue";
767 		else
768 			QueueDir = newstr(val);
769 		break;
770 
771 	  case 'r':		/* read timeout */
772 		ReadTimeout = convtime(val);
773 		break;
774 
775 	  case 'S':		/* status file */
776 		if (val[0] == '\0')
777 			StatFile = "sendmail.st";
778 		else
779 			StatFile = newstr(val);
780 		break;
781 
782 	  case 's':		/* be super safe, even if expensive */
783 		SuperSafe = atobool(val);
784 		break;
785 
786 	  case 'T':		/* queue timeout */
787 		TimeOut = convtime(val);
788 		break;
789 
790 	  case 't':		/* time zone name */
791 # ifdef V6
792 		StdTimezone = newstr(val);
793 		DstTimezone = index(StdTimeZone, ',');
794 		if (DstTimezone == NULL)
795 			syserr("bad time zone spec");
796 		else
797 			*DstTimezone++ = '\0';
798 # endif V6
799 		break;
800 
801 	  case 'u':		/* set default uid */
802 		if (safe)
803 			DefUid = atoi(val);
804 		break;
805 
806 	  case 'v':		/* run in verbose mode */
807 		Verbose = atobool(val);
808 		break;
809 
810 # ifdef DEBUG
811 	  case 'W':		/* set the wizards password */
812 		if (safe)
813 			WizWord = newstr(val);
814 		break;
815 # endif DEBUG
816 
817 	  case 'x':		/* load avg at which to auto-queue msgs */
818 		QueueLA = atoi(val);
819 		break;
820 
821 	  case 'X':		/* load avg at which to auto-reject connections */
822 		RefuseLA = atoi(val);
823 		break;
824 
825 	  default:
826 		break;
827 	}
828 	if (sticky)
829 		setbitn(opt, StickyOpt);
830 	return;
831 }
832 /*
833 **  SETCLASS -- set a word into a class
834 **
835 **	Parameters:
836 **		class -- the class to put the word in.
837 **		word -- the word to enter
838 **
839 **	Returns:
840 **		none.
841 **
842 **	Side Effects:
843 **		puts the word into the symbol table.
844 */
845 
846 setclass(class, word)
847 	int class;
848 	char *word;
849 {
850 	register STAB *s;
851 
852 	s = stab(word, ST_CLASS, ST_ENTER);
853 	setbitn(class, s->s_class);
854 }
855