xref: /csrg-svn/usr.sbin/sendmail/src/readcf.c (revision 10174)
1 # include "sendmail.h"
2 
3 SCCSID(@(#)readcf.c	3.49		01/06/83);
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 class;
54 	int ruleset = 0;
55 	char *q;
56 	char **pv;
57 	struct rewrite *rwp = NULL;
58 	char buf[MAXLINE];
59 	register char *p;
60 	extern char **prescan();
61 	extern char **copyplist();
62 	char exbuf[MAXLINE];
63 	extern char *fgetfolded();
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 		switch (buf[0])
77 		{
78 		  case '\0':
79 		  case '#':		/* comment */
80 			break;
81 
82 		  case 'R':		/* rewriting rule */
83 			for (p = &buf[1]; *p != '\0' && *p != '\t'; p++)
84 				continue;
85 
86 			if (*p == '\0')
87 			{
88 				syserr("invalid rewrite line \"%s\"", buf);
89 				break;
90 			}
91 
92 			/* allocate space for the rule header */
93 			if (rwp == NULL)
94 			{
95 				RewriteRules[ruleset] = rwp =
96 					(struct rewrite *) xalloc(sizeof *rwp);
97 			}
98 			else
99 			{
100 				rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
101 				rwp = rwp->r_next;
102 			}
103 			rwp->r_next = NULL;
104 
105 			/* expand and save the LHS */
106 			*p = '\0';
107 			expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv);
108 			rwp->r_lhs = prescan(exbuf, '\t');
109 			if (rwp->r_lhs != NULL)
110 				rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
111 
112 			/* expand and save the RHS */
113 			while (*++p == '\t')
114 				continue;
115 			q = p;
116 			while (*p != '\0' && *p != '\t')
117 				p++;
118 			*p = '\0';
119 			expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv);
120 			rwp->r_rhs = prescan(exbuf, '\t');
121 			if (rwp->r_rhs != NULL)
122 				rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
123 			break;
124 
125 		  case 'S':		/* select rewriting set */
126 			ruleset = atoi(&buf[1]);
127 			if (ruleset >= MAXRWSETS || ruleset < 0)
128 			{
129 				syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
130 				ruleset = 0;
131 			}
132 			rwp = NULL;
133 			break;
134 
135 		  case 'D':		/* macro definition */
136 			define(buf[1], newstr(&buf[2]), CurEnv);
137 			break;
138 
139 		  case 'H':		/* required header line */
140 			(void) chompheader(&buf[1], TRUE);
141 			break;
142 
143 		  case 'C':		/* word class */
144 		  case 'F':		/* word class from file */
145 			class = buf[1];
146 			if (!isalpha(class))
147 			{
148 				syserr("illegal class name %c", class);
149 				break;
150 			}
151 			if (isupper(class))
152 				class -= 'A';
153 			else
154 				class -= 'a';
155 
156 			/* read list of words from argument or file */
157 			if (buf[0] == 'F')
158 			{
159 				/* read from file */
160 				for (p = &buf[2]; *p != '\0' && !isspace(*p); p++)
161 					continue;
162 				if (*p == '\0')
163 					p = "%s";
164 				else
165 				{
166 					*p = '\0';
167 					while (isspace(*++p))
168 						continue;
169 				}
170 				fileclass(class, &buf[2], p);
171 				break;
172 			}
173 
174 			/* scan the list of words and set class for all */
175 			for (p = &buf[2]; *p != '\0'; )
176 			{
177 				register char *wd;
178 				char delim;
179 				register STAB *s;
180 
181 				while (*p != '\0' && isspace(*p))
182 					p++;
183 				wd = p;
184 				while (*p != '\0' && !isspace(*p))
185 					p++;
186 				delim = *p;
187 				*p = '\0';
188 				if (wd[0] != '\0')
189 				{
190 					s = stab(wd, ST_CLASS, ST_ENTER);
191 					s->s_class |= 1L << class;
192 				}
193 				*p = delim;
194 			}
195 			break;
196 
197 		  case 'M':		/* define mailer */
198 			makemailer(&buf[1], safe);
199 			break;
200 
201 		  case 'O':		/* set option */
202 			setoption(buf[1], &buf[2], safe, FALSE);
203 			break;
204 
205 		  case 'P':		/* set precedence */
206 			if (NumPriorities >= MAXPRIORITIES)
207 			{
208 				toomany('P', MAXPRIORITIES);
209 				break;
210 			}
211 			for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
212 				continue;
213 			if (*p == '\0')
214 				goto badline;
215 			*p = '\0';
216 			Priorities[NumPriorities].pri_name = newstr(&buf[1]);
217 			Priorities[NumPriorities].pri_val = atoi(++p);
218 			NumPriorities++;
219 			break;
220 
221 		  case 'T':		/* trusted user(s) */
222 			p = &buf[1];
223 			while (*p != '\0')
224 			{
225 				while (isspace(*p))
226 					p++;
227 				q = p;
228 				while (*p != '\0' && !isspace(*p))
229 					p++;
230 				if (*p != '\0')
231 					*p++ = '\0';
232 				if (*q == '\0')
233 					continue;
234 				for (pv = TrustedUsers; *pv != NULL; pv++)
235 					continue;
236 				if (pv >= &TrustedUsers[MAXTRUST])
237 				{
238 					toomany('T', MAXTRUST);
239 					break;
240 				}
241 				*pv = newstr(q);
242 			}
243 			break;
244 
245 		  default:
246 		  badline:
247 			syserr("unknown control line \"%s\"", buf);
248 		}
249 	}
250 	FileName = NULL;
251 }
252 /*
253 **  TOOMANY -- signal too many of some option
254 **
255 **	Parameters:
256 **		id -- the id of the error line
257 **		maxcnt -- the maximum possible values
258 **
259 **	Returns:
260 **		none.
261 **
262 **	Side Effects:
263 **		gives a syserr.
264 */
265 
266 toomany(id, maxcnt)
267 	char id;
268 	int maxcnt;
269 {
270 	syserr("too many %c lines, %d max", id, maxcnt);
271 }
272 /*
273 **  FILECLASS -- read members of a class from a file
274 **
275 **	Parameters:
276 **		class -- class to define.
277 **		filename -- name of file to read.
278 **		fmt -- scanf string to use for match.
279 **
280 **	Returns:
281 **		none
282 **
283 **	Side Effects:
284 **
285 **		puts all lines in filename that match a scanf into
286 **			the named class.
287 */
288 
289 fileclass(class, filename, fmt)
290 	int class;
291 	char *filename;
292 	char *fmt;
293 {
294 	register FILE *f;
295 	char buf[MAXLINE];
296 
297 	f = fopen(filename, "r");
298 	if (f == NULL)
299 	{
300 		syserr("cannot open %s", filename);
301 		return;
302 	}
303 
304 	while (fgets(buf, sizeof buf, f) != NULL)
305 	{
306 		register STAB *s;
307 		char wordbuf[MAXNAME+1];
308 
309 		if (sscanf(buf, fmt, wordbuf) != 1)
310 			continue;
311 		s = stab(wordbuf, ST_CLASS, ST_ENTER);
312 		s->s_class |= 1L << class;
313 	}
314 
315 	(void) fclose(f);
316 }
317 /*
318 **  MAKEMAILER -- define a new mailer.
319 **
320 **	Parameters:
321 **		line -- description of mailer.  This is in tokens
322 **			separated by white space.  The fields are:
323 **			* the name of the mailer, as refered to
324 **			  in the rewriting rules.
325 **			* the pathname of the program to fork to
326 **			  execute it.
327 **			* the options needed by this program.
328 **			* the macro string needed to translate
329 **			  a local "from" name to one that can be
330 **			  returned to this machine.
331 **			* the argument vector (a series of parameters).
332 **		safe -- set if this is a safe configuration file.
333 **
334 **	Returns:
335 **		none.
336 **
337 **	Side Effects:
338 **		enters the mailer into the mailer table.
339 */
340 
341 # define SETWORD \
342 		{ \
343 			while (*p != '\0' && isspace(*p)) \
344 				p++; \
345 			q = p; \
346 			while (*p != '\0' && !isspace(*p)) \
347 				p++; \
348 			if (*p != '\0') \
349 				*p++ = '\0'; \
350 		}
351 
352 makemailer(line, safe)
353 	char *line;
354 	bool safe;
355 {
356 	register char *p;
357 	register char *q;
358 	register struct mailer *m;
359 	register STAB *s;
360 	int i;
361 	char *mname;
362 	char *mpath;
363 	u_long mopts;
364 	short mrset, msset;
365 	char *margv[MAXPV + 1];
366 	extern u_long mfencode();
367 	extern int NextMailer;
368 
369 	if (NextMailer >= MAXMAILERS)
370 	{
371 		syserr("too many mailers defined (%d max)", MAXMAILERS);
372 		return;
373 	}
374 
375 	/* collect initial information */
376 	p = line;
377 	SETWORD;
378 	mname = q;
379 	SETWORD;
380 	mpath = q;
381 	SETWORD;
382 	mopts = mfencode(q);
383 	if (!safe)
384 		mopts &= ~M_RESTR;
385 	SETWORD;
386 	msset = atoi(q);
387 	SETWORD;
388 	mrset = atoi(q);
389 
390 	if (*p == '\0')
391 	{
392 		syserr("invalid M line in configuration file");
393 		return;
394 	}
395 	if (msset >= MAXRWSETS || mrset >= MAXRWSETS)
396 	{
397 		syserr("readcf: line %d: invalid rewrite set, %d max",
398 			LineNumber, MAXRWSETS);
399 		return;
400 	}
401 
402 	/* allocate a mailer */
403 	m = (struct mailer *) xalloc(sizeof *m);
404 	m->m_name = newstr(mname);
405 	m->m_mailer = newstr(mpath);
406 	m->m_flags = mopts;
407 	m->m_r_rwset = mrset;
408 	m->m_s_rwset = msset;
409 	m->m_mno = NextMailer;
410 	Mailer[NextMailer++] = m;
411 
412 	/* collect the argument vector */
413 	for (i = 0; i < MAXPV - 1 && *p != '\0'; i++)
414 	{
415 		SETWORD;
416 		margv[i] = newstr(q);
417 	}
418 	margv[i++] = NULL;
419 
420 	/* save the argv */
421 	m->m_argv = (char **) xalloc(sizeof margv[0] * i);
422 	bmove((char *) margv, (char *) m->m_argv, sizeof margv[0] * i);
423 	s = stab(m->m_name, ST_MAILER, ST_ENTER);
424 	s->s_mailer = m;
425 }
426 /*
427 **  PRINTRULES -- print rewrite rules (for debugging)
428 **
429 **	Parameters:
430 **		none.
431 **
432 **	Returns:
433 **		none.
434 **
435 **	Side Effects:
436 **		prints rewrite rules.
437 */
438 
439 # ifdef DEBUG
440 
441 printrules()
442 {
443 	register struct rewrite *rwp;
444 	register int ruleset;
445 
446 	for (ruleset = 0; ruleset < 10; ruleset++)
447 	{
448 		if (RewriteRules[ruleset] == NULL)
449 			continue;
450 		printf("\n----Rule Set %d:", ruleset);
451 
452 		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
453 		{
454 			printf("\nLHS:");
455 			printav(rwp->r_lhs);
456 			printf("RHS:");
457 			printav(rwp->r_rhs);
458 		}
459 	}
460 }
461 
462 # endif DEBUG
463 /*
464 **  MFENCODE -- crack mailer options
465 **
466 **	These options modify the functioning of the mailer
467 **	from the configuration table.
468 **
469 **	Parameters:
470 **		p -- pointer to vector of options.
471 **
472 **	Returns:
473 **		option list in binary.
474 **
475 **	Side Effects:
476 **		none.
477 */
478 
479 struct optlist
480 {
481 	char	opt_name;	/* external name of option */
482 	u_long	opt_value;	/* internal name of option */
483 };
484 struct optlist	OptList[] =
485 {
486 	'A',	M_ARPAFMT,
487 	'C',	M_CANONICAL,
488 	'D',	M_NEEDDATE,
489 	'e',	M_EXPENSIVE,
490 	'F',	M_NEEDFROM,
491 	'f',	M_FOPT,
492 	'h',	M_HST_UPPER,
493 	'I',	M_INTERNAL,
494 	'L',	M_LIMITS,
495 	'l',	M_LOCAL,
496 	'M',	M_MSGID,
497 	'm',	M_MUSER,
498 	'n',	M_NHDR,
499 	'P',	M_RPATH,
500 	'p',	M_FROMPATH,
501 	'R',	M_CRLF,
502 	'r',	M_ROPT,
503 	'S',	M_RESTR,
504 	's',	M_STRIPQ,
505 	'U',	M_UGLYUUCP,
506 	'u',	M_USR_UPPER,
507 	'x',	M_FULLNAME,
508 	'X',	M_XDOT,
509 	'\0',	0
510 };
511 
512 u_long
513 mfencode(p)
514 	register char *p;
515 {
516 	register struct optlist *o;
517 	register u_long opts = 0;
518 
519 	while (*p != '\0')
520 	{
521 		for (o = OptList; o->opt_name != '\0' && o->opt_name != *p; o++)
522 			continue;
523 		if (o->opt_name == '\0')
524 			syserr("bad mailer option %c", *p);
525 		opts |= o->opt_value;
526 		p++;
527 	}
528 	return (opts);
529 }
530 /*
531 **  MFDECODE -- decode mailer flags into external form.
532 **
533 **	Parameters:
534 **		flags -- value of flags to decode.
535 **		f -- file to write them onto.
536 **
537 **	Returns:
538 **		none.
539 **
540 **	Side Effects:
541 **		none.
542 */
543 
544 mfdecode(flags, f)
545 	u_long flags;
546 	FILE *f;
547 {
548 	register struct optlist *o;
549 
550 	putc('?', f);
551 	for (o = OptList; o->opt_name != '\0'; o++)
552 	{
553 		if ((o->opt_value & flags) == o->opt_value)
554 		{
555 			flags &= ~o->opt_value;
556 			putc(o->opt_name, f);
557 		}
558 	}
559 	putc('?', f);
560 }
561 /*
562 **  SETOPTION -- set global processing option
563 **
564 **	Parameters:
565 **		opt -- option name.
566 **		val -- option value (as a text string).
567 **		safe -- if set, this came from a system configuration file.
568 **		sticky -- if set, don't let other setoptions override
569 **			this value.
570 **
571 **	Returns:
572 **		none.
573 **
574 **	Side Effects:
575 **		Sets options as implied by the arguments.
576 */
577 
578 static int	StickyOpt[128 / sizeof (int)];	/* set if option is stuck */
579 extern char	*WizWord;			/* the stored wizard password */
580 
581 setoption(opt, val, safe, sticky)
582 	char opt;
583 	char *val;
584 	bool safe;
585 	bool sticky;
586 {
587 	int smask;
588 	int sindex;
589 	extern bool atobool();
590 
591 # ifdef DEBUG
592 	if (tTd(37, 1))
593 		printf("setoption %c=%s", opt, val);
594 # endif DEBUG
595 
596 	/*
597 	**  See if this option is preset for us.
598 	*/
599 
600 	sindex = opt;
601 	smask = 1 << (sindex % sizeof (int));
602 	sindex /= sizeof (int);
603 	if (bitset(smask, StickyOpt[sindex]))
604 	{
605 # ifdef DEBUG
606 		if (tTd(37, 1))
607 			printf(" (ignored)\n");
608 # endif DEBUG
609 		return;
610 	}
611 #ifdef DEBUG
612 	else if (tTd(37, 1))
613 		printf("\n");
614 #endif DEBUG
615 	if (sticky)
616 		StickyOpt[sindex] |= smask;
617 
618 	if (getruid() == 0)
619 		safe = TRUE;
620 
621 	switch (opt)
622 	{
623 	  case 'A':		/* set default alias file */
624 		if (val[0] == '\0')
625 			AliasFile = "aliases";
626 		else
627 			AliasFile = newstr(val);
628 		break;
629 
630 	  case 'a':		/* look for "@:@" in alias file */
631 		SafeAlias = atobool(val);
632 		break;
633 
634 	  case 'c':		/* don't connect to "expensive" mailers */
635 		NoConnect = atobool(val);
636 		break;
637 
638 	  case 'd':		/* delivery mode */
639 		switch (*val)
640 		{
641 		  case '\0':
642 			SendMode = SM_DELIVER;
643 			break;
644 
645 		  case SM_DELIVER:	/* do everything */
646 		  case SM_FORK:		/* fork after verification */
647 		  case SM_QUEUE:	/* queue only */
648 			SendMode = *val;
649 			break;
650 
651 		  default:
652 			syserr("Unknown delivery mode %c", *val);
653 			exit(EX_USAGE);
654 		}
655 		break;
656 
657 	  case 'D':		/* rebuild alias database as needed */
658 		AutoRebuild = atobool(val);
659 		break;
660 
661 	  case 'e':		/* set error processing mode */
662 		switch (*val)
663 		{
664 		  case EM_QUIET:	/* be silent about it */
665 			(void) freopen("/dev/null", "w", stdout);
666 			/* fall through... */
667 
668 		  case EM_MAIL:		/* mail back */
669 		  case EM_BERKNET:	/* do berknet error processing */
670 		  case EM_WRITE:	/* write back (or mail) */
671 			HoldErrs = TRUE;
672 			/* fall through... */
673 
674 		  case EM_PRINT:	/* print errors normally (default) */
675 			ErrorMode = *val;
676 			break;
677 		}
678 		break;
679 
680 	  case 'F':		/* file mode */
681 		FileMode = atooct(val);
682 		break;
683 
684 	  case 'f':		/* save Unix-style From lines on front */
685 		SaveFrom = atobool(val);
686 		break;
687 
688 	  case 'g':		/* default gid */
689 		if (safe)
690 			DefGid = atoi(val);
691 		break;
692 
693 	  case 'H':		/* help file */
694 		if (val[0] == '\0')
695 			HelpFile = "sendmail.hf";
696 		else
697 			HelpFile = newstr(val);
698 		break;
699 
700 	  case 'i':		/* ignore dot lines in message */
701 		IgnrDot = atobool(val);
702 		break;
703 
704 	  case 'L':		/* log level */
705 		LogLevel = atoi(val);
706 		break;
707 
708 	  case 'M':		/* define macro */
709 		define(val[0], newstr(&val[1]), CurEnv);
710 		break;
711 
712 	  case 'm':		/* send to me too */
713 		MeToo = atobool(val);
714 		break;
715 
716 	  case 'o':		/* assume old style headers */
717 		if (atobool(val))
718 			CurEnv->e_flags |= EF_OLDSTYLE;
719 		else
720 			CurEnv->e_flags &= ~EF_OLDSTYLE;
721 		break;
722 
723 	  case 'Q':		/* queue directory */
724 		if (val[0] == '\0')
725 			QueueDir = "mqueue";
726 		else
727 			QueueDir = newstr(val);
728 		break;
729 
730 	  case 'r':		/* read timeout */
731 		ReadTimeout = convtime(val);
732 		break;
733 
734 	  case 'S':		/* status file */
735 		if (val[0] == '\0')
736 			StatFile = "sendmail.st";
737 		else
738 			StatFile = newstr(val);
739 		break;
740 
741 	  case 's':		/* be super safe, even if expensive */
742 		SuperSafe = atobool(val);
743 		break;
744 
745 	  case 'T':		/* queue timeout */
746 		TimeOut = convtime(val);
747 		break;
748 
749 	  case 't':		/* time zone name */
750 # ifdef V6
751 		StdTimezone = newstr(val);
752 		DstTimezone = index(StdTimeZone, ',');
753 		if (DstTimezone == NULL)
754 			syserr("bad time zone spec");
755 		else
756 			*DstTimezone++ = '\0';
757 # endif V6
758 		break;
759 
760 	  case 'u':		/* set default uid */
761 		if (safe)
762 			DefUid = atoi(val);
763 		break;
764 
765 	  case 'v':		/* run in verbose mode */
766 		Verbose = atobool(val);
767 		break;
768 
769 # ifdef DEBUG
770 	  case 'W':		/* set the wizards password */
771 		if (safe)
772 			WizWord = newstr(val);
773 		break;
774 # endif DEBUG
775 
776 	  default:
777 		break;
778 	}
779 	return;
780 }
781