xref: /original-bsd/usr.sbin/sendmail/src/readcf.c (revision f0fd5f8a)
1 # include "sendmail.h"
2 
3 SCCSID(@(#)readcf.c	3.48		11/28/82);
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 	'f',	M_FOPT,
487 	'r',	M_ROPT,
488 	'P',	M_RPATH,
489 	'S',	M_RESTR,
490 	'n',	M_NHDR,
491 	'l',	M_LOCAL,
492 	's',	M_STRIPQ,
493 	'm',	M_MUSER,
494 	'F',	M_NEEDFROM,
495 	'D',	M_NEEDDATE,
496 	'M',	M_MSGID,
497 	'u',	M_USR_UPPER,
498 	'h',	M_HST_UPPER,
499 	'x',	M_FULLNAME,
500 	'A',	M_ARPAFMT,
501 	'U',	M_UGLYUUCP,
502 	'e',	M_EXPENSIVE,
503 	'X',	M_FULLSMTP,
504 	'C',	M_CANONICAL,
505 	'I',	M_INTERNAL,
506 	'\0',	0
507 };
508 
509 u_long
510 mfencode(p)
511 	register char *p;
512 {
513 	register struct optlist *o;
514 	register u_long opts = 0;
515 
516 	while (*p != '\0')
517 	{
518 		for (o = OptList; o->opt_name != '\0' && o->opt_name != *p; o++)
519 			continue;
520 		if (o->opt_name == '\0')
521 			syserr("bad mailer option %c", *p);
522 		opts |= o->opt_value;
523 		p++;
524 	}
525 	return (opts);
526 }
527 /*
528 **  MFDECODE -- decode mailer flags into external form.
529 **
530 **	Parameters:
531 **		flags -- value of flags to decode.
532 **		f -- file to write them onto.
533 **
534 **	Returns:
535 **		none.
536 **
537 **	Side Effects:
538 **		none.
539 */
540 
541 mfdecode(flags, f)
542 	u_long flags;
543 	FILE *f;
544 {
545 	register struct optlist *o;
546 
547 	putc('?', f);
548 	for (o = OptList; o->opt_name != '\0'; o++)
549 	{
550 		if ((o->opt_value & flags) == o->opt_value)
551 		{
552 			flags &= ~o->opt_value;
553 			putc(o->opt_name, f);
554 		}
555 	}
556 	putc('?', f);
557 }
558 /*
559 **  SETOPTION -- set global processing option
560 **
561 **	Parameters:
562 **		opt -- option name.
563 **		val -- option value (as a text string).
564 **		safe -- if set, this came from a system configuration file.
565 **		sticky -- if set, don't let other setoptions override
566 **			this value.
567 **
568 **	Returns:
569 **		none.
570 **
571 **	Side Effects:
572 **		Sets options as implied by the arguments.
573 */
574 
575 static int	StickyOpt[128 / sizeof (int)];	/* set if option is stuck */
576 extern char	*WizWord;			/* the stored wizard password */
577 
578 setoption(opt, val, safe, sticky)
579 	char opt;
580 	char *val;
581 	bool safe;
582 	bool sticky;
583 {
584 	int smask;
585 	int sindex;
586 	extern bool atobool();
587 
588 # ifdef DEBUG
589 	if (tTd(37, 1))
590 		printf("setoption %c=%s", opt, val);
591 # endif DEBUG
592 
593 	/*
594 	**  See if this option is preset for us.
595 	*/
596 
597 	sindex = opt;
598 	smask = 1 << (sindex % sizeof (int));
599 	sindex /= sizeof (int);
600 	if (bitset(smask, StickyOpt[sindex]))
601 	{
602 # ifdef DEBUG
603 		if (tTd(37, 1))
604 			printf(" (ignored)\n");
605 # endif DEBUG
606 		return;
607 	}
608 #ifdef DEBUG
609 	else if (tTd(37, 1))
610 		printf("\n");
611 #endif DEBUG
612 	if (sticky)
613 		StickyOpt[sindex] |= smask;
614 
615 	if (getruid() == 0)
616 		safe = TRUE;
617 
618 	switch (opt)
619 	{
620 	  case 'A':		/* set default alias file */
621 		if (val[0] == '\0')
622 			AliasFile = "aliases";
623 		else
624 			AliasFile = newstr(val);
625 		break;
626 
627 	  case 'a':		/* look for "@:@" in alias file */
628 		SafeAlias = atobool(val);
629 		break;
630 
631 	  case 'c':		/* don't connect to "expensive" mailers */
632 		NoConnect = atobool(val);
633 		break;
634 
635 	  case 'd':		/* delivery mode */
636 		switch (*val)
637 		{
638 		  case '\0':
639 			SendMode = SM_DELIVER;
640 			break;
641 
642 		  case SM_DELIVER:	/* do everything */
643 		  case SM_FORK:		/* fork after verification */
644 		  case SM_QUEUE:	/* queue only */
645 			SendMode = *val;
646 			break;
647 
648 		  default:
649 			syserr("Unknown delivery mode %c", *val);
650 			exit(EX_USAGE);
651 		}
652 		break;
653 
654 	  case 'D':		/* rebuild alias database as needed */
655 		AutoRebuild = atobool(val);
656 		break;
657 
658 	  case 'e':		/* set error processing mode */
659 		switch (*val)
660 		{
661 		  case EM_QUIET:	/* be silent about it */
662 			(void) freopen("/dev/null", "w", stdout);
663 			/* fall through... */
664 
665 		  case EM_MAIL:		/* mail back */
666 		  case EM_BERKNET:	/* do berknet error processing */
667 		  case EM_WRITE:	/* write back (or mail) */
668 			HoldErrs = TRUE;
669 			/* fall through... */
670 
671 		  case EM_PRINT:	/* print errors normally (default) */
672 			ErrorMode = *val;
673 			break;
674 		}
675 		break;
676 
677 	  case 'F':		/* file mode */
678 		FileMode = atooct(val);
679 		break;
680 
681 	  case 'f':		/* save Unix-style From lines on front */
682 		SaveFrom = atobool(val);
683 		break;
684 
685 	  case 'g':		/* default gid */
686 		if (safe)
687 			DefGid = atoi(val);
688 		break;
689 
690 	  case 'H':		/* help file */
691 		if (val[0] == '\0')
692 			HelpFile = "sendmail.hf";
693 		else
694 			HelpFile = newstr(val);
695 		break;
696 
697 	  case 'i':		/* ignore dot lines in message */
698 		IgnrDot = atobool(val);
699 		break;
700 
701 	  case 'L':		/* log level */
702 		LogLevel = atoi(val);
703 		break;
704 
705 	  case 'M':		/* define macro */
706 		define(val[0], newstr(&val[1]), CurEnv);
707 		break;
708 
709 	  case 'm':		/* send to me too */
710 		MeToo = atobool(val);
711 		break;
712 
713 	  case 'o':		/* assume old style headers */
714 		if (atobool(val))
715 			CurEnv->e_flags |= EF_OLDSTYLE;
716 		else
717 			CurEnv->e_flags &= ~EF_OLDSTYLE;
718 		break;
719 
720 	  case 'Q':		/* queue directory */
721 		if (val[0] == '\0')
722 			QueueDir = "mqueue";
723 		else
724 			QueueDir = newstr(val);
725 		break;
726 
727 	  case 'r':		/* read timeout */
728 		ReadTimeout = convtime(val);
729 		break;
730 
731 	  case 'S':		/* status file */
732 		if (val[0] == '\0')
733 			StatFile = "sendmail.st";
734 		else
735 			StatFile = newstr(val);
736 		break;
737 
738 	  case 's':		/* be super safe, even if expensive */
739 		SuperSafe = atobool(val);
740 		break;
741 
742 	  case 'T':		/* queue timeout */
743 		TimeOut = convtime(val);
744 		break;
745 
746 	  case 't':		/* time zone name */
747 # ifdef V6
748 		StdTimezone = newstr(val);
749 		DstTimezone = index(StdTimeZone, ',');
750 		if (DstTimezone == NULL)
751 			syserr("bad time zone spec");
752 		else
753 			*DstTimezone++ = '\0';
754 # endif V6
755 		break;
756 
757 	  case 'u':		/* set default uid */
758 		if (safe)
759 			DefUid = atoi(val);
760 		break;
761 
762 	  case 'v':		/* run in verbose mode */
763 		Verbose = atobool(val);
764 		break;
765 
766 # ifdef DEBUG
767 	  case 'W':		/* set the wizards password */
768 		if (safe)
769 			WizWord = newstr(val);
770 		break;
771 # endif DEBUG
772 
773 	  default:
774 		break;
775 	}
776 	return;
777 }
778