1/*
2 * Copyright (c) 1998-2006, 2008-2010, 2013 Proofpoint, Inc. and its suppliers.
3 *	All rights reserved.
4 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
5 * Copyright (c) 1988, 1993
6 *	The Regents of the University of California.  All rights reserved.
7 *
8 * By using this file, you agree to the terms and conditions set
9 * forth in the LICENSE file which can be found at the top level of
10 * the sendmail distribution.
11 *
12 */
13
14#include <sendmail.h>
15#include <sm/sendmail.h>
16#if STARTTLS
17# include <tls.h>
18#endif
19#if DNSSEC_TEST
20# include <sm_resolve.h>
21#endif
22
23SM_RCSID("@(#)$Id: readcf.c,v 8.692 2013-11-22 20:51:56 ca Exp $")
24
25#if NETINET || NETINET6
26# include <arpa/inet.h>
27#endif
28
29
30#define SECONDS
31#define MINUTES	* 60
32#define HOUR	* 3600
33#define HOURS	HOUR
34
35static void	fileclass __P((int, char *, char *, bool, bool, bool));
36static char	**makeargv __P((char *));
37static void	settimeout __P((char *, char *, bool));
38static void	toomany __P((int, int));
39static char	*extrquotstr __P((char *, char **, char *, bool *));
40static void	parse_class_words __P((int, char *));
41
42
43#if _FFR_BOUNCE_QUEUE
44static char *bouncequeue = NULL;
45static void initbouncequeue __P((void));
46
47/*
48**  INITBOUNCEQUEUE -- determine BounceQueue if option is set.
49**
50**	Parameters:
51**		none.
52**
53**	Returns:
54**		none.
55**
56**	Side Effects:
57**		sets BounceQueue
58*/
59
60static void
61initbouncequeue()
62{
63	STAB *s;
64
65	BounceQueue = NOQGRP;
66	if (bouncequeue == NULL || bouncequeue[0] == '\0')
67		return;
68
69	s = stab(bouncequeue, ST_QUEUE, ST_FIND);
70	if (s == NULL)
71	{
72		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
73			"Warning: option BounceQueue: unknown queue group %s\n",
74			bouncequeue);
75	}
76	else
77		BounceQueue = s->s_quegrp->qg_index;
78}
79#endif /* _FFR_BOUNCE_QUEUE */
80
81#if _FFR_RCPTFLAGS
82void setupdynmailers __P((void));
83#else
84#define setupdynmailers()
85#endif
86
87/*
88**  READCF -- read configuration file.
89**
90**	This routine reads the configuration file and builds the internal
91**	form.
92**
93**	The file is formatted as a sequence of lines, each taken
94**	atomically.  The first character of each line describes how
95**	the line is to be interpreted.  The lines are:
96**		Dxval		Define macro x to have value val.
97**		Cxword		Put word into class x.
98**		Fxfile [fmt]	Read file for lines to put into
99**				class x.  Use scanf string 'fmt'
100**				or "%s" if not present.  Fmt should
101**				only produce one string-valued result.
102**		Hname: value	Define header with field-name 'name'
103**				and value as specified; this will be
104**				macro expanded immediately before
105**				use.
106**		Sn		Use rewriting set n.
107**		Rlhs rhs	Rewrite addresses that match lhs to
108**				be rhs.
109**		Mn arg=val...	Define mailer.  n is the internal name.
110**				Args specify mailer parameters.
111**		Oxvalue		Set option x to value.
112**		O option value	Set option (long name) to value.
113**		Pname=value	Set precedence name to value.
114**		Qn arg=val...	Define queue groups.  n is the internal name.
115**				Args specify queue parameters.
116**		Vversioncode[/vendorcode]
117**				Version level/vendor name of
118**				configuration syntax.
119**		Kmapname mapclass arguments....
120**				Define keyed lookup of a given class.
121**				Arguments are class dependent.
122**		Eenvar=value	Set the environment value to the given value.
123**
124**	Parameters:
125**		cfname -- configuration file name.
126**		safe -- true if this is the system config file;
127**			false otherwise.
128**		e -- the main envelope.
129**
130**	Returns:
131**		none.
132**
133**	Side Effects:
134**		Builds several internal tables.
135*/
136
137void
138readcf(cfname, safe, e)
139	char *cfname;
140	bool safe;
141	register ENVELOPE *e;
142{
143	SM_FILE_T *cf;
144	int ruleset = -1;
145	char *q;
146	struct rewrite *rwp = NULL;
147	char *bp;
148	auto char *ep;
149	int nfuzzy;
150	char *file;
151	bool optional;
152	bool ok;
153	bool ismap;
154	int mid;
155	register char *p;
156	long sff = SFF_OPENASROOT;
157	struct stat statb;
158	char buf[MAXLINE];
159	int bufsize;
160	char exbuf[MAXLINE];
161	char pvpbuf[MAXLINE + MAXATOM];
162	static char *null_list[1] = { NULL };
163	extern unsigned char TokTypeNoC[];
164
165	FileName = cfname;
166	LineNumber = 0;
167
168	if (DontLockReadFiles)
169		sff |= SFF_NOLOCK;
170	cf = safefopen(cfname, O_RDONLY, 0444, sff);
171	if (cf == NULL)
172	{
173		syserr("cannot open");
174		finis(false, true, EX_OSFILE);
175	}
176
177	if (fstat(sm_io_getinfo(cf, SM_IO_WHAT_FD, NULL), &statb) < 0)
178	{
179		syserr("cannot fstat");
180		finis(false, true, EX_OSFILE);
181	}
182
183	if (!S_ISREG(statb.st_mode))
184	{
185		syserr("not a plain file");
186		finis(false, true, EX_OSFILE);
187	}
188
189	if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
190	{
191		if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS || OpMode == MD_CHECKCONFIG)
192			(void) sm_io_fprintf(smioerr, SM_TIME_DEFAULT,
193					     "%s: WARNING: dangerous write permissions\n",
194					     FileName);
195		if (LogLevel > 0)
196			sm_syslog(LOG_CRIT, NOQID,
197				  "%s: WARNING: dangerous write permissions",
198				  FileName);
199	}
200
201#if XLA
202	xla_zero();
203#endif
204
205	while (bufsize = sizeof(buf),
206	       (bp = fgetfolded(buf, &bufsize, cf)) != NULL)
207	{
208		char *nbp;
209
210		if (bp[0] == '#')
211		{
212			if (bp != buf)
213				sm_free(bp); /* XXX */
214			continue;
215		}
216
217		/* do macro expansion mappings */
218		nbp = translate_dollars(bp, bp, &bufsize);
219		if (nbp != bp && bp != buf)
220			sm_free(bp);
221		bp = nbp;
222
223		/* interpret this line */
224		errno = 0;
225		switch (bp[0])
226		{
227		  case '\0':
228		  case '#':		/* comment */
229			break;
230
231		  case 'R':		/* rewriting rule */
232			if (ruleset < 0)
233			{
234				syserr("missing valid ruleset for \"%s\"", bp);
235				break;
236			}
237			for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
238				continue;
239
240			if (*p == '\0')
241			{
242				syserr("invalid rewrite line \"%s\" (tab expected)", bp);
243				break;
244			}
245
246			/* allocate space for the rule header */
247			if (rwp == NULL)
248			{
249				RewriteRules[ruleset] = rwp =
250					(struct rewrite *) xalloc(sizeof(*rwp));
251			}
252			else
253			{
254				rwp->r_next = (struct rewrite *) xalloc(sizeof(*rwp));
255				rwp = rwp->r_next;
256			}
257			rwp->r_next = NULL;
258
259			/* expand and save the LHS */
260			*p = '\0';
261			expand(&bp[1], exbuf, sizeof(exbuf), e);
262			rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
263					     sizeof(pvpbuf), NULL,
264					     ConfigLevel >= 9 ? TokTypeNoC : IntTokenTab,
265					     true);
266			nfuzzy = 0;
267			if (rwp->r_lhs != NULL)
268			{
269				register char **ap;
270
271				rwp->r_lhs = copyplist(rwp->r_lhs, true, NULL);
272
273				/* count the number of fuzzy matches in LHS */
274				for (ap = rwp->r_lhs; *ap != NULL; ap++)
275				{
276					char *botch;
277
278					botch = NULL;
279					switch (ap[0][0] & 0377)
280					{
281					  case MATCHZANY:
282					  case MATCHANY:
283					  case MATCHONE:
284					  case MATCHCLASS:
285					  case MATCHNCLASS:
286						nfuzzy++;
287						break;
288
289					  case MATCHREPL:
290						botch = "$1-$9";
291						break;
292
293					  case CANONUSER:
294						botch = "$:";
295						break;
296
297					  case CALLSUBR:
298						botch = "$>";
299						break;
300
301					  case CONDIF:
302						botch = "$?";
303						break;
304
305					  case CONDFI:
306						botch = "$.";
307						break;
308
309					  case HOSTBEGIN:
310						botch = "$[";
311						break;
312
313					  case HOSTEND:
314						botch = "$]";
315						break;
316
317					  case LOOKUPBEGIN:
318						botch = "$(";
319						break;
320
321					  case LOOKUPEND:
322						botch = "$)";
323						break;
324					}
325					if (botch != NULL)
326						syserr("Inappropriate use of %s on LHS",
327							botch);
328				}
329				rwp->r_line = LineNumber;
330			}
331			else
332			{
333				syserr("R line: null LHS");
334				rwp->r_lhs = null_list;
335			}
336			if (nfuzzy > MAXMATCH)
337			{
338				syserr("R line: too many wildcards");
339				rwp->r_lhs = null_list;
340			}
341
342			/* expand and save the RHS */
343			while (*++p == '\t')
344				continue;
345			q = p;
346			while (*p != '\0' && *p != '\t')
347				p++;
348			*p = '\0';
349			expand(q, exbuf, sizeof(exbuf), e);
350			rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
351					     sizeof(pvpbuf), NULL,
352					     ConfigLevel >= 9 ? TokTypeNoC : IntTokenTab,
353					     true);
354			if (rwp->r_rhs != NULL)
355			{
356				register char **ap;
357				int args, endtoken;
358#if _FFR_EXTRA_MAP_CHECK
359				int nexttoken;
360#endif
361				bool inmap;
362
363				rwp->r_rhs = copyplist(rwp->r_rhs, true, NULL);
364
365				/* check no out-of-bounds replacements */
366				nfuzzy += '0';
367				inmap = false;
368				args = 0;
369				endtoken = 0;
370				for (ap = rwp->r_rhs; *ap != NULL; ap++)
371				{
372					char *botch;
373
374					botch = NULL;
375					switch (ap[0][0] & 0377)
376					{
377					  case MATCHREPL:
378						if (ap[0][1] <= '0' ||
379						    ap[0][1] > nfuzzy)
380						{
381							syserr("replacement $%c out of bounds",
382								ap[0][1]);
383						}
384						break;
385
386					  case MATCHZANY:
387						botch = "$*";
388						break;
389
390					  case MATCHANY:
391						botch = "$+";
392						break;
393
394					  case MATCHONE:
395						botch = "$-";
396						break;
397
398					  case MATCHCLASS:
399						botch = "$=";
400						break;
401
402					  case MATCHNCLASS:
403						botch = "$~";
404						break;
405
406					  case CANONHOST:
407						if (!inmap)
408							break;
409						if (++args >= MAX_MAP_ARGS)
410							syserr("too many arguments for map lookup");
411						break;
412
413					  case HOSTBEGIN:
414						endtoken = HOSTEND;
415						/* FALLTHROUGH */
416					  case LOOKUPBEGIN:
417						/* see above... */
418						if ((ap[0][0] & 0377) == LOOKUPBEGIN)
419							endtoken = LOOKUPEND;
420						if (inmap)
421							syserr("cannot nest map lookups");
422						inmap = true;
423						args = 0;
424#if _FFR_EXTRA_MAP_CHECK
425						if (ap[1] == NULL)
426						{
427							syserr("syntax error in map lookup");
428							break;
429						}
430						nexttoken = ap[1][0] & 0377;
431						if (nexttoken == CANONHOST ||
432						    nexttoken == CANONUSER ||
433						    nexttoken == endtoken)
434						{
435							syserr("missing map name for lookup");
436							break;
437						}
438						if (ap[2] == NULL)
439						{
440							syserr("syntax error in map lookup");
441							break;
442						}
443						if ((unsigned char) ap[0][0] == HOSTBEGIN)
444							break;
445						nexttoken = ap[2][0] & 0377;
446						if (nexttoken == CANONHOST ||
447						    nexttoken == CANONUSER ||
448						    nexttoken == endtoken)
449						{
450							syserr("missing key name for lookup");
451							break;
452						}
453#endif /* _FFR_EXTRA_MAP_CHECK */
454						break;
455
456					  case HOSTEND:
457					  case LOOKUPEND:
458						if ((ap[0][0] & 0377) != endtoken)
459							break;
460						inmap = false;
461						endtoken = 0;
462						break;
463
464
465#if 0
466/*
467**  This doesn't work yet as there are maps defined *after* the cf
468**  is read such as host, user, and alias.  So for now, it's removed.
469**  When it comes back, the RELEASE_NOTES entry will be:
470**	Emit warnings for unknown maps when reading the .cf file.  Based on
471**		patch from Robert Harker of Harker Systems.
472*/
473
474					  case LOOKUPBEGIN:
475						/*
476						**  Got a database lookup,
477						**  check if map is defined.
478						*/
479
480						ep = ap[1];
481						if ((ep[0] & 0377) != MACRODEXPAND &&
482						    stab(ep, ST_MAP, ST_FIND) == NULL)
483						{
484							(void) sm_io_fprintf(smioout,
485									     SM_TIME_DEFAULT,
486									     "Warning: %s: line %d: map %s not found\n",
487									     FileName,
488									     LineNumber,
489									     ep);
490						}
491						break;
492#endif /* 0 */
493					}
494					if (botch != NULL)
495						syserr("Inappropriate use of %s on RHS",
496							botch);
497				}
498				if (inmap)
499					syserr("missing map closing token");
500			}
501			else
502			{
503				syserr("R line: null RHS");
504				rwp->r_rhs = null_list;
505			}
506			break;
507
508		  case 'S':		/* select rewriting set */
509			expand(&bp[1], exbuf, sizeof(exbuf), e);
510			ruleset = strtorwset(exbuf, NULL, ST_ENTER);
511			if (ruleset < 0)
512				break;
513
514			rwp = RewriteRules[ruleset];
515			if (rwp != NULL)
516			{
517				if (OpMode == MD_TEST || OpMode == MD_CHECKCONFIG)
518					(void) sm_io_fprintf(smioout,
519							     SM_TIME_DEFAULT,
520							     "WARNING: Ruleset %s has multiple definitions\n",
521							    &bp[1]);
522				if (tTd(37, 1))
523					sm_dprintf("WARNING: Ruleset %s has multiple definitions\n",
524						   &bp[1]);
525				while (rwp->r_next != NULL)
526					rwp = rwp->r_next;
527			}
528			break;
529
530		  case 'D':		/* macro definition */
531			mid = macid_parse(&bp[1], &ep);
532			if (mid == 0)
533				break;
534			p = munchstring(ep, NULL, '\0');
535			macdefine(&e->e_macro, A_TEMP, mid, p);
536			break;
537
538		  case 'H':		/* required header line */
539			(void) chompheader(&bp[1], CHHDR_DEF, NULL, e);
540			break;
541
542		  case 'C':		/* word class */
543		  case 'T':		/* trusted user (set class `t') */
544			if (bp[0] == 'C')
545			{
546				mid = macid_parse(&bp[1], &ep);
547				if (mid == 0)
548					break;
549				expand(ep, exbuf, sizeof(exbuf), e);
550				p = exbuf;
551			}
552			else
553			{
554				mid = 't';
555				p = &bp[1];
556			}
557			while (*p != '\0')
558			{
559				register char *wd;
560				char delim;
561
562				while (*p != '\0' && SM_ISSPACE(*p))
563					p++;
564				wd = p;
565				while (*p != '\0' && !(SM_ISSPACE(*p)))
566					p++;
567				delim = *p;
568				*p = '\0';
569				if (wd[0] != '\0')
570					setclass(mid, wd);
571				*p = delim;
572			}
573			break;
574
575		  case 'F':		/* word class from file */
576			mid = macid_parse(&bp[1], &ep);
577			if (mid == 0)
578				break;
579			for (p = ep; SM_ISSPACE(*p); )
580				p++;
581			if (p[0] == '-' && p[1] == 'o')
582			{
583				optional = true;
584				while (*p != '\0' &&
585				       !(SM_ISSPACE(*p)))
586					p++;
587				while (SM_ISSPACE(*p))
588					p++;
589			}
590			else
591				optional = false;
592
593			/* check if [key]@map:spec */
594			ismap = false;
595			if (!SM_IS_DIR_DELIM(*p) &&
596			    *p != '|' &&
597			    (q = strchr(p, '@')) != NULL)
598			{
599				q++;
600
601				/* look for @LDAP or @map: in string */
602				if (strcmp(q, "LDAP") == 0 ||
603				    (*q != ':' &&
604				     strchr(q, ':') != NULL))
605					ismap = true;
606			}
607
608			if (ismap)
609			{
610				/* use entire spec */
611				file = p;
612			}
613			else
614			{
615				file = extrquotstr(p, &q, " ", &ok);
616				if (!ok)
617				{
618					syserr("illegal filename '%s'", p);
619					break;
620				}
621			}
622
623			if (*file == '|' || ismap)
624				p = "%s";
625			else
626			{
627				p = q;
628				if (*p == '\0')
629					p = "%s";
630				else
631				{
632					*p = '\0';
633					while (isascii(*++p) && isspace(*p))
634						continue;
635				}
636			}
637			fileclass(mid, file, p, ismap, safe, optional);
638			break;
639
640#if XLA
641		  case 'L':		/* extended load average description */
642			xla_init(&bp[1]);
643			break;
644#endif
645
646#if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)
647		  case 'L':		/* lookup macro */
648		  case 'G':		/* lookup class */
649			/* reserved for Sun -- NIS+ database lookup */
650			if (VendorCode != VENDOR_SUN)
651				goto badline;
652			sun_lg_config_line(bp, e);
653			break;
654#endif /* defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO) */
655
656		  case 'M':		/* define mailer */
657			makemailer(&bp[1]);
658			break;
659
660		  case 'O':		/* set option */
661			setoption(bp[1], &bp[2], safe, false, e);
662			break;
663
664		  case 'P':		/* set precedence */
665			if (NumPriorities >= MAXPRIORITIES)
666			{
667				toomany('P', MAXPRIORITIES);
668				break;
669			}
670			for (p = &bp[1]; *p != '\0' && *p != '='; p++)
671				continue;
672			if (*p == '\0')
673				goto badline;
674			*p = '\0';
675			Priorities[NumPriorities].pri_name = newstr(&bp[1]);
676			Priorities[NumPriorities].pri_val = atoi(++p);
677			NumPriorities++;
678			break;
679
680		  case 'Q':		/* define queue */
681			makequeue(&bp[1], true);
682			break;
683
684		  case 'V':		/* configuration syntax version */
685			for (p = &bp[1]; SM_ISSPACE(*p); p++)
686				continue;
687			if (!isascii(*p) || !isdigit(*p))
688			{
689				syserr("invalid argument to V line: \"%.20s\"",
690					&bp[1]);
691				break;
692			}
693			ConfigLevel = strtol(p, &ep, 10);
694
695			/*
696			**  Do heuristic tweaking for back compatibility.
697			*/
698
699			if (ConfigLevel >= 5)
700			{
701				/* level 5 configs have short name in $w */
702				p = macvalue('w', e);
703				if (p != NULL && (p = strchr(p, '.')) != NULL)
704				{
705					*p = '\0';
706					macdefine(&e->e_macro, A_TEMP, 'w',
707						  macvalue('w', e));
708				}
709			}
710			if (ConfigLevel >= 6)
711			{
712				ColonOkInAddr = false;
713			}
714
715			/*
716			**  Look for vendor code.
717			*/
718
719			if (*ep++ == '/')
720			{
721				/* extract vendor code */
722				for (p = ep; isascii(*p) && isalpha(*p); )
723					p++;
724				*p = '\0';
725
726				if (!setvendor(ep))
727					syserr("invalid V line vendor code: \"%s\"",
728						ep);
729			}
730			break;
731
732		  case 'K':
733			expand(&bp[1], exbuf, sizeof(exbuf), e);
734			(void) makemapentry(exbuf);
735			break;
736
737		  case 'E':
738			p = strchr(bp, '=');
739			if (p != NULL)
740				*p++ = '\0';
741			sm_setuserenv(&bp[1], p);
742			break;
743
744		  case 'X':		/* mail filter */
745#if MILTER
746			milter_setup(&bp[1]);
747#else /* MILTER */
748			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
749					     "Warning: Filter usage ('X') requires Milter support (-DMILTER)\n");
750#endif /* MILTER */
751			break;
752
753		  default:
754		  badline:
755			syserr("unknown configuration line \"%s\"", bp);
756		}
757		if (bp != buf)
758			sm_free(bp); /* XXX */
759	}
760	if (sm_io_error(cf))
761	{
762		syserr("I/O read error");
763		finis(false, true, EX_OSFILE);
764	}
765	(void) sm_io_close(cf, SM_TIME_DEFAULT);
766	FileName = NULL;
767
768#if _FFR_BOUNCE_QUEUE
769	initbouncequeue();
770#endif
771
772	/* initialize host maps from local service tables */
773	inithostmaps();
774
775	/* initialize daemon (if not defined yet) */
776	initdaemon();
777
778	/* determine if we need to do special name-server frotz */
779	{
780		int nmaps;
781		char *maptype[MAXMAPSTACK];
782		short mapreturn[MAXMAPACTIONS];
783
784		nmaps = switch_map_find("hosts", maptype, mapreturn);
785		UseNameServer = false;
786		if (nmaps > 0 && nmaps <= MAXMAPSTACK)
787		{
788			register int mapno;
789
790			for (mapno = 0; mapno < nmaps && !UseNameServer;
791			     mapno++)
792			{
793				if (strcmp(maptype[mapno], "dns") == 0)
794					UseNameServer = true;
795			}
796		}
797	}
798	setupdynmailers();
799}
800
801/*
802**  TRANSLATE_DOLLARS -- convert $x into internal form
803**
804**	Actually does all appropriate pre-processing of a config line
805**	to turn it into internal form.
806**
807**	Parameters:
808**		ibp -- the buffer to translate.
809**		obp -- where to put the translation; may be the same as obp
810**		bsp -- a pointer to the size of obp; will be updated if
811**			the buffer needs to be replaced.
812**
813**	Returns:
814**		The buffer pointer; may differ from obp if the expansion
815**		is larger then *bsp, in which case this will point to
816**		malloc()ed memory which must be free()d by the caller.
817*/
818
819char *
820translate_dollars(ibp, obp, bsp)
821	char *ibp;
822	char *obp;
823	int *bsp;
824{
825	register char *p;
826	auto char *ep;
827	char *bp;
828
829	if (tTd(37, 53))
830	{
831		sm_dprintf("translate_dollars(");
832		xputs(sm_debug_file(), ibp);
833		sm_dprintf(")\n");
834	}
835
836	bp = quote_internal_chars(ibp, obp, bsp);
837
838	for (p = bp; *p != '\0'; p++)
839	{
840		if (*p == '#' && p > bp && ConfigLevel >= 3)
841		{
842			register char *e;
843
844			switch (*--p & 0377)
845			{
846			  case MACROEXPAND:
847				/* it's from $# -- let it go through */
848				p++;
849				break;
850
851			  case '\\':
852				/* it's backslash escaped */
853				(void) sm_strlcpy(p, p + 1, strlen(p));
854				break;
855
856			  default:
857				/* delete leading white space */
858				while (SM_ISSPACE(*p) &&
859				       *p != '\n' && p > bp)
860				{
861					p--;
862				}
863				if ((e = strchr(++p, '\n')) != NULL)
864					(void) sm_strlcpy(p, e, strlen(p));
865				else
866					*p-- = '\0';
867				break;
868			}
869			continue;
870		}
871
872		if (*p != '$' || p[1] == '\0')
873			continue;
874
875		if (p[1] == '$')
876		{
877			/* actual dollar sign.... */
878			(void) sm_strlcpy(p, p + 1, strlen(p));
879			continue;
880		}
881
882		/* convert to macro expansion character */
883		*p++ = MACROEXPAND;
884
885		/* special handling for $=, $~, $&, and $? */
886		if (*p == '=' || *p == '~' || *p == '&' || *p == '?')
887			p++;
888
889		/* convert macro name to code */
890		*p = macid_parse(p, &ep);
891		if (ep != p + 1)
892			(void) sm_strlcpy(p + 1, ep, strlen(p + 1));
893	}
894
895	/* strip trailing white space from the line */
896	while (--p > bp && SM_ISSPACE(*p))
897		*p = '\0';
898
899	if (tTd(37, 53))
900	{
901		sm_dprintf("  translate_dollars => ");
902		xputs(sm_debug_file(), bp);
903		sm_dprintf("\n");
904	}
905
906	return bp;
907}
908/*
909**  TOOMANY -- signal too many of some option
910**
911**	Parameters:
912**		id -- the id of the error line
913**		maxcnt -- the maximum possible values
914**
915**	Returns:
916**		none.
917**
918**	Side Effects:
919**		gives a syserr.
920*/
921
922static void
923toomany(id, maxcnt)
924	int id;
925	int maxcnt;
926{
927	syserr("too many %c lines, %d max", id, maxcnt);
928}
929/*
930**  FILECLASS -- read members of a class from a file
931**
932**	Parameters:
933**		class -- class to define.
934**		filename -- name of file to read.
935**		fmt -- scanf string to use for match.
936**		ismap -- if set, this is a map lookup.
937**		safe -- if set, this is a safe read.
938**		optional -- if set, it is not an error for the file to
939**			not exist.
940**
941**	Returns:
942**		none
943**
944**	Side Effects:
945**		puts all lines in filename that match a scanf into
946**			the named class.
947*/
948
949/*
950**  Break up the match into words and add to class.
951*/
952
953static void
954parse_class_words(class, line)
955	int class;
956	char *line;
957{
958	while (line != NULL && *line != '\0')
959	{
960		register char *q;
961
962		/* strip leading spaces */
963		while (SM_ISSPACE(*line))
964			line++;
965		if (*line == '\0')
966			break;
967
968		/* find the end of the word */
969		q = line;
970		while (*line != '\0' && !(SM_ISSPACE(*line)))
971			line++;
972		if (*line != '\0')
973			*line++ = '\0';
974
975		/* enter the word in the symbol table */
976		setclass(class, q);
977	}
978}
979
980static void
981fileclass(class, filename, fmt, ismap, safe, optional)
982	int class;
983	char *filename;
984	char *fmt;
985	bool ismap;
986	bool safe;
987	bool optional;
988{
989	SM_FILE_T *f;
990	long sff;
991	pid_t pid;
992	register char *p;
993	char buf[MAXLINE];
994
995	if (tTd(37, 2))
996		sm_dprintf("fileclass(%s, fmt=%s)\n", filename, fmt);
997
998	if (*filename == '\0')
999	{
1000		syserr("fileclass: missing file name");
1001		return;
1002	}
1003	else if (ismap)
1004	{
1005		int status = 0;
1006		char *key;
1007		char *mn;
1008		char *cl, *spec;
1009		STAB *mapclass;
1010		MAP map;
1011
1012		mn = newstr(macname(class));
1013
1014		key = filename;
1015
1016		/* skip past key */
1017		if ((p = strchr(filename, '@')) == NULL)
1018		{
1019			/* should not happen */
1020			syserr("fileclass: bogus map specification");
1021			sm_free(mn);
1022			return;
1023		}
1024
1025		/* skip past '@' */
1026		*p++ = '\0';
1027		cl = p;
1028
1029#if LDAPMAP
1030		if (strcmp(cl, "LDAP") == 0)
1031		{
1032			int n;
1033			char *lc;
1034			char jbuf[MAXHOSTNAMELEN];
1035			char lcbuf[MAXLINE];
1036
1037			/* Get $j */
1038			expand("\201j", jbuf, sizeof(jbuf), &BlankEnvelope);
1039			if (jbuf[0] == '\0')
1040			{
1041				(void) sm_strlcpy(jbuf, "localhost",
1042						  sizeof(jbuf));
1043			}
1044
1045			/* impose the default schema */
1046			lc = macvalue(macid("{sendmailMTACluster}"), CurEnv);
1047			if (lc == NULL)
1048				lc = "";
1049			else
1050			{
1051				expand(lc, lcbuf, sizeof(lcbuf), CurEnv);
1052				lc = lcbuf;
1053			}
1054
1055			cl = "ldap";
1056			n = sm_snprintf(buf, sizeof(buf),
1057					"-k (&(objectClass=sendmailMTAClass)(sendmailMTAClassName=%s)(|(sendmailMTACluster=%s)(sendmailMTAHost=%s))) -v sendmailMTAClassValue,sendmailMTAClassSearch:FILTER:sendmailMTAClass,sendmailMTAClassURL:URL:sendmailMTAClass",
1058					mn, lc, jbuf);
1059			if (n >= sizeof(buf))
1060			{
1061				syserr("fileclass: F{%s}: Default LDAP string too long",
1062				       mn);
1063				sm_free(mn);
1064				return;
1065			}
1066			spec = buf;
1067		}
1068		else
1069#endif /* LDAPMAP */
1070		{
1071			if ((spec = strchr(cl, ':')) == NULL)
1072			{
1073				syserr("fileclass: F{%s}: missing map class",
1074				       mn);
1075				sm_free(mn);
1076				return;
1077			}
1078			*spec++ ='\0';
1079		}
1080
1081		/* set up map structure */
1082		mapclass = stab(cl, ST_MAPCLASS, ST_FIND);
1083		if (mapclass == NULL)
1084		{
1085			syserr("fileclass: F{%s}: class %s not available",
1086			       mn, cl);
1087			sm_free(mn);
1088			return;
1089		}
1090		memset(&map, '\0', sizeof(map));
1091		map.map_class = &mapclass->s_mapclass;
1092		map.map_mname = mn;
1093		map.map_mflags |= MF_FILECLASS;
1094
1095		if (tTd(37, 5))
1096			sm_dprintf("fileclass: F{%s}: map class %s, key %s, spec %s\n",
1097				   mn, cl, key, spec);
1098
1099
1100		/* parse map spec */
1101		if (!map.map_class->map_parse(&map, spec))
1102		{
1103			/* map_parse() showed the error already */
1104			sm_free(mn);
1105			return;
1106		}
1107		map.map_mflags |= MF_VALID;
1108
1109		/* open map */
1110		if (map.map_class->map_open(&map, O_RDONLY))
1111		{
1112			map.map_mflags |= MF_OPEN;
1113			map.map_pid = getpid();
1114		}
1115		else
1116		{
1117			if (!optional &&
1118			    !bitset(MF_OPTIONAL, map.map_mflags))
1119				syserr("fileclass: F{%s}: map open failed",
1120				       mn);
1121			sm_free(mn);
1122			return;
1123		}
1124
1125		/* lookup */
1126		p = (*map.map_class->map_lookup)(&map, key, NULL, &status);
1127		if (status != EX_OK && status != EX_NOTFOUND)
1128		{
1129			if (!optional)
1130				syserr("fileclass: F{%s}: map lookup failed",
1131				       mn);
1132			p = NULL;
1133		}
1134
1135		/* use the results */
1136		if (p != NULL)
1137			parse_class_words(class, p);
1138
1139		/* close map */
1140		map.map_mflags |= MF_CLOSING;
1141		map.map_class->map_close(&map);
1142		map.map_mflags &= ~(MF_OPEN|MF_WRITABLE|MF_CLOSING);
1143		sm_free(mn);
1144		return;
1145	}
1146	else if (filename[0] == '|')
1147	{
1148		auto int fd;
1149		int i;
1150		char *argv[MAXPV + 1];
1151
1152		i = 0;
1153		for (p = strtok(&filename[1], " \t");
1154		     p != NULL && i < MAXPV;
1155		     p = strtok(NULL, " \t"))
1156			argv[i++] = p;
1157		argv[i] = NULL;
1158		pid = prog_open(argv, &fd, CurEnv);
1159		if (pid < 0)
1160			f = NULL;
1161		else
1162			f = sm_io_open(SmFtStdiofd, SM_TIME_DEFAULT,
1163				       (void *) &fd, SM_IO_RDONLY, NULL);
1164	}
1165	else
1166	{
1167		pid = -1;
1168		sff = SFF_REGONLY;
1169		if (!bitnset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
1170			sff |= SFF_SAFEDIRPATH;
1171		if (!bitnset(DBS_LINKEDCLASSFILEINWRITABLEDIR,
1172			     DontBlameSendmail))
1173			sff |= SFF_NOWLINK;
1174		if (safe)
1175			sff |= SFF_OPENASROOT;
1176		else if (RealUid == 0)
1177			sff |= SFF_ROOTOK;
1178		if (DontLockReadFiles)
1179			sff |= SFF_NOLOCK;
1180		f = safefopen(filename, O_RDONLY, 0, sff);
1181	}
1182	if (f == NULL)
1183	{
1184		if (!optional)
1185			syserr("fileclass: cannot open '%s'", filename);
1186		return;
1187	}
1188
1189	while (sm_io_fgets(f, SM_TIME_DEFAULT, buf, sizeof(buf)) >= 0)
1190	{
1191#if SCANF
1192		char wordbuf[MAXLINE + 1];
1193#endif
1194
1195		if (buf[0] == '#')
1196			continue;
1197#if SCANF
1198		if (sm_io_sscanf(buf, fmt, wordbuf) != 1)
1199			continue;
1200		p = wordbuf;
1201#else /* SCANF */
1202		p = buf;
1203#endif /* SCANF */
1204
1205		parse_class_words(class, p);
1206
1207		/*
1208		**  If anything else is added here,
1209		**  check if the '@' map case above
1210		**  needs the code as well.
1211		*/
1212	}
1213
1214	(void) sm_io_close(f, SM_TIME_DEFAULT);
1215	if (pid > 0)
1216		(void) waitfor(pid);
1217}
1218
1219#if _FFR_RCPTFLAGS
1220/* first character for dynamically created mailers */
1221static char dynmailerp = ' ';
1222
1223/* list of first characters for cf defined mailers */
1224static char frst[MAXMAILERS + 1];
1225
1226/*
1227**  SETUPDYNMAILERS -- find a char that isn't used as first element of any
1228**		mailer name.
1229**
1230**	Parameters:
1231**		none
1232**
1233**	Returns:
1234**		none
1235**
1236**	Note: space is not valid in cf defined mailers hence the function
1237**		will always find a char. It's not nice, but this is for
1238**		internal names only.
1239*/
1240
1241void
1242setupdynmailers()
1243{
1244	int i;
1245	char pp[] = "YXZ0123456789ABCDEFGHIJKLMNOPQRSTUVWyxzabcfghijkmnoqtuvw ";
1246
1247	frst[MAXMAILERS] = '\0';
1248	for (i = 0; i < strlen(pp); i++)
1249	{
1250		if (strchr(frst, pp[i]) == NULL)
1251		{
1252			dynmailerp = pp[i];
1253			if (tTd(25, 8))
1254				sm_dprintf("dynmailerp=%c\n", dynmailerp);
1255			return;
1256		}
1257	}
1258
1259	/* NOTREACHED */
1260	SM_ASSERT(0);
1261}
1262
1263/*
1264**  NEWMODMAILER -- Create a new mailer with modifications
1265**
1266**	Parameters:
1267**		rcpt -- current RCPT
1268**		fl -- flag to set
1269**
1270**	Returns:
1271**		true iff successful.
1272**
1273**	Note: this creates a copy of the mailer for the rcpt and
1274**		modifies exactly one flag.  It does not work
1275**		for multiple flags!
1276*/
1277
1278bool
1279newmodmailer(rcpt, fl)
1280	ADDRESS *rcpt;
1281	int fl;
1282{
1283	int idx;
1284	struct mailer *m;
1285	STAB *s;
1286	char mname[256];
1287
1288	SM_REQUIRE(rcpt != NULL);
1289	if (rcpt->q_mailer == NULL)
1290		return false;
1291	if (tTd(25, 8))
1292		sm_dprintf("newmodmailer: rcpt=%s\n", rcpt->q_paddr);
1293	SM_REQUIRE(rcpt->q_mailer->m_name != NULL);
1294	SM_REQUIRE(rcpt->q_mailer->m_name[0] != '\0');
1295	sm_strlcpy(mname, rcpt->q_mailer->m_name, sizeof(mname));
1296	mname[0] = dynmailerp;
1297	if (tTd(25, 8))
1298		sm_dprintf("newmodmailer: name=%s\n", mname);
1299	s = stab(mname, ST_MAILER, ST_ENTER);
1300	if (s->s_mailer != NULL)
1301	{
1302		idx = s->s_mailer->m_mno;
1303		if (tTd(25, 6))
1304			sm_dprintf("newmodmailer: found idx=%d\n", idx);
1305	}
1306	else
1307	{
1308		idx = rcpt->q_mailer->m_mno;
1309		idx += MAXMAILERS;
1310		if (tTd(25, 6))
1311			sm_dprintf("newmodmailer: idx=%d\n", idx);
1312		if (idx > SM_ARRAY_SIZE(Mailer))
1313			return false;
1314	}
1315
1316	m = Mailer[idx];
1317	if (m == NULL)
1318		m = (struct mailer *) xalloc(sizeof(*m));
1319	memset((char *) m, '\0', sizeof(*m));
1320	STRUCTCOPY(*rcpt->q_mailer, *m);
1321	Mailer[idx] = m;
1322
1323	/* "modify" the mailer */
1324	setbitn(bitidx(fl), m->m_flags);
1325	rcpt->q_mailer = m;
1326	m->m_mno = idx;
1327	m->m_name = newstr(mname);
1328	if (tTd(25, 1))
1329		sm_dprintf("newmodmailer: mailer[%d]=%s %p\n",
1330			idx, Mailer[idx]->m_name, Mailer[idx]);
1331
1332	return true;
1333}
1334
1335#endif /* _FFR_RCPTFLAGS */
1336
1337/*
1338**  MAKEMAILER -- define a new mailer.
1339**
1340**	Parameters:
1341**		line -- description of mailer.  This is in labeled
1342**			fields.  The fields are:
1343**			   A -- the argv for this mailer
1344**			   C -- the character set for MIME conversions
1345**			   D -- the directory to run in
1346**			   E -- the eol string
1347**			   F -- the flags associated with the mailer
1348**			   L -- the maximum line length
1349**			   M -- the maximum message size
1350**			   N -- the niceness at which to run
1351**			   P -- the path to the mailer
1352**			   Q -- the queue group for the mailer
1353**			   R -- the recipient rewriting set
1354**			   S -- the sender rewriting set
1355**			   T -- the mailer type (for DSNs)
1356**			   U -- the uid to run as
1357**			   W -- the time to wait at the end
1358**			   m -- maximum messages per connection
1359**			   r -- maximum number of recipients per message
1360**			   / -- new root directory
1361**			The first word is the canonical name of the mailer.
1362**
1363**	Returns:
1364**		none.
1365**
1366**	Side Effects:
1367**		enters the mailer into the mailer table.
1368*/
1369
1370
1371void
1372makemailer(line)
1373	char *line;
1374{
1375	register char *p;
1376	register struct mailer *m;
1377	register STAB *s;
1378	int i;
1379	char fcode;
1380	auto char *endp;
1381	static int nextmailer = 0;	/* "free" index into Mailer struct */
1382
1383	/* allocate a mailer and set up defaults */
1384	m = (struct mailer *) xalloc(sizeof(*m));
1385	memset((char *) m, '\0', sizeof(*m));
1386	errno = 0; /* avoid bogus error text */
1387
1388	/* collect the mailer name */
1389	for (p = line;
1390	     *p != '\0' && *p != ',' && !(SM_ISSPACE(*p));
1391	     p++)
1392		continue;
1393	if (*p != '\0')
1394		*p++ = '\0';
1395	if (line[0] == '\0')
1396	{
1397		syserr("name required for mailer");
1398		return;
1399	}
1400	m->m_name = newstr(line);
1401#if _FFR_RCPTFLAGS
1402	frst[nextmailer] = line[0];
1403#endif
1404	m->m_qgrp = NOQGRP;
1405	m->m_uid = NO_UID;
1406	m->m_gid = NO_GID;
1407
1408	/* now scan through and assign info from the fields */
1409	while (*p != '\0')
1410	{
1411		auto char *delimptr;
1412
1413		while (*p != '\0' &&
1414		       (*p == ',' || (SM_ISSPACE(*p))))
1415			p++;
1416
1417		/* p now points to field code */
1418		fcode = *p;
1419		while (*p != '\0' && *p != '=' && *p != ',')
1420			p++;
1421		if (*p++ != '=')
1422		{
1423			syserr("mailer %s: `=' expected", m->m_name);
1424			return;
1425		}
1426		while (SM_ISSPACE(*p))
1427			p++;
1428
1429		/* p now points to the field body */
1430		p = munchstring(p, &delimptr, ',');
1431
1432		/* install the field into the mailer struct */
1433		switch (fcode)
1434		{
1435		  case 'P':		/* pathname */
1436			if (*p != '\0')	/* error is issued below */
1437				m->m_mailer = newstr(p);
1438			break;
1439
1440		  case 'F':		/* flags */
1441			for (; *p != '\0'; p++)
1442			{
1443				if (!(SM_ISSPACE(*p)))
1444				{
1445					if (*p == M_INTERNAL)
1446						sm_syslog(LOG_WARNING, NOQID,
1447							  "WARNING: mailer=%s, flag=%c deprecated",
1448							  m->m_name, *p);
1449					setbitn(bitidx(*p), m->m_flags);
1450				}
1451			}
1452			break;
1453
1454		  case 'S':		/* sender rewriting ruleset */
1455		  case 'R':		/* recipient rewriting ruleset */
1456			i = strtorwset(p, &endp, ST_ENTER);
1457			if (i < 0)
1458				return;
1459			if (fcode == 'S')
1460				m->m_sh_rwset = m->m_se_rwset = i;
1461			else
1462				m->m_rh_rwset = m->m_re_rwset = i;
1463
1464			p = endp;
1465			if (*p++ == '/')
1466			{
1467				i = strtorwset(p, NULL, ST_ENTER);
1468				if (i < 0)
1469					return;
1470				if (fcode == 'S')
1471					m->m_sh_rwset = i;
1472				else
1473					m->m_rh_rwset = i;
1474			}
1475			break;
1476
1477		  case 'E':		/* end of line string */
1478			if (*p == '\0')
1479				syserr("mailer %s: null end-of-line string",
1480					m->m_name);
1481			else
1482				m->m_eol = newstr(p);
1483			break;
1484
1485		  case 'A':		/* argument vector */
1486			if (*p != '\0')	/* error is issued below */
1487				m->m_argv = makeargv(p);
1488			break;
1489
1490		  case 'M':		/* maximum message size */
1491			m->m_maxsize = atol(p);
1492			break;
1493
1494		  case 'm':		/* maximum messages per connection */
1495			m->m_maxdeliveries = atoi(p);
1496			break;
1497
1498		  case 'r':		/* max recipient per envelope */
1499			m->m_maxrcpt = atoi(p);
1500			break;
1501
1502		  case 'L':		/* maximum line length */
1503			m->m_linelimit = atoi(p);
1504			if (m->m_linelimit < 0)
1505				m->m_linelimit = 0;
1506			break;
1507
1508		  case 'N':		/* run niceness */
1509			m->m_nice = atoi(p);
1510			break;
1511
1512		  case 'D':		/* working directory */
1513			if (*p == '\0')
1514				syserr("mailer %s: null working directory",
1515					m->m_name);
1516			else
1517				m->m_execdir = newstr(p);
1518			break;
1519
1520		  case 'C':		/* default charset */
1521			if (*p == '\0')
1522				syserr("mailer %s: null charset", m->m_name);
1523			else
1524				m->m_defcharset = newstr(p);
1525			break;
1526
1527		  case 'Q':		/* queue for this mailer */
1528			if (*p == '\0')
1529			{
1530				syserr("mailer %s: null queue", m->m_name);
1531				break;
1532			}
1533			s = stab(p, ST_QUEUE, ST_FIND);
1534			if (s == NULL)
1535				syserr("mailer %s: unknown queue %s",
1536					m->m_name, p);
1537			else
1538				m->m_qgrp = s->s_quegrp->qg_index;
1539			break;
1540
1541		  case 'T':		/* MTA-Name/Address/Diagnostic types */
1542			/* extract MTA name type; default to "dns" */
1543			m->m_mtatype = newstr(p);
1544			p = strchr(m->m_mtatype, '/');
1545			if (p != NULL)
1546			{
1547				*p++ = '\0';
1548				if (*p == '\0')
1549					p = NULL;
1550			}
1551			if (*m->m_mtatype == '\0')
1552				m->m_mtatype = "dns";
1553
1554			/* extract address type; default to "rfc822" */
1555			m->m_addrtype = p;
1556			if (p != NULL)
1557				p = strchr(p, '/');
1558			if (p != NULL)
1559			{
1560				*p++ = '\0';
1561				if (*p == '\0')
1562					p = NULL;
1563			}
1564			if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
1565				m->m_addrtype = "rfc822";
1566
1567			/* extract diagnostic type; default to "smtp" */
1568			m->m_diagtype = p;
1569			if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
1570				m->m_diagtype = "smtp";
1571			break;
1572
1573		  case 'U':		/* user id */
1574			if (isascii(*p) && !isdigit(*p))
1575			{
1576				char *q = p;
1577				struct passwd *pw;
1578
1579				while (*p != '\0' && isascii(*p) &&
1580# if _FFR_DOTTED_USERNAMES
1581				       (isalnum(*p) || strchr(SM_PWN_CHARS, *p) != NULL))
1582# else
1583				       (isalnum(*p) || strchr("-_", *p) != NULL))
1584# endif
1585					p++;
1586				while (SM_ISSPACE(*p))
1587					*p++ = '\0';
1588				if (*p != '\0')
1589					*p++ = '\0';
1590				if (*q == '\0')
1591				{
1592					syserr("mailer %s: null user name",
1593						m->m_name);
1594					break;
1595				}
1596				pw = sm_getpwnam(q);
1597				if (pw == NULL)
1598				{
1599					syserr("readcf: mailer U= flag: unknown user %s", q);
1600					break;
1601				}
1602				else
1603				{
1604					m->m_uid = pw->pw_uid;
1605					m->m_gid = pw->pw_gid;
1606				}
1607			}
1608			else
1609			{
1610				auto char *q;
1611
1612				m->m_uid = strtol(p, &q, 0);
1613				p = q;
1614				while (SM_ISSPACE(*p))
1615					p++;
1616				if (*p != '\0')
1617					p++;
1618			}
1619			while (SM_ISSPACE(*p))
1620				p++;
1621			if (*p == '\0')
1622				break;
1623			if (isascii(*p) && !isdigit(*p))
1624			{
1625				char *q = p;
1626				struct group *gr;
1627
1628				while (isascii(*p) &&
1629				       (isalnum(*p) || strchr(SM_PWN_CHARS, *p) != NULL))
1630					p++;
1631				*p++ = '\0';
1632				if (*q == '\0')
1633				{
1634					syserr("mailer %s: null group name",
1635						m->m_name);
1636					break;
1637				}
1638				gr = getgrnam(q);
1639				if (gr == NULL)
1640				{
1641					syserr("readcf: mailer U= flag: unknown group %s", q);
1642					break;
1643				}
1644				else
1645					m->m_gid = gr->gr_gid;
1646			}
1647			else
1648			{
1649				m->m_gid = strtol(p, NULL, 0);
1650			}
1651			break;
1652
1653		  case 'W':		/* wait timeout */
1654			m->m_wait = convtime(p, 's');
1655			break;
1656
1657		  case '/':		/* new root directory */
1658			if (*p == '\0')
1659				syserr("mailer %s: null root directory",
1660					m->m_name);
1661			else
1662				m->m_rootdir = newstr(p);
1663			break;
1664
1665		  default:
1666			syserr("M%s: unknown mailer equate %c=",
1667			       m->m_name, fcode);
1668			break;
1669		}
1670
1671		p = delimptr;
1672	}
1673
1674#if !HASRRESVPORT
1675	if (bitnset(M_SECURE_PORT, m->m_flags))
1676	{
1677		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1678				     "M%s: Warning: F=%c set on system that doesn't support rresvport()\n",
1679				     m->m_name, M_SECURE_PORT);
1680	}
1681#endif /* !HASRRESVPORT */
1682
1683#if !HASNICE
1684	if (m->m_nice != 0)
1685	{
1686		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1687				     "M%s: Warning: N= set on system that doesn't support nice()\n",
1688				     m->m_name);
1689	}
1690#endif /* !HASNICE */
1691
1692	/* do some rationality checking */
1693	if (m->m_argv == NULL)
1694	{
1695		syserr("M%s: A= argument required", m->m_name);
1696		return;
1697	}
1698	if (m->m_mailer == NULL)
1699	{
1700		syserr("M%s: P= argument required", m->m_name);
1701		return;
1702	}
1703
1704	if (nextmailer >= MAXMAILERS)
1705	{
1706		syserr("too many mailers defined (%d max)", MAXMAILERS);
1707		return;
1708	}
1709
1710	if (m->m_maxrcpt <= 0)
1711		m->m_maxrcpt = DEFAULT_MAX_RCPT;
1712
1713	/* do some heuristic cleanup for back compatibility */
1714	if (bitnset(M_LIMITS, m->m_flags))
1715	{
1716		if (m->m_linelimit == 0)
1717			m->m_linelimit = SMTPLINELIM;
1718		if (ConfigLevel < 2)
1719			setbitn(M_7BITS, m->m_flags);
1720	}
1721
1722	if (strcmp(m->m_mailer, "[TCP]") == 0)
1723	{
1724		syserr("M%s: P=[TCP] must be replaced by P=[IPC]", m->m_name);
1725		return;
1726	}
1727
1728	if (strcmp(m->m_mailer, "[IPC]") == 0)
1729	{
1730		/* Use the second argument for host or path to socket */
1731		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1732		    m->m_argv[1][0] == '\0')
1733		{
1734			syserr("M%s: too few parameters for %s mailer",
1735			       m->m_name, m->m_mailer);
1736			return;
1737		}
1738		if (strcmp(m->m_argv[0], "TCP") != 0
1739#if NETUNIX
1740		    && strcmp(m->m_argv[0], "FILE") != 0
1741#endif
1742		    )
1743		{
1744			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
1745					     "M%s: Warning: first argument in %s mailer must be %s\n",
1746					     m->m_name, m->m_mailer,
1747#if NETUNIX
1748					     "TCP or FILE"
1749#else
1750					     "TCP"
1751#endif
1752				     );
1753		}
1754		if (m->m_mtatype == NULL)
1755			m->m_mtatype = "dns";
1756		if (m->m_addrtype == NULL)
1757			m->m_addrtype = "rfc822";
1758		if (m->m_diagtype == NULL)
1759		{
1760			if (m->m_argv[0] != NULL &&
1761			    strcmp(m->m_argv[0], "FILE") == 0)
1762				m->m_diagtype = "x-unix";
1763			else
1764				m->m_diagtype = "smtp";
1765		}
1766	}
1767	else if (strcmp(m->m_mailer, "[FILE]") == 0)
1768	{
1769		/* Use the second argument for filename */
1770		if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1771		    m->m_argv[2] != NULL)
1772		{
1773			syserr("M%s: too %s parameters for [FILE] mailer",
1774			       m->m_name,
1775			       (m->m_argv[0] == NULL ||
1776				m->m_argv[1] == NULL) ? "few" : "many");
1777			return;
1778		}
1779		else if (strcmp(m->m_argv[0], "FILE") != 0)
1780		{
1781			syserr("M%s: first argument in [FILE] mailer must be FILE",
1782			       m->m_name);
1783			return;
1784		}
1785	}
1786
1787	if (m->m_eol == NULL)
1788	{
1789		char **pp;
1790
1791		/* default for SMTP is \r\n; use \n for local delivery */
1792		for (pp = m->m_argv; *pp != NULL; pp++)
1793		{
1794			for (p = *pp; *p != '\0'; )
1795			{
1796				if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
1797					break;
1798			}
1799			if (*p != '\0')
1800				break;
1801		}
1802		if (*pp == NULL)
1803			m->m_eol = "\r\n";
1804		else
1805			m->m_eol = "\n";
1806	}
1807
1808	/* enter the mailer into the symbol table */
1809	s = stab(m->m_name, ST_MAILER, ST_ENTER);
1810	if (s->s_mailer != NULL)
1811	{
1812		i = s->s_mailer->m_mno;
1813		sm_free(s->s_mailer); /* XXX */
1814	}
1815	else
1816	{
1817		i = nextmailer++;
1818	}
1819	Mailer[i] = s->s_mailer = m;
1820	m->m_mno = i;
1821}
1822/*
1823**  MUNCHSTRING -- translate a string into internal form.
1824**
1825**	Parameters:
1826**		p -- the string to munch.
1827**		delimptr -- if non-NULL, set to the pointer of the
1828**			field delimiter character.
1829**		delim -- the delimiter for the field.
1830**
1831**	Returns:
1832**		the munched string.
1833**
1834**	Side Effects:
1835**		the munched string is a local static buffer.
1836**		it must be copied before the function is called again.
1837*/
1838
1839char *
1840munchstring(p, delimptr, delim)
1841	register char *p;
1842	char **delimptr;
1843	int delim;
1844{
1845	register char *q;
1846	bool backslash = false;
1847	bool quotemode = false;
1848	static char buf[MAXLINE];
1849
1850	for (q = buf; *p != '\0' && q < &buf[sizeof(buf) - 1]; p++)
1851	{
1852		if (backslash)
1853		{
1854			/* everything is roughly literal */
1855			backslash = false;
1856			switch (*p)
1857			{
1858			  case 'r':		/* carriage return */
1859				*q++ = '\r';
1860				continue;
1861
1862			  case 'n':		/* newline */
1863				*q++ = '\n';
1864				continue;
1865
1866			  case 'f':		/* form feed */
1867				*q++ = '\f';
1868				continue;
1869
1870			  case 'b':		/* backspace */
1871				*q++ = '\b';
1872				continue;
1873			}
1874			*q++ = *p;
1875		}
1876		else
1877		{
1878			if (*p == '\\')
1879				backslash = true;
1880			else if (*p == '"')
1881				quotemode = !quotemode;
1882			else if (quotemode || *p != delim)
1883				*q++ = *p;
1884			else
1885				break;
1886		}
1887	}
1888
1889	if (delimptr != NULL)
1890		*delimptr = p;
1891	*q++ = '\0';
1892	return buf;
1893}
1894/*
1895**  EXTRQUOTSTR -- extract a (quoted) string.
1896**
1897**	This routine deals with quoted (") strings and escaped
1898**	spaces (\\ ).
1899**
1900**	Parameters:
1901**		p -- source string.
1902**		delimptr -- if non-NULL, set to the pointer of the
1903**			field delimiter character.
1904**		delimbuf -- delimiters for the field.
1905**		st -- if non-NULL, store the return value (whether the
1906**			string was correctly quoted) here.
1907**
1908**	Returns:
1909**		the extracted string.
1910**
1911**	Side Effects:
1912**		the returned string is a local static buffer.
1913**		it must be copied before the function is called again.
1914*/
1915
1916static char *
1917extrquotstr(p, delimptr, delimbuf, st)
1918	register char *p;
1919	char **delimptr;
1920	char *delimbuf;
1921	bool *st;
1922{
1923	register char *q;
1924	bool backslash = false;
1925	bool quotemode = false;
1926	static char buf[MAXLINE];
1927
1928	for (q = buf; *p != '\0' && q < &buf[sizeof(buf) - 1]; p++)
1929	{
1930		if (backslash)
1931		{
1932			backslash = false;
1933			if (*p != ' ')
1934				*q++ = '\\';
1935		}
1936		if (*p == '\\')
1937			backslash = true;
1938		else if (*p == '"')
1939			quotemode = !quotemode;
1940		else if (quotemode ||
1941			 strchr(delimbuf, (int) *p) == NULL)
1942			*q++ = *p;
1943		else
1944			break;
1945	}
1946
1947	if (delimptr != NULL)
1948		*delimptr = p;
1949	*q++ = '\0';
1950	if (st != NULL)
1951		*st = !(quotemode || backslash);
1952	return buf;
1953}
1954/*
1955**  MAKEARGV -- break up a string into words
1956**
1957**	Parameters:
1958**		p -- the string to break up.
1959**
1960**	Returns:
1961**		a char **argv (dynamically allocated)
1962**
1963**	Side Effects:
1964**		munges p.
1965*/
1966
1967static char **
1968makeargv(p)
1969	register char *p;
1970{
1971	char *q;
1972	int i;
1973	char **avp;
1974	char *argv[MAXPV + 1];
1975
1976	/* take apart the words */
1977	i = 0;
1978	while (*p != '\0' && i < MAXPV)
1979	{
1980		q = p;
1981		while (*p != '\0' && !(SM_ISSPACE(*p)))
1982			p++;
1983		while (SM_ISSPACE(*p))
1984			*p++ = '\0';
1985		argv[i++] = newstr(q);
1986	}
1987	argv[i++] = NULL;
1988
1989	/* now make a copy of the argv */
1990	avp = (char **) xalloc(sizeof(*avp) * i);
1991	memmove((char *) avp, (char *) argv, sizeof(*avp) * i);
1992
1993	return avp;
1994}
1995/*
1996**  PRINTRULES -- print rewrite rules (for debugging)
1997**
1998**	Parameters:
1999**		none.
2000**
2001**	Returns:
2002**		none.
2003**
2004**	Side Effects:
2005**		prints rewrite rules.
2006*/
2007
2008void
2009printrules()
2010{
2011	register struct rewrite *rwp;
2012	register int ruleset;
2013
2014	for (ruleset = 0; ruleset < 10; ruleset++)
2015	{
2016		if (RewriteRules[ruleset] == NULL)
2017			continue;
2018		sm_dprintf("\n----Rule Set %d:", ruleset);
2019
2020		for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
2021		{
2022			sm_dprintf("\nLHS:");
2023			printav(sm_debug_file(), rwp->r_lhs);
2024			sm_dprintf("RHS:");
2025			printav(sm_debug_file(), rwp->r_rhs);
2026		}
2027	}
2028}
2029/*
2030**  PRINTMAILER -- print mailer structure (for debugging)
2031**
2032**	Parameters:
2033**		fp -- output file
2034**		m -- the mailer to print
2035**
2036**	Returns:
2037**		none.
2038*/
2039
2040void
2041printmailer(fp, m)
2042	SM_FILE_T *fp;
2043	register MAILER *m;
2044{
2045	int j;
2046
2047	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2048			     "mailer %d (%s): P=%s S=", m->m_mno, m->m_name,
2049			     m->m_mailer);
2050	if (RuleSetNames[m->m_se_rwset] == NULL)
2051		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d/",
2052				     m->m_se_rwset);
2053	else
2054		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s/",
2055				     RuleSetNames[m->m_se_rwset]);
2056	if (RuleSetNames[m->m_sh_rwset] == NULL)
2057		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d R=",
2058				     m->m_sh_rwset);
2059	else
2060		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s R=",
2061				     RuleSetNames[m->m_sh_rwset]);
2062	if (RuleSetNames[m->m_re_rwset] == NULL)
2063		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d/",
2064				     m->m_re_rwset);
2065	else
2066		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s/",
2067				     RuleSetNames[m->m_re_rwset]);
2068	if (RuleSetNames[m->m_rh_rwset] == NULL)
2069		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%d ",
2070				     m->m_rh_rwset);
2071	else
2072		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "%s ",
2073				     RuleSetNames[m->m_rh_rwset]);
2074	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "M=%ld U=%d:%d F=",
2075			     m->m_maxsize, (int) m->m_uid, (int) m->m_gid);
2076	for (j = '\0'; j <= '\177'; j++)
2077		if (bitnset(j, m->m_flags))
2078			(void) sm_io_putc(fp, SM_TIME_DEFAULT, j);
2079	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " L=%d E=",
2080			     m->m_linelimit);
2081	xputs(fp, m->m_eol);
2082	if (m->m_defcharset != NULL)
2083		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " C=%s",
2084				     m->m_defcharset);
2085	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " T=%s/%s/%s",
2086			     m->m_mtatype == NULL
2087				? "<undefined>" : m->m_mtatype,
2088			     m->m_addrtype == NULL
2089				? "<undefined>" : m->m_addrtype,
2090			     m->m_diagtype == NULL
2091				? "<undefined>" : m->m_diagtype);
2092	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " r=%d", m->m_maxrcpt);
2093	if (m->m_argv != NULL)
2094	{
2095		char **a = m->m_argv;
2096
2097		(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, " A=");
2098		while (*a != NULL)
2099		{
2100			if (a != m->m_argv)
2101				(void) sm_io_fprintf(fp, SM_TIME_DEFAULT,
2102						     " ");
2103			xputs(fp, *a++);
2104		}
2105	}
2106	(void) sm_io_fprintf(fp, SM_TIME_DEFAULT, "\n");
2107}
2108
2109#if STARTTLS
2110static struct ssl_options
2111{
2112	const char	*sslopt_name;	/* name of the flag */
2113	long		sslopt_bits;	/* bits to set/clear */
2114} SSL_Option[] =
2115{
2116/* Workaround for bugs are turned on by default (as well as some others) */
2117#ifdef SSL_OP_MICROSOFT_SESS_ID_BUG
2118	{ "SSL_OP_MICROSOFT_SESS_ID_BUG",	SSL_OP_MICROSOFT_SESS_ID_BUG	},
2119#endif
2120#ifdef SSL_OP_NETSCAPE_CHALLENGE_BUG
2121	{ "SSL_OP_NETSCAPE_CHALLENGE_BUG",	SSL_OP_NETSCAPE_CHALLENGE_BUG	},
2122#endif
2123#ifdef SSL_OP_LEGACY_SERVER_CONNECT
2124	{ "SSL_OP_LEGACY_SERVER_CONNECT",	SSL_OP_LEGACY_SERVER_CONNECT	},
2125#endif
2126#ifdef SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG
2127	{ "SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG",	SSL_OP_NETSCAPE_REUSE_CIPHER_CHANGE_BUG	},
2128#endif
2129#ifdef SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG
2130	{ "SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG",	SSL_OP_SSLREF2_REUSE_CERT_TYPE_BUG	},
2131#endif
2132#ifdef SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER
2133	{ "SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER",	SSL_OP_MICROSOFT_BIG_SSLV3_BUFFER	},
2134#endif
2135#ifdef SSL_OP_MSIE_SSLV2_RSA_PADDING
2136	{ "SSL_OP_MSIE_SSLV2_RSA_PADDING",	SSL_OP_MSIE_SSLV2_RSA_PADDING	},
2137#endif
2138#ifdef SSL_OP_SSLEAY_080_CLIENT_DH_BUG
2139	{ "SSL_OP_SSLEAY_080_CLIENT_DH_BUG",	SSL_OP_SSLEAY_080_CLIENT_DH_BUG	},
2140#endif
2141#ifdef SSL_OP_TLS_D5_BUG
2142	{ "SSL_OP_TLS_D5_BUG",	SSL_OP_TLS_D5_BUG	},
2143#endif
2144#ifdef SSL_OP_TLS_BLOCK_PADDING_BUG
2145	{ "SSL_OP_TLS_BLOCK_PADDING_BUG",	SSL_OP_TLS_BLOCK_PADDING_BUG	},
2146#endif
2147#ifdef SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
2148	{ "SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS",	SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS	},
2149#endif
2150#ifdef SSL_OP_ALL
2151	{ "SSL_OP_ALL",	SSL_OP_ALL	},
2152#endif
2153#ifdef SSL_OP_NO_QUERY_MTU
2154	{ "SSL_OP_NO_QUERY_MTU",	SSL_OP_NO_QUERY_MTU	},
2155#endif
2156#ifdef SSL_OP_COOKIE_EXCHANGE
2157	{ "SSL_OP_COOKIE_EXCHANGE",	SSL_OP_COOKIE_EXCHANGE	},
2158#endif
2159#ifdef SSL_OP_NO_TICKET
2160	{ "SSL_OP_NO_TICKET",	SSL_OP_NO_TICKET	},
2161#endif
2162#ifdef SSL_OP_CISCO_ANYCONNECT
2163	{ "SSL_OP_CISCO_ANYCONNECT",	SSL_OP_CISCO_ANYCONNECT	},
2164#endif
2165#ifdef SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION
2166	{ "SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION",	SSL_OP_NO_SESSION_RESUMPTION_ON_RENEGOTIATION	},
2167#endif
2168#ifdef SSL_OP_NO_COMPRESSION
2169	{ "SSL_OP_NO_COMPRESSION",	SSL_OP_NO_COMPRESSION	},
2170#endif
2171#ifdef SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION
2172	{ "SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION",	SSL_OP_ALLOW_UNSAFE_LEGACY_RENEGOTIATION	},
2173#endif
2174#ifdef SSL_OP_SINGLE_ECDH_USE
2175	{ "SSL_OP_SINGLE_ECDH_USE",	SSL_OP_SINGLE_ECDH_USE	},
2176#endif
2177#ifdef SSL_OP_SINGLE_DH_USE
2178	{ "SSL_OP_SINGLE_DH_USE",	SSL_OP_SINGLE_DH_USE	},
2179#endif
2180#ifdef SSL_OP_EPHEMERAL_RSA
2181	{ "SSL_OP_EPHEMERAL_RSA",	SSL_OP_EPHEMERAL_RSA	},
2182#endif
2183#ifdef SSL_OP_CIPHER_SERVER_PREFERENCE
2184	{ "SSL_OP_CIPHER_SERVER_PREFERENCE",	SSL_OP_CIPHER_SERVER_PREFERENCE	},
2185#endif
2186#ifdef SSL_OP_TLS_ROLLBACK_BUG
2187	{ "SSL_OP_TLS_ROLLBACK_BUG",	SSL_OP_TLS_ROLLBACK_BUG	},
2188#endif
2189#ifdef SSL_OP_NO_SSLv2
2190	{ "SSL_OP_NO_SSLv2",	SSL_OP_NO_SSLv2	},
2191#endif
2192#ifdef SSL_OP_NO_SSLv3
2193	{ "SSL_OP_NO_SSLv3",	SSL_OP_NO_SSLv3	},
2194#endif
2195#ifdef SSL_OP_NO_TLSv1
2196	{ "SSL_OP_NO_TLSv1",	SSL_OP_NO_TLSv1	},
2197#endif
2198#ifdef SSL_OP_NO_TLSv1_3
2199	{ "SSL_OP_NO_TLSv1_3",	SSL_OP_NO_TLSv1_3	},
2200#endif
2201#ifdef SSL_OP_NO_TLSv1_2
2202	{ "SSL_OP_NO_TLSv1_2",	SSL_OP_NO_TLSv1_2	},
2203#endif
2204#ifdef SSL_OP_NO_TLSv1_1
2205	{ "SSL_OP_NO_TLSv1_1",	SSL_OP_NO_TLSv1_1	},
2206#endif
2207#ifdef SSL_OP_PKCS1_CHECK_1
2208	{ "SSL_OP_PKCS1_CHECK_1",	SSL_OP_PKCS1_CHECK_1	},
2209#endif
2210#ifdef SSL_OP_PKCS1_CHECK_2
2211	{ "SSL_OP_PKCS1_CHECK_2",	SSL_OP_PKCS1_CHECK_2	},
2212#endif
2213#ifdef SSL_OP_NETSCAPE_CA_DN_BUG
2214	{ "SSL_OP_NETSCAPE_CA_DN_BUG",	SSL_OP_NETSCAPE_CA_DN_BUG	},
2215#endif
2216#ifdef SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG
2217	{ "SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG",	SSL_OP_NETSCAPE_DEMO_CIPHER_CHANGE_BUG	},
2218#endif
2219#ifdef SSL_OP_CRYPTOPRO_TLSEXT_BUG
2220	{ "SSL_OP_CRYPTOPRO_TLSEXT_BUG",	SSL_OP_CRYPTOPRO_TLSEXT_BUG	},
2221#endif
2222#ifdef SSL_OP_TLSEXT_PADDING
2223	{ "SSL_OP_TLSEXT_PADDING",	SSL_OP_TLSEXT_PADDING	},
2224#endif
2225#ifdef SSL_OP_NO_RENEGOTIATION
2226	{ "SSL_OP_NO_RENEGOTIATION",    SSL_OP_NO_RENEGOTIATION },
2227#endif
2228#ifdef SSL_OP_NO_ANTI_REPLAY
2229	{ "SSL_OP_NO_ANTI_REPLAY",	SSL_OP_NO_ANTI_REPLAY },
2230#endif
2231#ifdef SSL_OP_ALLOW_NO_DHE_KEX
2232	{ "SSL_OP_ALLOW_NO_DHE_KEX",	SSL_OP_ALLOW_NO_DHE_KEX },
2233#endif
2234#ifdef SSL_OP_NO_ENCRYPT_THEN_MAC
2235	{ "SSL_OP_NO_ENCRYPT_THEN_MAC",	SSL_OP_NO_ENCRYPT_THEN_MAC },
2236#endif
2237#ifdef SSL_OP_ENABLE_MIDDLEBOX_COMPAT
2238	{ "SSL_OP_ENABLE_MIDDLEBOX_COMPAT",	SSL_OP_ENABLE_MIDDLEBOX_COMPAT },
2239#endif
2240#ifdef SSL_OP_PRIORITIZE_CHACHA
2241	{ "SSL_OP_PRIORITIZE_CHACHA",	SSL_OP_PRIORITIZE_CHACHA },
2242#endif
2243	{ NULL,		0		}
2244};
2245
2246/*
2247** READSSLOPTIONS  -- read SSL_OP_* values
2248**
2249**	Parameters:
2250**		opt -- name of option (can be NULL)
2251**		val -- string with SSL_OP_* values or hex value
2252**		delim -- end of string (e.g., '\0' or ';')
2253**		pssloptions -- return value (output)
2254**
2255**	Returns:
2256**		0 on success.
2257*/
2258
2259#define SSLOPERR_NAN	1
2260#define SSLOPERR_NOTFOUND	2
2261
2262static int readssloptions __P((char *, char *, unsigned long *, int ));
2263
2264static int
2265readssloptions(opt, val, pssloptions, delim)
2266	char *opt;
2267	char *val;
2268	unsigned long *pssloptions;
2269	int delim;
2270{
2271	char *p;
2272	int ret;
2273
2274	ret = 0;
2275	for (p = val; *p != '\0' && *p != delim; )
2276	{
2277		bool clearmode;
2278		char *q;
2279		unsigned long sslopt_val;
2280		struct ssl_options *sslopts;
2281
2282		while (*p == ' ')
2283			p++;
2284		if (*p == '\0')
2285			break;
2286		clearmode = false;
2287		if (*p == '-' || *p == '+')
2288			clearmode = *p++ == '-';
2289		q = p;
2290		while (*p != '\0' && !(SM_ISSPACE(*p)) && *p != ',')
2291			p++;
2292		if (*p != '\0')
2293			*p++ = '\0';
2294		sslopt_val = 0;
2295		if (isdigit(*q))
2296		{
2297			char *end;
2298
2299			sslopt_val = strtoul(q, &end, 0);
2300
2301			/* not a complete "syntax" check but good enough */
2302			if (end == q)
2303			{
2304				errno = 0;
2305				ret = SSLOPERR_NAN;
2306				if (opt != NULL)
2307					syserr("readcf: %s option value %s not a number",
2308						opt, q);
2309				sslopt_val = 0;
2310			}
2311		}
2312		else
2313		{
2314			for (sslopts = SSL_Option;
2315			     sslopts->sslopt_name != NULL; sslopts++)
2316			{
2317				if (sm_strcasecmp(q, sslopts->sslopt_name) == 0)
2318				{
2319					sslopt_val = sslopts->sslopt_bits;
2320					break;
2321				}
2322			}
2323			if (sslopts->sslopt_name == NULL)
2324			{
2325				errno = 0;
2326				ret = SSLOPERR_NOTFOUND;
2327				if (opt != NULL)
2328					syserr("readcf: %s option value %s unrecognized",
2329						opt, q);
2330			}
2331		}
2332		if (sslopt_val != 0)
2333		{
2334			if (clearmode)
2335				*pssloptions &= ~sslopt_val;
2336			else
2337				*pssloptions |= sslopt_val;
2338		}
2339	}
2340	return ret;
2341}
2342
2343/*
2344** GET_TLS_SE_OPTIONS -- get TLS session options (from ruleset)
2345**
2346**	Parameters:
2347**		e -- envelope
2348**		ssl -- TLS session context
2349**		tlsi_ctx -- TLS info context
2350**		srv -- server?
2351**
2352**	Returns:
2353**		0 on success.
2354*/
2355
2356int
2357get_tls_se_options(e, ssl, tlsi_ctx, srv)
2358	ENVELOPE *e;
2359	SSL *ssl;
2360	tlsi_ctx_T *tlsi_ctx;
2361	bool srv;
2362{
2363	bool saveQuickAbort, saveSuprErrs, ok;
2364	char *optionlist, *opt, *val;
2365	char *keyfile, *certfile;
2366	size_t len, i;
2367	int ret;
2368
2369#  define who (srv ? "server" : "client")
2370#  define NAME_C_S macvalue(macid(srv ? "{client_name}" : "{server_name}"), e)
2371#  define ADDR_C_S macvalue(macid(srv ? "{client_addr}" : "{server_addr}"), e)
2372#  define WHICH srv ? "srv" : "clt"
2373
2374	ret = 0;
2375	keyfile = certfile = opt = val = NULL;
2376	saveQuickAbort = QuickAbort;
2377	saveSuprErrs = SuprErrs;
2378	SuprErrs = true;
2379	QuickAbort = false;
2380
2381	optionlist = NULL;
2382	ok = rscheck(srv ? "tls_srv_features" : "tls_clt_features",
2383		     NAME_C_S, ADDR_C_S, e,
2384		     RSF_RMCOMM|RSF_ADDR|RSF_STRING,
2385		     5, NULL, NOQID, NULL, &optionlist) == EX_OK;
2386	if (!ok && LogLevel > 8)
2387	{
2388		sm_syslog(LOG_NOTICE, NOQID,
2389			  "rscheck(tls_%s_features)=failed, relay=%s [%s], errors=%d",
2390			  WHICH, NAME_C_S, ADDR_C_S,
2391			  Errors);
2392	}
2393	QuickAbort = saveQuickAbort;
2394	SuprErrs = saveSuprErrs;
2395	if (ok && LogLevel > 9)
2396	{
2397		sm_syslog(LOG_INFO, NOQID,
2398			  "tls_%s_features=%s, relay=%s [%s]",
2399			  WHICH, optionlist, NAME_C_S, ADDR_C_S);
2400	}
2401	if (!ok || optionlist == NULL || (len = strlen(optionlist)) < 2)
2402	{
2403		if (LogLevel > 9)
2404			sm_syslog(LOG_INFO, NOQID,
2405				  "tls_%s_features=empty, relay=%s [%s]",
2406				  WHICH, NAME_C_S, ADDR_C_S);
2407
2408		return ok ? 0 : 1;
2409	}
2410
2411	i = 0;
2412	if (optionlist[0] == '"' && optionlist[len - 1] == '"')
2413	{
2414		optionlist[0] = ' ';
2415		optionlist[--len] = '\0';
2416		if (len <= 2)
2417		{
2418			if (LogLevel > 9 && len > 1)
2419				sm_syslog(LOG_INFO, NOQID,
2420				  "tls_%s_features=too_short, relay=%s [%s]",
2421				  WHICH, NAME_C_S, ADDR_C_S);
2422
2423			/* this is not treated as error! */
2424			return 0;
2425		}
2426		i = 1;
2427	}
2428
2429#  define INVALIDSYNTAX	\
2430	do {	\
2431		if (LogLevel > 7)	\
2432			sm_syslog(LOG_INFO, NOQID,	\
2433				  "tls_%s_features=invalid_syntax, opt=%s, relay=%s [%s]",	\
2434				  WHICH, opt, NAME_C_S, ADDR_C_S);	\
2435		return -1;	\
2436	} while (0)
2437
2438#  define CHECKLEN	\
2439	do {	\
2440		if (i >= len)	\
2441			INVALIDSYNTAX;	\
2442	} while (0)
2443
2444#  define SKIPWS	\
2445	do {	\
2446		while (i < len && SM_ISSPACE(optionlist[i]))	\
2447			++i;	\
2448		CHECKLEN;	\
2449	} while (0)
2450
2451	/* parse and handle opt=val; */
2452	do {
2453		char sep;
2454
2455		SKIPWS;
2456		opt = optionlist + i;
2457		sep = '=';
2458		while (i < len && optionlist[i] != sep
2459			&& optionlist[i] != '\0' && !SM_ISSPACE(optionlist[i]))
2460			++i;
2461		CHECKLEN;
2462		while (i < len && SM_ISSPACE(optionlist[i]))
2463			optionlist[i++] = '\0';
2464		CHECKLEN;
2465		if (optionlist[i] != sep)
2466			INVALIDSYNTAX;
2467		optionlist[i++] = '\0';
2468
2469		SKIPWS;
2470		val = optionlist + i;
2471		sep = ';';
2472		while (i < len && optionlist[i] != sep && optionlist[i] != '\0')
2473			++i;
2474		if (optionlist[i] != '\0')
2475		{
2476			CHECKLEN;
2477			optionlist[i++] = '\0';
2478		}
2479
2480		if (LogLevel > 13)
2481			sm_syslog(LOG_DEBUG, NOQID,
2482				  "tls_%s_features=parsed, %s=%s, relay=%s [%s]",
2483				  WHICH, opt, val, NAME_C_S, ADDR_C_S);
2484
2485		if (sm_strcasecmp(opt, "options") == 0)
2486		{
2487			unsigned long ssloptions;
2488
2489			ssloptions = 0;
2490			ret = readssloptions(NULL, val, &ssloptions, ';');
2491			if (ret == 0)
2492				(void) SSL_set_options(ssl, (long) ssloptions);
2493			else if (LogLevel > 8)
2494			{
2495				sm_syslog(LOG_WARNING, NOQID,
2496					  "tls_%s_features=%s, error=%s, relay=%s [%s]",
2497					  WHICH, val,
2498					  (ret == SSLOPERR_NAN) ? "not a number" :
2499					  ((ret == SSLOPERR_NOTFOUND) ? "SSL_OP not found" :
2500					  "unknown"),
2501					  NAME_C_S, ADDR_C_S);
2502			}
2503		}
2504		else if (sm_strcasecmp(opt, "cipherlist") == 0)
2505		{
2506			if (SSL_set_cipher_list(ssl, val) <= 0)
2507			{
2508				ret = 1;
2509				if (LogLevel > 7)
2510				{
2511					sm_syslog(LOG_WARNING, NOQID,
2512						  "STARTTLS=%s, error: SSL_set_cipher_list(%s) failed",
2513						  who, val);
2514
2515					tlslogerr(LOG_WARNING, 9, who);
2516				}
2517			}
2518		}
2519		else if (sm_strcasecmp(opt, "flags") == 0)
2520		{
2521			char *p;
2522
2523			for (p = val; *p != '\0'; p++)
2524			{
2525				if (isascii(*p) && isalnum(*p))
2526					setbitn(bitidx(*p), tlsi_ctx->tlsi_flags);
2527			}
2528		}
2529		else if (sm_strcasecmp(opt, "keyfile") == 0)
2530			keyfile = val;
2531		else if (sm_strcasecmp(opt, "certfile") == 0)
2532			certfile = val;
2533		else
2534		{
2535			ret = 1;
2536			if (LogLevel > 7)
2537			{
2538				sm_syslog(LOG_INFO, NOQID,
2539					  "tls_%s_features=unknown_option, opt=%s, relay=%s [%s]",
2540					  WHICH, opt, NAME_C_S, ADDR_C_S);
2541			}
2542		}
2543
2544	} while (optionlist[i] != '\0' && i < len);
2545
2546	/* need cert and key before we can use the options */
2547	/* does not implement the "," hack for 2nd cert/key pair */
2548	if (keyfile != NULL && certfile != NULL)
2549	{
2550		load_certkey(ssl, srv, certfile, keyfile);
2551		keyfile = certfile = NULL;
2552	}
2553	else if (keyfile != NULL || certfile != NULL)
2554	{
2555		ret = 1;
2556		if (LogLevel > 7)
2557		{
2558			sm_syslog(LOG_INFO, NOQID,
2559				  "tls_%s_features=only_one_of_CertFile/KeyFile_specified, relay=%s [%s]",
2560				  WHICH, NAME_C_S, ADDR_C_S);
2561		}
2562	}
2563
2564	return ret;
2565#  undef who
2566#  undef NAME_C_S
2567#  undef ADDR_C_S
2568#  undef WHICH
2569}
2570#endif /* STARTTLS */
2571
2572/*
2573**  SETOPTION -- set global processing option
2574**
2575**	Parameters:
2576**		opt -- option name.
2577**		val -- option value (as a text string).
2578**		safe -- set if this came from a configuration file.
2579**			Some options (if set from the command line) will
2580**			reset the user id to avoid security problems.
2581**		sticky -- if set, don't let other setoptions override
2582**			this value.
2583**		e -- the main envelope.
2584**
2585**	Returns:
2586**		none.
2587**
2588**	Side Effects:
2589**		Sets options as implied by the arguments.
2590*/
2591
2592static BITMAP256	StickyOpt;		/* set if option is stuck */
2593
2594#if NAMED_BIND
2595
2596static struct resolverflags
2597{
2598	char	*rf_name;	/* name of the flag */
2599	long	rf_bits;	/* bits to set/clear */
2600} ResolverFlags[] =
2601{
2602	{ "debug",	RES_DEBUG	},
2603	{ "aaonly",	RES_AAONLY	},
2604	{ "usevc",	RES_USEVC	},
2605	{ "primary",	RES_PRIMARY	},
2606	{ "igntc",	RES_IGNTC	},
2607	{ "recurse",	RES_RECURSE	},
2608	{ "defnames",	RES_DEFNAMES	},
2609	{ "stayopen",	RES_STAYOPEN	},
2610	{ "dnsrch",	RES_DNSRCH	},
2611# ifdef RES_USE_INET6
2612	{ "use_inet6",	RES_USE_INET6	},
2613# endif
2614# ifdef RES_USE_EDNS0
2615	{ "use_edns0",	RES_USE_EDNS0	},
2616# endif
2617# ifdef RES_USE_DNSSEC
2618	{ "use_dnssec",	RES_USE_DNSSEC	},
2619# endif
2620# if RES_TRUSTAD
2621	{ "trustad",	RES_TRUSTAD	},
2622# endif
2623	{ "true",	0		},	/* avoid error on old syntax */
2624	{ "true",	0		},	/* avoid error on old syntax */
2625	{ NULL,		0		}
2626};
2627
2628#endif /* NAMED_BIND */
2629
2630#define OI_NONE		0	/* no special treatment */
2631#define OI_SAFE		0x0001	/* safe for random people to use */
2632#define OI_SUBOPT	0x0002	/* option has suboptions */
2633
2634static struct optioninfo
2635{
2636	char		*o_name;	/* long name of option */
2637	unsigned char	o_code;		/* short name of option */
2638	unsigned short	o_flags;	/* option flags */
2639} OptionTab[] =
2640{
2641#if defined(SUN_EXTENSIONS) && defined(REMOTE_MODE)
2642	{ "RemoteMode",			'>',		OI_NONE	},
2643#endif
2644	{ "SevenBitInput",		'7',		OI_SAFE	},
2645	{ "EightBitMode",		'8',		OI_SAFE	},
2646	{ "AliasFile",			'A',		OI_NONE	},
2647	{ "AliasWait",			'a',		OI_NONE	},
2648	{ "BlankSub",			'B',		OI_NONE	},
2649	{ "MinFreeBlocks",		'b',		OI_SAFE	},
2650	{ "CheckpointInterval",		'C',		OI_SAFE	},
2651	{ "HoldExpensive",		'c',		OI_NONE	},
2652	{ "DeliveryMode",		'd',		OI_SAFE	},
2653	{ "ErrorHeader",		'E',		OI_NONE	},
2654	{ "ErrorMode",			'e',		OI_SAFE	},
2655	{ "TempFileMode",		'F',		OI_NONE	},
2656	{ "SaveFromLine",		'f',		OI_NONE	},
2657	{ "MatchGECOS",			'G',		OI_NONE	},
2658
2659	/* no long name, just here to avoid problems in setoption */
2660	{ "",				'g',		OI_NONE	},
2661	{ "HelpFile",			'H',		OI_NONE	},
2662	{ "MaxHopCount",		'h',		OI_NONE	},
2663	{ "ResolverOptions",		'I',		OI_NONE	},
2664	{ "IgnoreDots",			'i',		OI_SAFE	},
2665	{ "ForwardPath",		'J',		OI_NONE	},
2666	{ "SendMimeErrors",		'j',		OI_SAFE	},
2667	{ "ConnectionCacheSize",	'k',		OI_NONE	},
2668	{ "ConnectionCacheTimeout",	'K',		OI_NONE	},
2669	{ "UseErrorsTo",		'l',		OI_NONE	},
2670	{ "LogLevel",			'L',		OI_SAFE	},
2671	{ "MeToo",			'm',		OI_SAFE	},
2672
2673	/* no long name, just here to avoid problems in setoption */
2674	{ "",				'M',		OI_NONE	},
2675	{ "CheckAliases",		'n',		OI_NONE	},
2676	{ "OldStyleHeaders",		'o',		OI_SAFE	},
2677	{ "DaemonPortOptions",		'O',		OI_NONE	},
2678	{ "PrivacyOptions",		'p',		OI_SAFE	},
2679	{ "PostmasterCopy",		'P',		OI_NONE	},
2680	{ "QueueFactor",		'q',		OI_NONE	},
2681	{ "QueueDirectory",		'Q',		OI_NONE	},
2682	{ "DontPruneRoutes",		'R',		OI_NONE	},
2683	{ "Timeout",			'r',		OI_SUBOPT },
2684	{ "StatusFile",			'S',		OI_NONE	},
2685	{ "SuperSafe",			's',		OI_SAFE	},
2686	{ "QueueTimeout",		'T',		OI_NONE	},
2687	{ "TimeZoneSpec",		't',		OI_NONE	},
2688	{ "UserDatabaseSpec",		'U',		OI_NONE	},
2689	{ "DefaultUser",		'u',		OI_NONE	},
2690	{ "FallbackMXhost",		'V',		OI_NONE	},
2691	{ "Verbose",			'v',		OI_SAFE	},
2692	{ "TryNullMXList",		'w',		OI_NONE	},
2693	{ "QueueLA",			'x',		OI_NONE	},
2694	{ "RefuseLA",			'X',		OI_NONE	},
2695	{ "RecipientFactor",		'y',		OI_NONE	},
2696	{ "ForkEachJob",		'Y',		OI_NONE	},
2697	{ "ClassFactor",		'z',		OI_NONE	},
2698	{ "RetryFactor",		'Z',		OI_NONE	},
2699#define O_QUEUESORTORD	0x81
2700	{ "QueueSortOrder",		O_QUEUESORTORD,	OI_SAFE	},
2701#define O_HOSTSFILE	0x82
2702	{ "HostsFile",			O_HOSTSFILE,	OI_NONE	},
2703#define O_MQA		0x83
2704	{ "MinQueueAge",		O_MQA,		OI_SAFE	},
2705#define O_DEFCHARSET	0x85
2706	{ "DefaultCharSet",		O_DEFCHARSET,	OI_SAFE	},
2707#define O_SSFILE	0x86
2708	{ "ServiceSwitchFile",		O_SSFILE,	OI_NONE	},
2709#define O_DIALDELAY	0x87
2710	{ "DialDelay",			O_DIALDELAY,	OI_SAFE	},
2711#define O_NORCPTACTION	0x88
2712	{ "NoRecipientAction",		O_NORCPTACTION,	OI_SAFE	},
2713#define O_SAFEFILEENV	0x89
2714	{ "SafeFileEnvironment",	O_SAFEFILEENV,	OI_NONE	},
2715#define O_MAXMSGSIZE	0x8a
2716	{ "MaxMessageSize",		O_MAXMSGSIZE,	OI_NONE	},
2717#define O_COLONOKINADDR	0x8b
2718	{ "ColonOkInAddr",		O_COLONOKINADDR, OI_SAFE },
2719#define O_MAXQUEUERUN	0x8c
2720	{ "MaxQueueRunSize",		O_MAXQUEUERUN,	OI_SAFE	},
2721#define O_MAXCHILDREN	0x8d
2722	{ "MaxDaemonChildren",		O_MAXCHILDREN,	OI_NONE	},
2723#define O_KEEPCNAMES	0x8e
2724	{ "DontExpandCnames",		O_KEEPCNAMES,	OI_NONE	},
2725#define O_MUSTQUOTE	0x8f
2726	{ "MustQuoteChars",		O_MUSTQUOTE,	OI_NONE	},
2727#define O_SMTPGREETING	0x90
2728	{ "SmtpGreetingMessage",	O_SMTPGREETING,	OI_NONE	},
2729#define O_UNIXFROM	0x91
2730	{ "UnixFromLine",		O_UNIXFROM,	OI_NONE	},
2731#define O_OPCHARS	0x92
2732	{ "OperatorChars",		O_OPCHARS,	OI_NONE	},
2733#define O_DONTINITGRPS	0x93
2734	{ "DontInitGroups",		O_DONTINITGRPS,	OI_NONE	},
2735#define O_SLFH		0x94
2736	{ "SingleLineFromHeader",	O_SLFH,		OI_SAFE	},
2737#define O_ABH		0x95
2738	{ "AllowBogusHELO",		O_ABH,		OI_SAFE	},
2739#define O_CONNTHROT	0x97
2740	{ "ConnectionRateThrottle",	O_CONNTHROT,	OI_NONE	},
2741#define O_UGW		0x99
2742	{ "UnsafeGroupWrites",		O_UGW,		OI_NONE	},
2743#define O_DBLBOUNCE	0x9a
2744	{ "DoubleBounceAddress",	O_DBLBOUNCE,	OI_NONE	},
2745#define O_HSDIR		0x9b
2746	{ "HostStatusDirectory",	O_HSDIR,	OI_NONE	},
2747#define O_SINGTHREAD	0x9c
2748	{ "SingleThreadDelivery",	O_SINGTHREAD,	OI_NONE	},
2749#define O_RUNASUSER	0x9d
2750	{ "RunAsUser",			O_RUNASUSER,	OI_NONE	},
2751#define O_DSN_RRT	0x9e
2752	{ "RrtImpliesDsn",		O_DSN_RRT,	OI_NONE	},
2753#define O_PIDFILE	0x9f
2754	{ "PidFile",			O_PIDFILE,	OI_NONE	},
2755#define O_DONTBLAMESENDMAIL	0xa0
2756	{ "DontBlameSendmail",		O_DONTBLAMESENDMAIL,	OI_NONE	},
2757#define O_DPI		0xa1
2758	{ "DontProbeInterfaces",	O_DPI,		OI_NONE	},
2759#define O_MAXRCPT	0xa2
2760	{ "MaxRecipientsPerMessage",	O_MAXRCPT,	OI_SAFE	},
2761#define O_DEADLETTER	0xa3
2762	{ "DeadLetterDrop",		O_DEADLETTER,	OI_NONE	},
2763#if _FFR_DONTLOCKFILESFORREAD_OPTION
2764# define O_DONTLOCK	0xa4
2765	{ "DontLockFilesForRead",	O_DONTLOCK,	OI_NONE	},
2766#endif
2767#define O_MAXALIASRCSN	0xa5
2768	{ "MaxAliasRecursion",		O_MAXALIASRCSN,	OI_NONE	},
2769#define O_CNCTONLYTO	0xa6
2770	{ "ConnectOnlyTo",		O_CNCTONLYTO,	OI_NONE	},
2771#define O_TRUSTUSER	0xa7
2772	{ "TrustedUser",		O_TRUSTUSER,	OI_NONE	},
2773#define O_MAXMIMEHDRLEN	0xa8
2774	{ "MaxMimeHeaderLength",	O_MAXMIMEHDRLEN,	OI_NONE	},
2775#define O_CONTROLSOCKET	0xa9
2776	{ "ControlSocketName",		O_CONTROLSOCKET,	OI_NONE	},
2777#define O_MAXHDRSLEN	0xaa
2778	{ "MaxHeadersLength",		O_MAXHDRSLEN,	OI_NONE	},
2779#if _FFR_MAX_FORWARD_ENTRIES
2780# define O_MAXFORWARD	0xab
2781	{ "MaxForwardEntries",		O_MAXFORWARD,	OI_NONE	},
2782#endif
2783#define O_PROCTITLEPREFIX	0xac
2784	{ "ProcessTitlePrefix",		O_PROCTITLEPREFIX,	OI_NONE	},
2785#define O_SASLINFO	0xad
2786#if _FFR_ALLOW_SASLINFO
2787	{ "DefaultAuthInfo",		O_SASLINFO,	OI_SAFE	},
2788#else
2789	{ "DefaultAuthInfo",		O_SASLINFO,	OI_NONE	},
2790#endif
2791#define O_SASLMECH	0xae
2792	{ "AuthMechanisms",		O_SASLMECH,	OI_NONE	},
2793#define O_CLIENTPORT	0xaf
2794	{ "ClientPortOptions",		O_CLIENTPORT,	OI_NONE	},
2795#define O_DF_BUFSIZE	0xb0
2796	{ "DataFileBufferSize",		O_DF_BUFSIZE,	OI_NONE	},
2797#define O_XF_BUFSIZE	0xb1
2798	{ "XscriptFileBufferSize",	O_XF_BUFSIZE,	OI_NONE	},
2799#define O_LDAPDEFAULTSPEC	0xb2
2800	{ "LDAPDefaultSpec",		O_LDAPDEFAULTSPEC,	OI_NONE	},
2801#define O_SRVCERTFILE	0xb4
2802	{ "ServerCertFile",		O_SRVCERTFILE,	OI_NONE	},
2803#define O_SRVKEYFILE	0xb5
2804	{ "ServerKeyFile",		O_SRVKEYFILE,	OI_NONE	},
2805#define O_CLTCERTFILE	0xb6
2806	{ "ClientCertFile",		O_CLTCERTFILE,	OI_NONE	},
2807#define O_CLTKEYFILE	0xb7
2808	{ "ClientKeyFile",		O_CLTKEYFILE,	OI_NONE	},
2809#define O_CACERTFILE	0xb8
2810	{ "CACertFile",			O_CACERTFILE,	OI_NONE	},
2811#define O_CACERTPATH	0xb9
2812	{ "CACertPath",			O_CACERTPATH,	OI_NONE	},
2813#define O_DHPARAMS	0xba
2814	{ "DHParameters",		O_DHPARAMS,	OI_NONE	},
2815#define O_INPUTMILTER	0xbb
2816	{ "InputMailFilters",		O_INPUTMILTER,	OI_NONE	},
2817#define O_MILTER	0xbc
2818	{ "Milter",			O_MILTER,	OI_SUBOPT	},
2819#define O_SASLOPTS	0xbd
2820	{ "AuthOptions",		O_SASLOPTS,	OI_NONE	},
2821#define O_QUEUE_FILE_MODE	0xbe
2822	{ "QueueFileMode",		O_QUEUE_FILE_MODE, OI_NONE	},
2823#define O_DIG_ALG	0xbf
2824	{ "CertFingerprintAlgorithm",		O_DIG_ALG,	OI_NONE	},
2825#define O_CIPHERLIST	0xc0
2826	{ "CipherList",			O_CIPHERLIST,	OI_NONE	},
2827#define O_RANDFILE	0xc1
2828	{ "RandFile",			O_RANDFILE,	OI_NONE	},
2829#define O_TLS_SRV_OPTS	0xc2
2830	{ "TLSSrvOptions",		O_TLS_SRV_OPTS,	OI_NONE	},
2831#define O_RCPTTHROT	0xc3
2832	{ "BadRcptThrottle",		O_RCPTTHROT,	OI_SAFE	},
2833#define O_DLVR_MIN	0xc4
2834	{ "DeliverByMin",		O_DLVR_MIN,	OI_NONE	},
2835#define O_MAXQUEUECHILDREN	0xc5
2836	{ "MaxQueueChildren",		O_MAXQUEUECHILDREN,	OI_NONE	},
2837#define O_MAXRUNNERSPERQUEUE	0xc6
2838	{ "MaxRunnersPerQueue",		O_MAXRUNNERSPERQUEUE,	OI_NONE },
2839#define O_DIRECTSUBMODIFIERS	0xc7
2840	{ "DirectSubmissionModifiers",	O_DIRECTSUBMODIFIERS,	OI_NONE },
2841#define O_NICEQUEUERUN	0xc8
2842	{ "NiceQueueRun",		O_NICEQUEUERUN,	OI_NONE	},
2843#define O_SHMKEY	0xc9
2844	{ "SharedMemoryKey",		O_SHMKEY,	OI_NONE	},
2845#define O_SASLBITS	0xca
2846	{ "AuthMaxBits",		O_SASLBITS,	OI_NONE	},
2847#define O_MBDB		0xcb
2848	{ "MailboxDatabase",		O_MBDB,		OI_NONE	},
2849#define O_MSQ		0xcc
2850	{ "UseMSP",	O_MSQ,		OI_NONE	},
2851#define O_DELAY_LA	0xcd
2852	{ "DelayLA",	O_DELAY_LA,	OI_NONE	},
2853#define O_FASTSPLIT	0xce
2854	{ "FastSplit",	O_FASTSPLIT,	OI_NONE	},
2855#define O_SOFTBOUNCE	0xcf
2856	{ "SoftBounce",	O_SOFTBOUNCE,	OI_NONE	},
2857#define O_SHMKEYFILE	0xd0
2858	{ "SharedMemoryKeyFile",	O_SHMKEYFILE,	OI_NONE	},
2859#define O_REJECTLOGINTERVAL	0xd1
2860	{ "RejectLogInterval",	O_REJECTLOGINTERVAL,	OI_NONE	},
2861#define O_REQUIRES_DIR_FSYNC	0xd2
2862	{ "RequiresDirfsync",	O_REQUIRES_DIR_FSYNC,	OI_NONE	},
2863#define O_CONNECTION_RATE_WINDOW_SIZE	0xd3
2864	{ "ConnectionRateWindowSize", O_CONNECTION_RATE_WINDOW_SIZE, OI_NONE },
2865#define O_CRLFILE	0xd4
2866	{ "CRLFile",		O_CRLFILE,	OI_NONE	},
2867#define O_FALLBACKSMARTHOST	0xd5
2868	{ "FallbackSmartHost",		O_FALLBACKSMARTHOST,	OI_NONE	},
2869#define O_SASLREALM	0xd6
2870	{ "AuthRealm",		O_SASLREALM,	OI_NONE	},
2871#define O_CRLPATH	0xd7
2872	{ "CRLPath",		O_CRLPATH,	OI_NONE	},
2873#define O_HELONAME 0xd8
2874	{ "HeloName",   O_HELONAME,     OI_NONE },
2875#if _FFR_MEMSTAT
2876# define O_REFUSELOWMEM	0xd9
2877	{ "RefuseLowMem",	O_REFUSELOWMEM,	OI_NONE },
2878# define O_QUEUELOWMEM	0xda
2879	{ "QueueLowMem",	O_QUEUELOWMEM,	OI_NONE },
2880# define O_MEMRESOURCE	0xdb
2881	{ "MemoryResource",	O_MEMRESOURCE,	OI_NONE },
2882#endif /* _FFR_MEMSTAT */
2883#define O_MAXNOOPCOMMANDS 0xdc
2884	{ "MaxNOOPCommands",	O_MAXNOOPCOMMANDS,	OI_NONE },
2885#if _FFR_MSG_ACCEPT
2886# define O_MSG_ACCEPT 0xdd
2887	{ "MessageAccept",	O_MSG_ACCEPT,	OI_NONE },
2888#endif
2889#if _FFR_QUEUE_RUN_PARANOIA
2890# define O_CHK_Q_RUNNERS 0xde
2891	{ "CheckQueueRunners",	O_CHK_Q_RUNNERS,	OI_NONE },
2892#endif
2893#if _FFR_EIGHT_BIT_ADDR_OK
2894# if !ALLOW_255
2895#  ERROR FFR_EIGHT_BIT_ADDR_OK requires _ALLOW_255
2896# endif
2897# define O_EIGHT_BIT_ADDR_OK	0xdf
2898	{ "EightBitAddrOK",	O_EIGHT_BIT_ADDR_OK,	OI_NONE },
2899#endif /* _FFR_EIGHT_BIT_ADDR_OK */
2900#if _FFR_ADDR_TYPE_MODES
2901# define O_ADDR_TYPE_MODES	0xe0
2902	{ "AddrTypeModes",	O_ADDR_TYPE_MODES,	OI_NONE },
2903#endif
2904#if _FFR_BADRCPT_SHUTDOWN
2905# define O_RCPTSHUTD	0xe1
2906	{ "BadRcptShutdown",		O_RCPTSHUTD,	OI_SAFE },
2907# define O_RCPTSHUTDG	0xe2
2908	{ "BadRcptShutdownGood",	O_RCPTSHUTDG,	OI_SAFE	},
2909#endif /* _FFR_BADRCPT_SHUTDOWN */
2910#define O_SRV_SSL_OPTIONS	0xe3
2911	{ "ServerSSLOptions",		O_SRV_SSL_OPTIONS,	OI_NONE	},
2912#define O_CLT_SSL_OPTIONS	0xe4
2913	{ "ClientSSLOptions",		O_CLT_SSL_OPTIONS,	OI_NONE	},
2914#define O_MAX_QUEUE_AGE	0xe5
2915	{ "MaxQueueAge",	O_MAX_QUEUE_AGE,	OI_NONE },
2916#if _FFR_RCPTTHROTDELAY
2917# define O_RCPTTHROTDELAY	0xe6
2918	{ "BadRcptThrottleDelay",	O_RCPTTHROTDELAY,	OI_SAFE	},
2919#endif
2920#if 0 && _FFR_QOS && defined(SOL_IP) && defined(IP_TOS)
2921# define O_INETQOS	0xe7	/* reserved for FFR_QOS */
2922	{ "InetQoS",			O_INETQOS,	OI_NONE },
2923#endif
2924#if STARTTLS && _FFR_FIPSMODE
2925# define O_FIPSMODE	0xe8
2926	{ "FIPSMode",		O_FIPSMODE,	OI_NONE	},
2927#endif
2928#if _FFR_REJECT_NUL_BYTE
2929# define O_REJECTNUL	0xe9
2930	{ "RejectNUL",	O_REJECTNUL,	OI_SAFE	},
2931#endif
2932#if _FFR_BOUNCE_QUEUE
2933# define O_BOUNCEQUEUE 0xea
2934	{ "BounceQueue",		O_BOUNCEQUEUE,	OI_NONE },
2935#endif
2936#if _FFR_ADD_BCC
2937# define O_ADDBCC 0xeb
2938	{ "AddBcc",			O_ADDBCC,	OI_NONE },
2939#endif
2940#define O_USECOMPRESSEDIPV6ADDRESSES 0xec
2941	{ "UseCompressedIPv6Addresses",	O_USECOMPRESSEDIPV6ADDRESSES, OI_NONE },
2942#if STARTTLS
2943# define O_SSLENGINE	0xed
2944	{ "SSLEngine",		O_SSLENGINE,	OI_NONE	},
2945# define O_SSLENGINEPATH	0xee
2946	{ "SSLEnginePath",	O_SSLENGINEPATH,	OI_NONE	},
2947# define O_TLSFB2CLEAR		0xef
2948	{ "TLSFallbacktoClear",	O_TLSFB2CLEAR,	OI_NONE	},
2949#endif
2950#if DNSSEC_TEST
2951# define O_NSPORTIP		0xf0
2952	{ "NameServer",	O_NSPORTIP,	OI_NONE	},
2953#endif
2954#if DANE
2955# define O_DANE		0xf1
2956	{ "DANE",	O_DANE,	OI_NONE	},
2957#endif
2958#if DNSSEC_TEST
2959# define O_NSSRCHLIST		0xf2
2960	{ "NameSearchList",	O_NSSRCHLIST,	OI_NONE	},
2961#endif
2962#if _FFR_BLANKENV_MACV
2963# define O_HACKS	0xf4
2964	{ "Hacks",		O_HACKS,	OI_NONE	},
2965#endif
2966#if _FFR_KEEPBCC
2967# define O_KEEPBCC	0xf3
2968	{ "KeepBcc",		O_KEEPBCC,	OI_NONE	},
2969#endif
2970
2971#if _FFR_CLIENTCA
2972#define O_CLTCACERTFILE	0xf5
2973	{ "ClientCACertFile",			O_CLTCACERTFILE, OI_NONE },
2974#define O_CLTCACERTPATH	0xf6
2975	{ "ClientCACertPath",			O_CLTCACERTPATH, OI_NONE },
2976#endif
2977#if _FFR_TLS_ALTNAMES
2978# define O_CHECKALTNAMES 0xf7
2979	{ "SetCertAltnames",			O_CHECKALTNAMES, OI_NONE },
2980#endif
2981
2982	{ NULL,				'\0',		OI_NONE	}
2983};
2984
2985# define CANONIFY(val)
2986
2987# define SET_OPT_DEFAULT(opt, val)	opt = val
2988
2989/* set a string option by expanding the value and assigning it */
2990/* WARNING this belongs ONLY into a case statement! */
2991#define SET_STRING_EXP(str)	\
2992		expand(val, exbuf, sizeof(exbuf), e);	\
2993		newval = sm_pstrdup_x(exbuf);		\
2994		if (str != NULL)	\
2995			sm_free(str);	\
2996		CANONIFY(newval);	\
2997		str = newval;		\
2998		break
2999
3000#define OPTNAME	o->o_name == NULL ? "<unknown>" : o->o_name
3001
3002void
3003setoption(opt, val, safe, sticky, e)
3004	int opt;
3005	char *val;
3006	bool safe;
3007	bool sticky;
3008	register ENVELOPE *e;
3009{
3010	register char *p;
3011	register struct optioninfo *o;
3012	char *subopt;
3013	int i;
3014	bool can_setuid = RunAsUid == 0;
3015	auto char *ep;
3016	char buf[50];
3017	extern bool Warn_Q_option;
3018#if _FFR_ALLOW_SASLINFO
3019	extern unsigned int SubmitMode;
3020#endif
3021#if STARTTLS || SM_CONF_SHM
3022	char *newval;
3023	char exbuf[MAXLINE];
3024#endif
3025#if STARTTLS
3026	unsigned long *pssloptions = NULL;
3027#endif
3028
3029	errno = 0;
3030	if (opt == ' ')
3031	{
3032		/* full word options */
3033		struct optioninfo *sel;
3034
3035		p = strchr(val, '=');
3036		if (p == NULL)
3037			p = &val[strlen(val)];
3038		while (*--p == ' ')
3039			continue;
3040		while (*++p == ' ')
3041			*p = '\0';
3042		if (p == val)
3043		{
3044			syserr("readcf: null option name");
3045			return;
3046		}
3047		if (*p == '=')
3048			*p++ = '\0';
3049		while (*p == ' ')
3050			p++;
3051		subopt = strchr(val, '.');
3052		if (subopt != NULL)
3053			*subopt++ = '\0';
3054		sel = NULL;
3055		for (o = OptionTab; o->o_name != NULL; o++)
3056		{
3057			if (sm_strncasecmp(o->o_name, val, strlen(val)) != 0)
3058				continue;
3059			if (strlen(o->o_name) == strlen(val))
3060			{
3061				/* completely specified -- this must be it */
3062				sel = NULL;
3063				break;
3064			}
3065			if (sel != NULL)
3066				break;
3067			sel = o;
3068		}
3069		if (sel != NULL && o->o_name == NULL)
3070			o = sel;
3071		else if (o->o_name == NULL)
3072		{
3073			syserr("readcf: unknown option name %s", val);
3074			return;
3075		}
3076		else if (sel != NULL)
3077		{
3078			syserr("readcf: ambiguous option name %s (matches %s and %s)",
3079				val, sel->o_name, o->o_name);
3080			return;
3081		}
3082		if (strlen(val) != strlen(o->o_name))
3083		{
3084			int oldVerbose = Verbose;
3085
3086			Verbose = 1;
3087			message("Option %s used as abbreviation for %s",
3088				val, o->o_name);
3089			Verbose = oldVerbose;
3090		}
3091		opt = o->o_code;
3092		val = p;
3093	}
3094	else
3095	{
3096		for (o = OptionTab; o->o_name != NULL; o++)
3097		{
3098			if (o->o_code == opt)
3099				break;
3100		}
3101		if (o->o_name == NULL)
3102		{
3103			syserr("readcf: unknown option name 0x%x", opt & 0xff);
3104			return;
3105		}
3106		subopt = NULL;
3107	}
3108
3109	if (subopt != NULL && !bitset(OI_SUBOPT, o->o_flags))
3110	{
3111		if (tTd(37, 1))
3112			sm_dprintf("setoption: %s does not support suboptions, ignoring .%s\n",
3113				   OPTNAME, subopt);
3114		subopt = NULL;
3115	}
3116
3117	if (tTd(37, 1))
3118	{
3119		sm_dprintf(isascii(opt) && isprint(opt) ?
3120			   "setoption %s (%c)%s%s=" :
3121			   "setoption %s (0x%x)%s%s=",
3122			   OPTNAME, opt, subopt == NULL ? "" : ".",
3123			   subopt == NULL ? "" : subopt);
3124		xputs(sm_debug_file(), val);
3125	}
3126
3127	/*
3128	**  See if this option is preset for us.
3129	*/
3130
3131	if (!sticky && bitnset(opt, StickyOpt))
3132	{
3133		if (tTd(37, 1))
3134			sm_dprintf(" (ignored)\n");
3135		return;
3136	}
3137
3138	/*
3139	**  Check to see if this option can be specified by this user.
3140	*/
3141
3142	if (!safe && RealUid == 0)
3143		safe = true;
3144	if (!safe && !bitset(OI_SAFE, o->o_flags))
3145	{
3146		if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
3147		{
3148			int dp;
3149
3150			if (tTd(37, 1))
3151				sm_dprintf(" (unsafe)");
3152			dp = drop_privileges(true);
3153			setstat(dp);
3154		}
3155	}
3156	if (tTd(37, 1))
3157		sm_dprintf("\n");
3158
3159	switch (opt & 0xff)
3160	{
3161	  case '7':		/* force seven-bit input */
3162		SevenBitInput = atobool(val);
3163		break;
3164
3165	  case '8':		/* handling of 8-bit input */
3166#if MIME8TO7
3167		switch (*val)
3168		{
3169		  case 'p':		/* pass 8 bit, convert MIME */
3170			MimeMode = MM_CVTMIME|MM_PASS8BIT;
3171			break;
3172
3173		  case 'm':		/* convert 8-bit, convert MIME */
3174			MimeMode = MM_CVTMIME|MM_MIME8BIT;
3175			break;
3176
3177		  case 's':		/* strict adherence */
3178			MimeMode = MM_CVTMIME;
3179			break;
3180
3181# if 0
3182		  case 'r':		/* reject 8-bit, don't convert MIME */
3183			MimeMode = 0;
3184			break;
3185
3186		  case 'j':		/* "just send 8" */
3187			MimeMode = MM_PASS8BIT;
3188			break;
3189
3190		  case 'a':		/* encode 8 bit if available */
3191			MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
3192			break;
3193
3194		  case 'c':		/* convert 8 bit to MIME, never 7 bit */
3195			MimeMode = MM_MIME8BIT;
3196			break;
3197# endif /* 0 */
3198
3199		  default:
3200			syserr("Unknown 8-bit mode %c", *val);
3201			finis(false, true, EX_USAGE);
3202		}
3203#else /* MIME8TO7 */
3204		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3205				     "Warning: Option: %s requires MIME8TO7 support\n",
3206				     OPTNAME);
3207#endif /* MIME8TO7 */
3208		break;
3209
3210	  case 'A':		/* set default alias file */
3211		if (val[0] == '\0')
3212		{
3213			char *al;
3214
3215			SET_OPT_DEFAULT(al, "aliases");
3216			setalias(al);
3217		}
3218		else
3219			setalias(val);
3220		break;
3221
3222	  case 'a':		/* look N minutes for "@:@" in alias file */
3223		if (val[0] == '\0')
3224			SafeAlias = 5 MINUTES;
3225		else
3226			SafeAlias = convtime(val, 'm');
3227		break;
3228
3229	  case 'B':		/* substitution for blank character */
3230		SpaceSub = val[0];
3231		if (SpaceSub == '\0')
3232			SpaceSub = ' ';
3233		break;
3234
3235	  case 'b':		/* min blocks free on queue fs/max msg size */
3236		p = strchr(val, '/');
3237		if (p != NULL)
3238		{
3239			*p++ = '\0';
3240			MaxMessageSize = atol(p);
3241		}
3242		MinBlocksFree = atol(val);
3243		break;
3244
3245	  case 'c':		/* don't connect to "expensive" mailers */
3246		NoConnect = atobool(val);
3247		break;
3248
3249	  case 'C':		/* checkpoint every N addresses */
3250		if (safe || CheckpointInterval > atoi(val))
3251			CheckpointInterval = atoi(val);
3252		break;
3253
3254	  case 'd':		/* delivery mode */
3255		switch (*val)
3256		{
3257		  case '\0':
3258			set_delivery_mode(SM_DELIVER, e);
3259			break;
3260
3261		  case SM_QUEUE:	/* queue only */
3262		  case SM_DEFER:	/* queue only and defer map lookups */
3263		  case SM_DELIVER:	/* do everything */
3264		  case SM_FORK:		/* fork after verification */
3265#if _FFR_DM_ONE
3266		/* deliver first TA in background, then queue */
3267		  case SM_DM_ONE:
3268#endif
3269			set_delivery_mode(*val, e);
3270			break;
3271
3272#if _FFR_PROXY
3273		  case SM_PROXY_REQ:
3274			set_delivery_mode(*val, e);
3275			break;
3276#endif /* _FFR_PROXY */
3277
3278		  default:
3279			syserr("Unknown delivery mode %c", *val);
3280			finis(false, true, EX_USAGE);
3281		}
3282		break;
3283
3284	  case 'E':		/* error message header/header file */
3285		if (*val != '\0')
3286			ErrMsgFile = newstr(val);
3287		break;
3288
3289	  case 'e':		/* set error processing mode */
3290		switch (*val)
3291		{
3292		  case EM_QUIET:	/* be silent about it */
3293		  case EM_MAIL:		/* mail back */
3294		  case EM_BERKNET:	/* do berknet error processing */
3295		  case EM_WRITE:	/* write back (or mail) */
3296		  case EM_PRINT:	/* print errors normally (default) */
3297			e->e_errormode = *val;
3298			break;
3299		}
3300		break;
3301
3302	  case 'F':		/* file mode */
3303		FileMode = atooct(val) & 0777;
3304		break;
3305
3306	  case 'f':		/* save Unix-style From lines on front */
3307		SaveFrom = atobool(val);
3308		break;
3309
3310	  case 'G':		/* match recipients against GECOS field */
3311		MatchGecos = atobool(val);
3312		break;
3313
3314	  case 'g':		/* default gid */
3315  g_opt:
3316		if (isascii(*val) && isdigit(*val))
3317			DefGid = atoi(val);
3318		else
3319		{
3320			register struct group *gr;
3321
3322			DefGid = -1;
3323			gr = getgrnam(val);
3324			if (gr == NULL)
3325				syserr("readcf: option %c: unknown group %s",
3326					opt, val);
3327			else
3328				DefGid = gr->gr_gid;
3329		}
3330		break;
3331
3332	  case 'H':		/* help file */
3333		if (val[0] == '\0')
3334		{
3335			SET_OPT_DEFAULT(HelpFile, "helpfile");
3336		}
3337		else
3338		{
3339			CANONIFY(val);
3340			HelpFile = newstr(val);
3341		}
3342		break;
3343
3344	  case 'h':		/* maximum hop count */
3345		MaxHopCount = atoi(val);
3346		break;
3347
3348	  case 'I':		/* use internet domain name server */
3349#if NAMED_BIND
3350		for (p = val; *p != 0; )
3351		{
3352			bool clearmode;
3353			char *q;
3354			struct resolverflags *rfp;
3355
3356			while (*p == ' ')
3357				p++;
3358			if (*p == '\0')
3359				break;
3360			clearmode = false;
3361			if (*p == '-')
3362				clearmode = true;
3363			else if (*p != '+')
3364				p--;
3365			p++;
3366			q = p;
3367			while (*p != '\0' && !(SM_ISSPACE(*p)))
3368				p++;
3369			if (*p != '\0')
3370				*p++ = '\0';
3371			if (sm_strcasecmp(q, "HasWildcardMX") == 0)
3372			{
3373				HasWildcardMX = !clearmode;
3374				continue;
3375			}
3376			if (sm_strcasecmp(q, "WorkAroundBrokenAAAA") == 0)
3377			{
3378				WorkAroundBrokenAAAA = !clearmode;
3379				continue;
3380			}
3381			for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
3382			{
3383				if (sm_strcasecmp(q, rfp->rf_name) == 0)
3384					break;
3385			}
3386			if (rfp->rf_name == NULL)
3387				syserr("readcf: I option value %s unrecognized", q);
3388			else if (clearmode)
3389				_res.options &= ~rfp->rf_bits;
3390			else
3391				_res.options |= rfp->rf_bits;
3392		}
3393		if (tTd(8, 2))
3394			sm_dprintf("_res.options = %x, HasWildcardMX = %d\n",
3395				   (unsigned int) _res.options, HasWildcardMX);
3396#else /* NAMED_BIND */
3397		usrerr("name server (I option) specified but BIND not compiled in");
3398#endif /* NAMED_BIND */
3399		break;
3400
3401	  case 'i':		/* ignore dot lines in message */
3402		IgnrDot = atobool(val);
3403		break;
3404
3405	  case 'j':		/* send errors in MIME (RFC 1341) format */
3406		SendMIMEErrors = atobool(val);
3407		break;
3408
3409	  case 'J':		/* .forward search path */
3410		CANONIFY(val);
3411		ForwardPath = newstr(val);
3412		break;
3413
3414	  case 'k':		/* connection cache size */
3415		MaxMciCache = atoi(val);
3416		if (MaxMciCache < 0)
3417			MaxMciCache = 0;
3418		break;
3419
3420	  case 'K':		/* connection cache timeout */
3421		MciCacheTimeout = convtime(val, 'm');
3422		break;
3423
3424	  case 'l':		/* use Errors-To: header */
3425		UseErrorsTo = atobool(val);
3426		break;
3427
3428	  case 'L':		/* log level */
3429		if (safe || LogLevel < atoi(val))
3430			LogLevel = atoi(val);
3431		break;
3432
3433	  case 'M':		/* define macro */
3434		sticky = false;
3435		i = macid_parse(val, &ep);
3436		if (i == 0)
3437			break;
3438		p = newstr(ep);
3439		if (!safe)
3440			cleanstrcpy(p, p, strlen(p) + 1);
3441		macdefine(&CurEnv->e_macro, A_TEMP, i, p);
3442		break;
3443
3444	  case 'm':		/* send to me too */
3445		MeToo = atobool(val);
3446		break;
3447
3448	  case 'n':		/* validate RHS in newaliases */
3449		CheckAliases = atobool(val);
3450		break;
3451
3452	    /* 'N' available -- was "net name" */
3453
3454	  case 'O':		/* daemon options */
3455		if (!setdaemonoptions(val))
3456			syserr("too many daemons defined (%d max)", MAXDAEMONS);
3457		break;
3458
3459	  case 'o':		/* assume old style headers */
3460		if (atobool(val))
3461			CurEnv->e_flags |= EF_OLDSTYLE;
3462		else
3463			CurEnv->e_flags &= ~EF_OLDSTYLE;
3464		break;
3465
3466	  case 'p':		/* select privacy level */
3467		p = val;
3468		for (;;)
3469		{
3470			register struct prival *pv;
3471			extern struct prival PrivacyValues[];
3472
3473			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
3474				p++;
3475			if (*p == '\0')
3476				break;
3477			val = p;
3478			while (isascii(*p) && isalnum(*p))
3479				p++;
3480			if (*p != '\0')
3481				*p++ = '\0';
3482
3483			for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
3484			{
3485				if (sm_strcasecmp(val, pv->pv_name) == 0)
3486					break;
3487			}
3488			if (pv->pv_name == NULL)
3489				syserr("readcf: Op line: %s unrecognized", val);
3490			else
3491				PrivacyFlags |= pv->pv_flag;
3492		}
3493		sticky = false;
3494		break;
3495
3496	  case 'P':		/* postmaster copy address for returned mail */
3497		PostMasterCopy = newstr(val);
3498		break;
3499
3500	  case 'q':		/* slope of queue only function */
3501		QueueFactor = atoi(val);
3502		break;
3503
3504	  case 'Q':		/* queue directory */
3505		if (val[0] == '\0')
3506		{
3507			QueueDir = "mqueue";
3508		}
3509		else
3510		{
3511			QueueDir = newstr(val);
3512		}
3513		if (RealUid != 0 && !safe)
3514			Warn_Q_option = true;
3515		break;
3516
3517	  case 'R':		/* don't prune routes */
3518		DontPruneRoutes = atobool(val);
3519		break;
3520
3521	  case 'r':		/* read timeout */
3522		if (subopt == NULL)
3523			inittimeouts(val, sticky);
3524		else
3525			settimeout(subopt, val, sticky);
3526		break;
3527
3528	  case 'S':		/* status file */
3529		if (val[0] == '\0')
3530		{
3531			SET_OPT_DEFAULT(StatFile, "statistics");
3532		}
3533		else
3534		{
3535			CANONIFY(val);
3536			StatFile = newstr(val);
3537		}
3538		break;
3539
3540	  case 's':		/* be super safe, even if expensive */
3541		if (tolower(*val) == 'i')
3542			SuperSafe = SAFE_INTERACTIVE;
3543		else if (tolower(*val) == 'p')
3544#if MILTER
3545			SuperSafe = SAFE_REALLY_POSTMILTER;
3546#else /* MILTER */
3547			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3548				"Warning: SuperSafe=PostMilter requires Milter support (-DMILTER)\n");
3549#endif /* MILTER */
3550		else
3551			SuperSafe = atobool(val) ? SAFE_REALLY : SAFE_NO;
3552		break;
3553
3554	  case 'T':		/* queue timeout */
3555		p = strchr(val, '/');
3556		if (p != NULL)
3557		{
3558			*p++ = '\0';
3559			settimeout("queuewarn", p, sticky);
3560		}
3561		settimeout("queuereturn", val, sticky);
3562		break;
3563
3564	  case 't':		/* time zone name */
3565		TimeZoneSpec = newstr(val);
3566		break;
3567
3568	  case 'U':		/* location of user database */
3569		UdbSpec = newstr(val);
3570		break;
3571
3572	  case 'u':		/* set default uid */
3573		for (p = val; *p != '\0'; p++)
3574		{
3575# if _FFR_DOTTED_USERNAMES
3576			if (*p == '/' || *p == ':')
3577# else
3578			if (*p == '.' || *p == '/' || *p == ':')
3579# endif
3580			{
3581				*p++ = '\0';
3582				break;
3583			}
3584		}
3585		if (isascii(*val) && isdigit(*val))
3586		{
3587			DefUid = atoi(val);
3588			setdefuser();
3589		}
3590		else
3591		{
3592			register struct passwd *pw;
3593
3594			DefUid = -1;
3595			pw = sm_getpwnam(val);
3596			if (pw == NULL)
3597			{
3598				syserr("readcf: option u: unknown user %s", val);
3599				break;
3600			}
3601			else
3602			{
3603				DefUid = pw->pw_uid;
3604				DefGid = pw->pw_gid;
3605				DefUser = newstr(pw->pw_name);
3606			}
3607		}
3608
3609# ifdef UID_MAX
3610		if (DefUid > UID_MAX)
3611		{
3612			syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
3613				(long)DefUid, (long)UID_MAX);
3614			break;
3615		}
3616# endif /* UID_MAX */
3617
3618		/* handle the group if it is there */
3619		if (*p == '\0')
3620			break;
3621		val = p;
3622		goto g_opt;
3623
3624	  case 'V':		/* fallback MX host */
3625		if (val[0] != '\0')
3626			FallbackMX = newstr(val);
3627		break;
3628
3629	  case 'v':		/* run in verbose mode */
3630		Verbose = atobool(val) ? 1 : 0;
3631		break;
3632
3633	  case 'w':		/* if we are best MX, try host directly */
3634		TryNullMXList = atobool(val);
3635		break;
3636
3637	    /* 'W' available -- was wizard password */
3638
3639	  case 'x':		/* load avg at which to auto-queue msgs */
3640		QueueLA = atoi(val);
3641		break;
3642
3643	  case 'X':	/* load avg at which to auto-reject connections */
3644		RefuseLA = atoi(val);
3645		break;
3646
3647	  case O_DELAY_LA:	/* load avg at which to delay connections */
3648		DelayLA = atoi(val);
3649		break;
3650
3651	  case 'y':		/* work recipient factor */
3652		WkRecipFact = atoi(val);
3653		break;
3654
3655	  case 'Y':		/* fork jobs during queue runs */
3656		ForkQueueRuns = atobool(val);
3657		break;
3658
3659	  case 'z':		/* work message class factor */
3660		WkClassFact = atoi(val);
3661		break;
3662
3663	  case 'Z':		/* work time factor */
3664		WkTimeFact = atoi(val);
3665		break;
3666
3667
3668#if _FFR_QUEUE_GROUP_SORTORDER
3669	/* coordinate this with makequeue() */
3670#endif
3671	  case O_QUEUESORTORD:	/* queue sorting order */
3672		switch (*val)
3673		{
3674		  case 'f':	/* File Name */
3675		  case 'F':
3676			QueueSortOrder = QSO_BYFILENAME;
3677			break;
3678
3679		  case 'h':	/* Host first */
3680		  case 'H':
3681			QueueSortOrder = QSO_BYHOST;
3682			break;
3683
3684		  case 'm':	/* Modification time */
3685		  case 'M':
3686			QueueSortOrder = QSO_BYMODTIME;
3687			break;
3688
3689		  case 'p':	/* Priority order */
3690		  case 'P':
3691			QueueSortOrder = QSO_BYPRIORITY;
3692			break;
3693
3694		  case 't':	/* Submission time */
3695		  case 'T':
3696			QueueSortOrder = QSO_BYTIME;
3697			break;
3698
3699		  case 'r':	/* Random */
3700		  case 'R':
3701			QueueSortOrder = QSO_RANDOM;
3702			break;
3703
3704#if _FFR_RHS
3705		  case 's':	/* Shuffled host name */
3706		  case 'S':
3707			QueueSortOrder = QSO_BYSHUFFLE;
3708			break;
3709#endif /* _FFR_RHS */
3710
3711		  case 'n':	/* none */
3712		  case 'N':
3713			QueueSortOrder = QSO_NONE;
3714			break;
3715
3716		  default:
3717			syserr("Invalid queue sort order \"%s\"", val);
3718		}
3719		break;
3720
3721	  case O_HOSTSFILE:	/* pathname of /etc/hosts file */
3722		CANONIFY(val);
3723		HostsFile = newstr(val);
3724		break;
3725
3726	  case O_MQA:		/* minimum queue age between deliveries */
3727		MinQueueAge = convtime(val, 'm');
3728		break;
3729
3730	  case O_MAX_QUEUE_AGE:
3731		MaxQueueAge = convtime(val, 'm');
3732		break;
3733
3734	  case O_DEFCHARSET:	/* default character set for mimefying */
3735		DefaultCharSet = newstr(denlstring(val, true, true));
3736		break;
3737
3738	  case O_SSFILE:	/* service switch file */
3739		CANONIFY(val);
3740		ServiceSwitchFile = newstr(val);
3741		break;
3742
3743	  case O_DIALDELAY:	/* delay for dial-on-demand operation */
3744		DialDelay = convtime(val, 's');
3745		break;
3746
3747	  case O_NORCPTACTION:	/* what to do if no recipient */
3748		if (sm_strcasecmp(val, "none") == 0)
3749			NoRecipientAction = NRA_NO_ACTION;
3750		else if (sm_strcasecmp(val, "add-to") == 0)
3751			NoRecipientAction = NRA_ADD_TO;
3752		else if (sm_strcasecmp(val, "add-apparently-to") == 0)
3753			NoRecipientAction = NRA_ADD_APPARENTLY_TO;
3754		else if (sm_strcasecmp(val, "add-bcc") == 0)
3755			NoRecipientAction = NRA_ADD_BCC;
3756		else if (sm_strcasecmp(val, "add-to-undisclosed") == 0)
3757			NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
3758		else
3759			syserr("Invalid NoRecipientAction: %s", val);
3760		break;
3761
3762	  case O_SAFEFILEENV:	/* chroot() environ for writing to files */
3763		if (*val == '\0')
3764			break;
3765
3766		/* strip trailing slashes */
3767		p = val + strlen(val) - 1;
3768		while (p >= val && *p == '/')
3769			*p-- = '\0';
3770
3771		if (*val == '\0')
3772			break;
3773
3774		SafeFileEnv = newstr(val);
3775		break;
3776
3777	  case O_MAXMSGSIZE:	/* maximum message size */
3778		MaxMessageSize = atol(val);
3779		break;
3780
3781	  case O_COLONOKINADDR:	/* old style handling of colon addresses */
3782		ColonOkInAddr = atobool(val);
3783		break;
3784
3785	  case O_MAXQUEUERUN:	/* max # of jobs in a single queue run */
3786		MaxQueueRun = atoi(val);
3787		break;
3788
3789	  case O_MAXCHILDREN:	/* max # of children of daemon */
3790		MaxChildren = atoi(val);
3791		break;
3792
3793	  case O_MAXQUEUECHILDREN: /* max # of children of daemon */
3794		MaxQueueChildren = atoi(val);
3795		break;
3796
3797	  case O_MAXRUNNERSPERQUEUE: /* max # runners in a queue group */
3798		MaxRunnersPerQueue = atoi(val);
3799		break;
3800
3801	  case O_NICEQUEUERUN:		/* nice queue runs */
3802#if !HASNICE
3803		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3804				     "Warning: NiceQueueRun set on system that doesn't support nice()\n");
3805#endif
3806
3807		/* XXX do we want to check the range? > 0 ? */
3808		NiceQueueRun = atoi(val);
3809		break;
3810
3811	  case O_SHMKEY:		/* shared memory key */
3812#if SM_CONF_SHM
3813		ShmKey = atol(val);
3814#else /* SM_CONF_SHM */
3815		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3816				     "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
3817				     OPTNAME);
3818#endif /* SM_CONF_SHM */
3819		break;
3820
3821	  case O_SHMKEYFILE:		/* shared memory key file */
3822#if SM_CONF_SHM
3823		SET_STRING_EXP(ShmKeyFile);
3824#else /* SM_CONF_SHM */
3825		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3826				     "Warning: Option: %s requires shared memory support (-DSM_CONF_SHM)\n",
3827				     OPTNAME);
3828		break;
3829#endif /* SM_CONF_SHM */
3830
3831#if _FFR_MAX_FORWARD_ENTRIES
3832	  case O_MAXFORWARD:	/* max # of forward entries */
3833		MaxForwardEntries = atoi(val);
3834		break;
3835#endif
3836
3837	  case O_KEEPCNAMES:	/* don't expand CNAME records */
3838		DontExpandCnames = atobool(val);
3839		break;
3840
3841	  case O_MUSTQUOTE:	/* must quote these characters in phrases */
3842		(void) sm_strlcpy(buf, "@,;:\\()[]", sizeof(buf));
3843		if (strlen(val) < sizeof(buf) - 10)
3844			(void) sm_strlcat(buf, val, sizeof(buf));
3845		else
3846			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3847					     "Warning: MustQuoteChars too long, ignored.\n");
3848		MustQuoteChars = newstr(buf);
3849		break;
3850
3851	  case O_SMTPGREETING:	/* SMTP greeting message (old $e macro) */
3852		SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
3853		break;
3854
3855	  case O_UNIXFROM:	/* UNIX From_ line (old $l macro) */
3856		UnixFromLine = newstr(munchstring(val, NULL, '\0'));
3857		break;
3858
3859	  case O_OPCHARS:	/* operator characters (old $o macro) */
3860		if (OperatorChars != NULL)
3861			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3862					     "Warning: OperatorChars is being redefined.\n         It should only be set before ruleset definitions.\n");
3863		OperatorChars = newstr(munchstring(val, NULL, '\0'));
3864		break;
3865
3866	  case O_DONTINITGRPS:	/* don't call initgroups(3) */
3867		DontInitGroups = atobool(val);
3868		break;
3869
3870	  case O_SLFH:		/* make sure from fits on one line */
3871		SingleLineFromHeader = atobool(val);
3872		break;
3873
3874	  case O_ABH:		/* allow HELO commands with syntax errors */
3875		AllowBogusHELO = atobool(val);
3876		break;
3877
3878	  case O_CONNTHROT:	/* connection rate throttle */
3879		ConnRateThrottle = atoi(val);
3880		break;
3881
3882	  case O_UGW:		/* group writable files are unsafe */
3883		if (!atobool(val))
3884		{
3885			setbitn(DBS_GROUPWRITABLEFORWARDFILESAFE,
3886				DontBlameSendmail);
3887			setbitn(DBS_GROUPWRITABLEINCLUDEFILESAFE,
3888				DontBlameSendmail);
3889		}
3890		break;
3891
3892	  case O_DBLBOUNCE:	/* address to which to send double bounces */
3893		DoubleBounceAddr = newstr(val);
3894		break;
3895
3896	  case O_HSDIR:		/* persistent host status directory */
3897		if (val[0] != '\0')
3898		{
3899			CANONIFY(val);
3900			HostStatDir = newstr(val);
3901		}
3902		break;
3903
3904	  case O_SINGTHREAD:	/* single thread deliveries (requires hsdir) */
3905		SingleThreadDelivery = atobool(val);
3906		break;
3907
3908	  case O_RUNASUSER:	/* run bulk of code as this user */
3909		for (p = val; *p != '\0'; p++)
3910		{
3911# if _FFR_DOTTED_USERNAMES
3912			if (*p == '/' || *p == ':')
3913# else
3914			if (*p == '.' || *p == '/' || *p == ':')
3915# endif
3916			{
3917				*p++ = '\0';
3918				break;
3919			}
3920		}
3921		if (isascii(*val) && isdigit(*val))
3922		{
3923			if (can_setuid)
3924				RunAsUid = atoi(val);
3925		}
3926		else
3927		{
3928			register struct passwd *pw;
3929
3930			pw = sm_getpwnam(val);
3931			if (pw == NULL)
3932			{
3933				syserr("readcf: option RunAsUser: unknown user %s", val);
3934				break;
3935			}
3936			else if (can_setuid)
3937			{
3938				if (*p == '\0')
3939					RunAsUserName = newstr(val);
3940				RunAsUid = pw->pw_uid;
3941				RunAsGid = pw->pw_gid;
3942			}
3943			else if (EffGid == pw->pw_gid)
3944				RunAsGid = pw->pw_gid;
3945			else if (UseMSP && *p == '\0')
3946				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
3947						     "WARNING: RunAsUser for MSP ignored, check group ids (egid=%ld, want=%ld)\n",
3948						     (long) EffGid,
3949						     (long) pw->pw_gid);
3950		}
3951# ifdef UID_MAX
3952		if (RunAsUid > UID_MAX)
3953		{
3954			syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
3955				(long) RunAsUid, (long) UID_MAX);
3956			break;
3957		}
3958# endif /* UID_MAX */
3959		if (*p != '\0')
3960		{
3961			if (isascii(*p) && isdigit(*p))
3962			{
3963				gid_t runasgid;
3964
3965				runasgid = (gid_t) atoi(p);
3966				if (can_setuid || EffGid == runasgid)
3967					RunAsGid = runasgid;
3968				else if (UseMSP)
3969					(void) sm_io_fprintf(smioout,
3970							     SM_TIME_DEFAULT,
3971							     "WARNING: RunAsUser for MSP ignored, check group ids (egid=%ld, want=%ld)\n",
3972							     (long) EffGid,
3973							     (long) runasgid);
3974			}
3975			else
3976			{
3977				register struct group *gr;
3978
3979				gr = getgrnam(p);
3980				if (gr == NULL)
3981					syserr("readcf: option RunAsUser: unknown group %s",
3982						p);
3983				else if (can_setuid || EffGid == gr->gr_gid)
3984					RunAsGid = gr->gr_gid;
3985				else if (UseMSP)
3986					(void) sm_io_fprintf(smioout,
3987							     SM_TIME_DEFAULT,
3988							     "WARNING: RunAsUser for MSP ignored, check group ids (egid=%ld, want=%ld)\n",
3989							     (long) EffGid,
3990							     (long) gr->gr_gid);
3991			}
3992		}
3993		if (tTd(47, 5))
3994			sm_dprintf("readcf: RunAsUser = %d:%d\n",
3995				   (int) RunAsUid, (int) RunAsGid);
3996		break;
3997
3998	  case O_DSN_RRT:
3999		RrtImpliesDsn = atobool(val);
4000		break;
4001
4002	  case O_PIDFILE:
4003		PSTRSET(PidFile, val);
4004		break;
4005
4006	  case O_DONTBLAMESENDMAIL:
4007		p = val;
4008		for (;;)
4009		{
4010			register struct dbsval *dbs;
4011			extern struct dbsval DontBlameSendmailValues[];
4012
4013			while (isascii(*p) && (isspace(*p) || ispunct(*p)))
4014				p++;
4015			if (*p == '\0')
4016				break;
4017			val = p;
4018			while (isascii(*p) && isalnum(*p))
4019				p++;
4020			if (*p != '\0')
4021				*p++ = '\0';
4022
4023			for (dbs = DontBlameSendmailValues;
4024			     dbs->dbs_name != NULL; dbs++)
4025			{
4026				if (sm_strcasecmp(val, dbs->dbs_name) == 0)
4027					break;
4028			}
4029			if (dbs->dbs_name == NULL)
4030				syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
4031			else if (dbs->dbs_flag == DBS_SAFE)
4032				clrbitmap(DontBlameSendmail);
4033			else
4034				setbitn(dbs->dbs_flag, DontBlameSendmail);
4035		}
4036		sticky = false;
4037		break;
4038
4039	  case O_DPI:
4040		if (sm_strcasecmp(val, "loopback") == 0)
4041			DontProbeInterfaces = DPI_SKIPLOOPBACK;
4042		else if (atobool(val))
4043			DontProbeInterfaces = DPI_PROBENONE;
4044		else
4045			DontProbeInterfaces = DPI_PROBEALL;
4046		break;
4047
4048	  case O_MAXRCPT:
4049		MaxRcptPerMsg = atoi(val);
4050		break;
4051
4052	  case O_RCPTTHROT:
4053		BadRcptThrottle = atoi(val);
4054		break;
4055
4056#if _FFR_RCPTTHROTDELAY
4057	  case O_RCPTTHROTDELAY:
4058		BadRcptThrottleDelay = atoi(val);
4059		break;
4060#endif
4061
4062	  case O_DEADLETTER:
4063		CANONIFY(val);
4064		PSTRSET(DeadLetterDrop, val);
4065		break;
4066
4067#if _FFR_DONTLOCKFILESFORREAD_OPTION
4068	  case O_DONTLOCK:
4069		DontLockReadFiles = atobool(val);
4070		break;
4071#endif
4072
4073	  case O_MAXALIASRCSN:
4074		MaxAliasRecursion = atoi(val);
4075		break;
4076
4077	  case O_CNCTONLYTO:
4078		/* XXX should probably use gethostbyname */
4079#if NETINET || NETINET6
4080		i = 0;
4081		if ((subopt = strchr(val, '@')) != NULL)
4082		{
4083			*subopt = '\0';
4084			i = (int) strtoul(val, NULL, 0);
4085
4086			/* stricter checks? probably not useful. */
4087			if (i > USHRT_MAX)
4088			{
4089				syserr("readcf: option ConnectOnlyTo: invalid port %s",
4090					val);
4091				break;
4092			}
4093			val = subopt + 1;
4094		}
4095		ConnectOnlyTo.sa.sa_family = AF_UNSPEC;
4096# if NETINET6
4097		if (anynet_pton(AF_INET6, val,
4098				&ConnectOnlyTo.sin6.sin6_addr) == 1)
4099		{
4100			ConnectOnlyTo.sa.sa_family = AF_INET6;
4101			if (i != 0)
4102				ConnectOnlyTo.sin6.sin6_port = htons(i);
4103		}
4104		else
4105# endif /* NETINET6 */
4106# if NETINET
4107		{
4108			ConnectOnlyTo.sin.sin_addr.s_addr = inet_addr(val);
4109			if (ConnectOnlyTo.sin.sin_addr.s_addr != INADDR_NONE)
4110				ConnectOnlyTo.sa.sa_family = AF_INET;
4111			if (i != 0)
4112				ConnectOnlyTo.sin.sin_port = htons(i);
4113		}
4114
4115# endif /* NETINET */
4116		if (ConnectOnlyTo.sa.sa_family == AF_UNSPEC)
4117		{
4118			syserr("readcf: option ConnectOnlyTo: invalid IP address %s",
4119			       val);
4120			break;
4121		}
4122#endif /* NETINET || NETINET6 */
4123		break;
4124
4125	  case O_TRUSTUSER:
4126# if !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING)
4127		if (!UseMSP)
4128			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4129					     "readcf: option TrustedUser may cause problems on systems\n        which do not support fchown() if UseMSP is not set.\n");
4130# endif /* !HASFCHOWN && !defined(_FFR_DROP_TRUSTUSER_WARNING) */
4131		if (isascii(*val) && isdigit(*val))
4132			TrustedUid = atoi(val);
4133		else
4134		{
4135			register struct passwd *pw;
4136
4137			TrustedUid = 0;
4138			pw = sm_getpwnam(val);
4139			if (pw == NULL)
4140			{
4141				syserr("readcf: option TrustedUser: unknown user %s", val);
4142				break;
4143			}
4144			else
4145				TrustedUid = pw->pw_uid;
4146		}
4147
4148# ifdef UID_MAX
4149		if (TrustedUid > UID_MAX)
4150		{
4151			syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
4152				(long) TrustedUid, (long) UID_MAX);
4153			TrustedUid = 0;
4154		}
4155# endif /* UID_MAX */
4156		break;
4157
4158	  case O_MAXMIMEHDRLEN:
4159		p = strchr(val, '/');
4160		if (p != NULL)
4161			*p++ = '\0';
4162		MaxMimeHeaderLength = atoi(val);
4163		if (p != NULL && *p != '\0')
4164			MaxMimeFieldLength = atoi(p);
4165		else
4166			MaxMimeFieldLength = MaxMimeHeaderLength / 2;
4167
4168		if (MaxMimeHeaderLength <= 0)
4169			MaxMimeHeaderLength = 0;
4170		else if (MaxMimeHeaderLength < 128)
4171			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4172					     "Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
4173
4174		if (MaxMimeFieldLength <= 0)
4175			MaxMimeFieldLength = 0;
4176		else if (MaxMimeFieldLength < 40)
4177			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4178					     "Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
4179
4180		/*
4181		**  Headers field values now include leading space, so let's
4182		**  adjust the values to be "backward compatible".
4183		*/
4184
4185		if (MaxMimeHeaderLength > 0)
4186			MaxMimeHeaderLength++;
4187		if (MaxMimeFieldLength > 0)
4188			MaxMimeFieldLength++;
4189		break;
4190
4191	  case O_CONTROLSOCKET:
4192		PSTRSET(ControlSocketName, val);
4193		break;
4194
4195	  case O_MAXHDRSLEN:
4196		MaxHeadersLength = atoi(val);
4197
4198		if (MaxHeadersLength > 0 &&
4199		    MaxHeadersLength < (MAXHDRSLEN / 2))
4200			(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4201					     "Warning: MaxHeadersLength: headers length limit set lower than %d\n",
4202					     (MAXHDRSLEN / 2));
4203		break;
4204
4205	  case O_PROCTITLEPREFIX:
4206		PSTRSET(ProcTitlePrefix, val);
4207		break;
4208
4209#if SASL
4210	  case O_SASLINFO:
4211# if _FFR_ALLOW_SASLINFO
4212		/*
4213		**  Allow users to select their own authinfo file
4214		**  under certain circumstances, otherwise just ignore
4215		**  the option.  If the option isn't ignored, several
4216		**  commands don't work very well, e.g., mailq.
4217		**  However, this is not a "perfect" solution.
4218		**  If mail is queued, the authentication info
4219		**  will not be used in subsequent delivery attempts.
4220		**  If we really want to support this, then it has
4221		**  to be stored in the queue file.
4222		*/
4223		if (!bitset(SUBMIT_MSA, SubmitMode) && RealUid != 0 &&
4224		    RunAsUid != RealUid)
4225			break;
4226# endif /* _FFR_ALLOW_SASLINFO */
4227		PSTRSET(SASLInfo, val);
4228		break;
4229
4230	  case O_SASLMECH:
4231		if (AuthMechanisms != NULL)
4232			sm_free(AuthMechanisms); /* XXX */
4233		if (*val != '\0')
4234			AuthMechanisms = newstr(val);
4235		else
4236			AuthMechanisms = NULL;
4237		break;
4238
4239	  case O_SASLREALM:
4240		if (AuthRealm != NULL)
4241			sm_free(AuthRealm);
4242		if (*val != '\0')
4243			AuthRealm = newstr(val);
4244		else
4245			AuthRealm = NULL;
4246		break;
4247
4248	  case O_SASLOPTS:
4249		while (val != NULL && *val != '\0')
4250		{
4251			switch (*val)
4252			{
4253			  case 'A':
4254				SASLOpts |= SASL_AUTH_AUTH;
4255				break;
4256
4257			  case 'a':
4258				SASLOpts |= SASL_SEC_NOACTIVE;
4259				break;
4260
4261			  case 'c':
4262				SASLOpts |= SASL_SEC_PASS_CREDENTIALS;
4263				break;
4264
4265			  case 'd':
4266				SASLOpts |= SASL_SEC_NODICTIONARY;
4267				break;
4268
4269			  case 'f':
4270				SASLOpts |= SASL_SEC_FORWARD_SECRECY;
4271				break;
4272
4273#  if SASL >= 20101
4274			  case 'm':
4275				SASLOpts |= SASL_SEC_MUTUAL_AUTH;
4276				break;
4277#  endif /* SASL >= 20101 */
4278
4279			  case 'p':
4280				SASLOpts |= SASL_SEC_NOPLAINTEXT;
4281				break;
4282
4283			  case 'y':
4284				SASLOpts |= SASL_SEC_NOANONYMOUS;
4285				break;
4286
4287			  case ' ':	/* ignore */
4288			  case '\t':	/* ignore */
4289			  case ',':	/* ignore */
4290				break;
4291
4292			  default:
4293				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4294						     "Warning: Option: %s unknown parameter '%c'\n",
4295						     OPTNAME,
4296						     (isascii(*val) &&
4297							isprint(*val))
4298							? *val : '?');
4299				break;
4300			}
4301			++val;
4302			val = strpbrk(val, ", \t");
4303			if (val != NULL)
4304				++val;
4305		}
4306		break;
4307
4308	  case O_SASLBITS:
4309		MaxSLBits = atoi(val);
4310		break;
4311
4312#else /* SASL */
4313	  case O_SASLINFO:
4314	  case O_SASLMECH:
4315	  case O_SASLREALM:
4316	  case O_SASLOPTS:
4317	  case O_SASLBITS:
4318		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4319				     "Warning: Option: %s requires SASL support (-DSASL)\n",
4320				     OPTNAME);
4321		break;
4322#endif /* SASL */
4323
4324#if STARTTLS
4325	  case O_TLSFB2CLEAR:
4326		TLSFallbacktoClear = atobool(val);
4327		break;
4328	  case O_SRVCERTFILE:
4329		SET_STRING_EXP(SrvCertFile);
4330	  case O_SRVKEYFILE:
4331		SET_STRING_EXP(SrvKeyFile);
4332	  case O_CLTCERTFILE:
4333		SET_STRING_EXP(CltCertFile);
4334	  case O_CLTKEYFILE:
4335		SET_STRING_EXP(CltKeyFile);
4336	  case O_CACERTFILE:
4337		SET_STRING_EXP(CACertFile);
4338	  case O_CACERTPATH:
4339		SET_STRING_EXP(CACertPath);
4340#if _FFR_CLIENTCA
4341	  case O_CLTCACERTFILE:
4342		SET_STRING_EXP(CltCACertFile);
4343	  case O_CLTCACERTPATH:
4344		SET_STRING_EXP(CltCACertPath);
4345#endif
4346	  case O_DHPARAMS:
4347		SET_STRING_EXP(DHParams);
4348	  case O_CIPHERLIST:
4349		SET_STRING_EXP(CipherList);
4350	  case O_DIG_ALG:
4351		SET_STRING_EXP(CertFingerprintAlgorithm);
4352	  case O_SSLENGINEPATH:
4353		SET_STRING_EXP(SSLEnginePath);
4354	  case O_SSLENGINE:
4355		newval = sm_pstrdup_x(val);
4356		if (SSLEngine != NULL)
4357			sm_free(SSLEngine);
4358		SSLEngine = newval;
4359
4360		/*
4361		**  Which engines need to be initialized before fork()?
4362		**  XXX hack, should be an option?
4363		*/
4364
4365		if (strcmp(SSLEngine, "chil") == 0)
4366			SSLEngineprefork = true;
4367		break;
4368	  case O_SRV_SSL_OPTIONS:
4369		pssloptions = &Srv_SSL_Options;
4370	  case O_CLT_SSL_OPTIONS:
4371		if (pssloptions == NULL)
4372			pssloptions = &Clt_SSL_Options;
4373		(void) readssloptions(o->o_name, val, pssloptions, '\0');
4374		if (tTd(37, 8))
4375			sm_dprintf("ssloptions=%#lx\n", *pssloptions);
4376
4377		pssloptions = NULL;
4378		break;
4379
4380	  case O_CRLFILE:
4381		SET_STRING_EXP(CRLFile);
4382		break;
4383
4384	  case O_CRLPATH:
4385		SET_STRING_EXP(CRLPath);
4386		break;
4387
4388	/*
4389	**  XXX How about options per daemon/client instead of globally?
4390	**  This doesn't work well for some options, e.g., no server cert,
4391	**  but fine for others.
4392	**
4393	**  XXX Some people may want different certs per server.
4394	**
4395	**  See also srvfeatures()
4396	*/
4397
4398	  case O_TLS_SRV_OPTS:
4399		while (val != NULL && *val != '\0')
4400		{
4401			switch (*val)
4402			{
4403			  case 'V':
4404				TLS_Srv_Opts |= TLS_I_NO_VRFY;
4405				break;
4406			/*
4407			**  Server without a cert? That works only if
4408			**  AnonDH is enabled as cipher, which is not in the
4409			**  default list. Hence the CipherList option must
4410			**  be available. Moreover: which clients support this
4411			**  besides sendmail with this setting?
4412			*/
4413
4414			  case 'C':
4415				TLS_Srv_Opts &= ~TLS_I_SRV_CERT;
4416				break;
4417			  case ' ':	/* ignore */
4418			  case '\t':	/* ignore */
4419			  case ',':	/* ignore */
4420				break;
4421			  default:
4422				(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4423						     "Warning: Option: %s unknown parameter '%c'\n",
4424						     OPTNAME,
4425						     (isascii(*val) &&
4426							isprint(*val))
4427							? *val : '?');
4428				break;
4429			}
4430			++val;
4431			val = strpbrk(val, ", \t");
4432			if (val != NULL)
4433				++val;
4434		}
4435		break;
4436
4437	  case O_RANDFILE:
4438		PSTRSET(RandFile, val);
4439		break;
4440
4441#else /* STARTTLS */
4442	  case O_SRVCERTFILE:
4443	  case O_SRVKEYFILE:
4444	  case O_CLTCERTFILE:
4445	  case O_CLTKEYFILE:
4446	  case O_CACERTFILE:
4447	  case O_CACERTPATH:
4448#if _FFR_CLIENTCA
4449	  case O_CLTCACERTFILE:
4450	  case O_CLTCACERTPATH:
4451#endif
4452	  case O_DHPARAMS:
4453	  case O_SRV_SSL_OPTIONS:
4454	  case O_CLT_SSL_OPTIONS:
4455	  case O_CIPHERLIST:
4456	  case O_DIG_ALG:
4457	  case O_CRLFILE:
4458	  case O_CRLPATH:
4459	  case O_RANDFILE:
4460		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4461				     "Warning: Option: %s requires TLS support\n",
4462				     OPTNAME);
4463		break;
4464
4465#endif /* STARTTLS */
4466#if STARTTLS && _FFR_FIPSMODE
4467	  case O_FIPSMODE:
4468		FipsMode = atobool(val);
4469		break;
4470#endif
4471
4472	  case O_CLIENTPORT:
4473		setclientoptions(val);
4474		break;
4475
4476	  case O_DF_BUFSIZE:
4477		DataFileBufferSize = atoi(val);
4478		break;
4479
4480	  case O_XF_BUFSIZE:
4481		XscriptFileBufferSize = atoi(val);
4482		break;
4483
4484	  case O_LDAPDEFAULTSPEC:
4485#if LDAPMAP
4486		ldapmap_set_defaults(val);
4487#else /* LDAPMAP */
4488		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4489				     "Warning: Option: %s requires LDAP support (-DLDAPMAP)\n",
4490				     OPTNAME);
4491#endif /* LDAPMAP */
4492		break;
4493
4494	  case O_INPUTMILTER:
4495#if MILTER
4496		InputFilterList = newstr(val);
4497#else /* MILTER */
4498		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4499				     "Warning: Option: %s requires Milter support (-DMILTER)\n",
4500				     OPTNAME);
4501#endif /* MILTER */
4502		break;
4503
4504	  case O_MILTER:
4505#if MILTER
4506		milter_set_option(subopt, val, sticky);
4507#else /* MILTER */
4508		(void) sm_io_fprintf(smioout, SM_TIME_DEFAULT,
4509				     "Warning: Option: %s requires Milter support (-DMILTER)\n",
4510				     OPTNAME);
4511#endif /* MILTER */
4512		break;
4513
4514	  case O_QUEUE_FILE_MODE:	/* queue file mode */
4515		QueueFileMode = atooct(val) & 0777;
4516		break;
4517
4518	  case O_DLVR_MIN:	/* deliver by minimum time */
4519		DeliverByMin = convtime(val, 's');
4520		break;
4521
4522	  /* modifiers {daemon_flags} for direct submissions */
4523	  case O_DIRECTSUBMODIFIERS:
4524		{
4525			BITMAP256 m;	/* ignored */
4526			extern ENVELOPE BlankEnvelope;
4527
4528			macdefine(&BlankEnvelope.e_macro, A_PERM,
4529				  macid("{daemon_flags}"),
4530				  getmodifiers(val, m));
4531		}
4532		break;
4533
4534	  case O_FASTSPLIT:
4535		FastSplit = atoi(val);
4536		break;
4537
4538	  case O_MBDB:
4539		Mbdb = newstr(val);
4540		break;
4541
4542	  case O_MSQ:
4543		UseMSP = atobool(val);
4544		break;
4545
4546	  case O_SOFTBOUNCE:
4547		SoftBounce = atobool(val);
4548		break;
4549
4550	  case O_REJECTLOGINTERVAL:	/* time btwn log msgs while refusing */
4551		RejectLogInterval = convtime(val, 'h');
4552		break;
4553
4554	  case O_REQUIRES_DIR_FSYNC:
4555#if REQUIRES_DIR_FSYNC
4556		RequiresDirfsync = atobool(val);
4557#else
4558		/* silently ignored... required for cf file option */
4559#endif
4560		break;
4561
4562	  case O_CONNECTION_RATE_WINDOW_SIZE:
4563		ConnectionRateWindowSize = convtime(val, 's');
4564		break;
4565
4566	  case O_FALLBACKSMARTHOST:	/* fallback smart host */
4567		if (val[0] != '\0')
4568			FallbackSmartHost = newstr(val);
4569		break;
4570
4571	  case O_HELONAME:
4572		HeloName = newstr(val);
4573		break;
4574
4575#if _FFR_MEMSTAT
4576	  case O_REFUSELOWMEM:
4577		RefuseLowMem = atoi(val);
4578		break;
4579	  case O_QUEUELOWMEM:
4580		QueueLowMem = atoi(val);
4581		break;
4582	  case O_MEMRESOURCE:
4583		MemoryResource = newstr(val);
4584		break;
4585#endif /* _FFR_MEMSTAT */
4586
4587	  case O_MAXNOOPCOMMANDS:
4588		MaxNOOPCommands = atoi(val);
4589		break;
4590
4591#if _FFR_MSG_ACCEPT
4592	  case O_MSG_ACCEPT:
4593		MessageAccept = newstr(val);
4594		break;
4595#endif
4596
4597#if _FFR_QUEUE_RUN_PARANOIA
4598	  case O_CHK_Q_RUNNERS:
4599		CheckQueueRunners = atoi(val);
4600		break;
4601#endif
4602
4603#if _FFR_EIGHT_BIT_ADDR_OK
4604	  case O_EIGHT_BIT_ADDR_OK:
4605		EightBitAddrOK = atobool(val);
4606		break;
4607#endif
4608
4609#if _FFR_ADDR_TYPE_MODES
4610	  case O_ADDR_TYPE_MODES:
4611		AddrTypeModes = atobool(val);
4612		break;
4613#endif
4614
4615#if _FFR_BADRCPT_SHUTDOWN
4616	  case O_RCPTSHUTD:
4617		BadRcptShutdown = atoi(val);
4618		break;
4619
4620	  case O_RCPTSHUTDG:
4621		BadRcptShutdownGood = atoi(val);
4622		break;
4623#endif /* _FFR_BADRCPT_SHUTDOWN */
4624
4625#if _FFR_REJECT_NUL_BYTE
4626	  case O_REJECTNUL:
4627		RejectNUL = atobool(val);
4628		break;
4629#endif
4630
4631#if _FFR_BOUNCE_QUEUE
4632	  case O_BOUNCEQUEUE:
4633		bouncequeue = newstr(val);
4634		break;
4635#endif
4636
4637#if _FFR_ADD_BCC
4638	  case O_ADDBCC:
4639		AddBcc = atobool(val);
4640		break;
4641#endif
4642	  case O_USECOMPRESSEDIPV6ADDRESSES:
4643		UseCompressedIPv6Addresses = atobool(val);
4644		break;
4645
4646#if DNSSEC_TEST
4647	  case O_NSPORTIP:
4648		nsportip(val);
4649		break;
4650	  case O_NSSRCHLIST:
4651		NameSearchList = sm_strdup(val);
4652		break;
4653#endif
4654
4655#if DANE
4656	  case O_DANE:
4657		if (sm_strcasecmp(val, "always") == 0)
4658			Dane = DANE_ALWAYS;
4659		else
4660			Dane = atobool(val) ? DANE_SECURE : DANE_NEVER;
4661		break;
4662#endif
4663
4664#if _FFR_BLANKENV_MACV
4665	  case O_HACKS:
4666		Hacks = (int) strtol(val, NULL, 0);
4667		break;
4668#endif
4669
4670#if _FFR_KEEPBCC
4671	  case O_KEEPBCC:
4672		KeepBcc = atobool(val);
4673		break;
4674#endif
4675
4676# if _FFR_TLS_ALTNAMES
4677	  case O_CHECKALTNAMES:
4678		SetCertAltnames = atobool(val);
4679		break;
4680# endif
4681
4682	  default:
4683		if (tTd(37, 1))
4684		{
4685			if (isascii(opt) && isprint(opt))
4686				sm_dprintf("Warning: option %c unknown\n", opt);
4687			else
4688				sm_dprintf("Warning: option 0x%x unknown\n", opt);
4689		}
4690		break;
4691	}
4692
4693	/*
4694	**  Options with suboptions are responsible for taking care
4695	**  of sticky-ness (e.g., that a command line setting is kept
4696	**  when reading in the sendmail.cf file).  This has to be done
4697	**  when the suboptions are parsed since each suboption must be
4698	**  sticky, not the root option.
4699	*/
4700
4701	if (sticky && !bitset(OI_SUBOPT, o->o_flags))
4702		setbitn(opt, StickyOpt);
4703}
4704/*
4705**  SETCLASS -- set a string into a class
4706**
4707**	Parameters:
4708**		class -- the class to put the string in.
4709**		str -- the string to enter
4710**
4711**	Returns:
4712**		none.
4713**
4714**	Side Effects:
4715**		puts the word into the symbol table.
4716*/
4717
4718void
4719setclass(class, str)
4720	int class;
4721	char *str;
4722{
4723	register STAB *s;
4724
4725	if ((str[0] & 0377) == MATCHCLASS)
4726	{
4727		int mid;
4728
4729		str++;
4730		mid = macid(str);
4731		if (mid == 0)
4732			return;
4733
4734		if (tTd(37, 8))
4735			sm_dprintf("setclass(%s, $=%s)\n",
4736				   macname(class), macname(mid));
4737		copy_class(mid, class);
4738	}
4739	else
4740	{
4741		if (tTd(37, 8))
4742			sm_dprintf("setclass(%s, %s)\n", macname(class), str);
4743
4744		s = stab(str, ST_CLASS, ST_ENTER);
4745		setbitn(bitidx(class), s->s_class);
4746	}
4747}
4748/*
4749**  MAKEMAPENTRY -- create a map entry
4750**
4751**	Parameters:
4752**		line -- the config file line
4753**
4754**	Returns:
4755**		A pointer to the map that has been created.
4756**		NULL if there was a syntax error.
4757**
4758**	Side Effects:
4759**		Enters the map into the dictionary.
4760*/
4761
4762MAP *
4763makemapentry(line)
4764	char *line;
4765{
4766	register char *p;
4767	char *mapname;
4768	char *classname;
4769	register STAB *s;
4770	STAB *class;
4771
4772	for (p = line; SM_ISSPACE(*p); p++)
4773		continue;
4774	if (!(isascii(*p) && isalnum(*p)))
4775	{
4776		syserr("readcf: config K line: no map name");
4777		return NULL;
4778	}
4779
4780	mapname = p;
4781	while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
4782		continue;
4783	if (*p != '\0')
4784		*p++ = '\0';
4785	while (SM_ISSPACE(*p))
4786		p++;
4787	if (!(isascii(*p) && isalnum(*p)))
4788	{
4789		syserr("readcf: config K line, map %s: no map class", mapname);
4790		return NULL;
4791	}
4792	classname = p;
4793	while (isascii(*++p) && isalnum(*p))
4794		continue;
4795	if (*p != '\0')
4796		*p++ = '\0';
4797	while (SM_ISSPACE(*p))
4798		p++;
4799
4800	/* look up the class */
4801	class = stab(classname, ST_MAPCLASS, ST_FIND);
4802	if (class == NULL)
4803	{
4804		syserr("readcf: map %s: class %s not available", mapname,
4805			classname);
4806		return NULL;
4807	}
4808
4809	/* enter the map */
4810	s = stab(mapname, ST_MAP, ST_ENTER);
4811	s->s_map.map_class = &class->s_mapclass;
4812	s->s_map.map_mname = newstr(mapname);
4813
4814	if (class->s_mapclass.map_parse(&s->s_map, p))
4815		s->s_map.map_mflags |= MF_VALID;
4816
4817	if (tTd(37, 5))
4818	{
4819		sm_dprintf("map %s, class %s, flags %lx, file %s,\n",
4820			   s->s_map.map_mname, s->s_map.map_class->map_cname,
4821			   s->s_map.map_mflags, s->s_map.map_file);
4822		sm_dprintf("\tapp %s, domain %s, rebuild %s\n",
4823			   s->s_map.map_app, s->s_map.map_domain,
4824			   s->s_map.map_rebuild);
4825	}
4826	return &s->s_map;
4827}
4828/*
4829**  STRTORWSET -- convert string to rewriting set number
4830**
4831**	Parameters:
4832**		p -- the pointer to the string to decode.
4833**		endp -- if set, store the trailing delimiter here.
4834**		stabmode -- ST_ENTER to create this entry, ST_FIND if
4835**			it must already exist.
4836**
4837**	Returns:
4838**		The appropriate ruleset number.
4839**		-1 if it is not valid (error already printed)
4840*/
4841
4842int
4843strtorwset(p, endp, stabmode)
4844	char *p;
4845	char **endp;
4846	int stabmode;
4847{
4848	int ruleset;
4849	static int nextruleset = MAXRWSETS;
4850
4851	while (SM_ISSPACE(*p))
4852		p++;
4853	if (!isascii(*p))
4854	{
4855		syserr("invalid ruleset name: \"%.20s\"", p);
4856		return -1;
4857	}
4858	if (isdigit(*p))
4859	{
4860		ruleset = strtol(p, endp, 10);
4861		if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
4862		{
4863			syserr("bad ruleset %d (%d max)",
4864				ruleset, MAXRWSETS / 2);
4865			ruleset = -1;
4866		}
4867	}
4868	else
4869	{
4870		STAB *s;
4871		char delim;
4872		char *q = NULL;
4873
4874		q = p;
4875		while (*p != '\0' && isascii(*p) && (isalnum(*p) || *p == '_'))
4876			p++;
4877		if (q == p || !(isascii(*q) && isalpha(*q)))
4878		{
4879			/* no valid characters */
4880			syserr("invalid ruleset name: \"%.20s\"", q);
4881			return -1;
4882		}
4883		while (SM_ISSPACE(*p))
4884			*p++ = '\0';
4885		delim = *p;
4886		if (delim != '\0')
4887			*p = '\0';
4888		s = stab(q, ST_RULESET, stabmode);
4889		if (delim != '\0')
4890			*p = delim;
4891
4892		if (s == NULL)
4893			return -1;
4894
4895		if (stabmode == ST_ENTER && delim == '=')
4896		{
4897			while (isascii(*++p) && isspace(*p))
4898				continue;
4899			if (!(isascii(*p) && isdigit(*p)))
4900			{
4901				syserr("bad ruleset definition \"%s\" (number required after `=')", q);
4902				ruleset = -1;
4903			}
4904			else
4905			{
4906				ruleset = strtol(p, endp, 10);
4907				if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
4908				{
4909					syserr("bad ruleset number %d in \"%s\" (%d max)",
4910						ruleset, q, MAXRWSETS / 2);
4911					ruleset = -1;
4912				}
4913			}
4914		}
4915		else
4916		{
4917			if (endp != NULL)
4918				*endp = p;
4919			if (s->s_ruleset >= 0)
4920				ruleset = s->s_ruleset;
4921			else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
4922			{
4923				syserr("%s: too many named rulesets (%d max)",
4924					q, MAXRWSETS / 2);
4925				ruleset = -1;
4926			}
4927		}
4928		if (s->s_ruleset >= 0 &&
4929		    ruleset >= 0 &&
4930		    ruleset != s->s_ruleset)
4931		{
4932			syserr("%s: ruleset changed value (old %d, new %d)",
4933				q, s->s_ruleset, ruleset);
4934			ruleset = s->s_ruleset;
4935		}
4936		else if (ruleset >= 0)
4937		{
4938			s->s_ruleset = ruleset;
4939		}
4940		if (stabmode == ST_ENTER && ruleset >= 0)
4941		{
4942			char *h = NULL;
4943
4944			if (RuleSetNames[ruleset] != NULL)
4945				sm_free(RuleSetNames[ruleset]); /* XXX */
4946			if (delim != '\0' && (h = strchr(q, delim)) != NULL)
4947				*h = '\0';
4948			RuleSetNames[ruleset] = newstr(q);
4949			if (delim == '/' && h != NULL)
4950				*h = delim;	/* put back delim */
4951		}
4952	}
4953	return ruleset;
4954}
4955/*
4956**  SETTIMEOUT -- set an individual timeout
4957**
4958**	Parameters:
4959**		name -- the name of the timeout.
4960**		val -- the value of the timeout.
4961**		sticky -- if set, don't let other setoptions override
4962**			this value.
4963**
4964**	Returns:
4965**		none.
4966*/
4967
4968/* set if Timeout sub-option is stuck */
4969static BITMAP256	StickyTimeoutOpt;
4970
4971static struct timeoutinfo
4972{
4973	char		*to_name;	/* long name of timeout */
4974	unsigned char	to_code;	/* code for option */
4975} TimeOutTab[] =
4976{
4977#define TO_INITIAL			0x01
4978	{ "initial",			TO_INITIAL			},
4979#define TO_MAIL				0x02
4980	{ "mail",			TO_MAIL				},
4981#define TO_RCPT				0x03
4982	{ "rcpt",			TO_RCPT				},
4983#define TO_DATAINIT			0x04
4984	{ "datainit",			TO_DATAINIT			},
4985#define TO_DATABLOCK			0x05
4986	{ "datablock",			TO_DATABLOCK			},
4987#define TO_DATAFINAL			0x06
4988	{ "datafinal",			TO_DATAFINAL			},
4989#define TO_COMMAND			0x07
4990	{ "command",			TO_COMMAND			},
4991#define TO_RSET				0x08
4992	{ "rset",			TO_RSET				},
4993#define TO_HELO				0x09
4994	{ "helo",			TO_HELO				},
4995#define TO_QUIT				0x0A
4996	{ "quit",			TO_QUIT				},
4997#define TO_MISC				0x0B
4998	{ "misc",			TO_MISC				},
4999#define TO_IDENT			0x0C
5000	{ "ident",			TO_IDENT			},
5001#define TO_FILEOPEN			0x0D
5002	{ "fileopen",			TO_FILEOPEN			},
5003#define TO_CONNECT			0x0E
5004	{ "connect",			TO_CONNECT			},
5005#define TO_ICONNECT			0x0F
5006	{ "iconnect",			TO_ICONNECT			},
5007#define TO_QUEUEWARN			0x10
5008	{ "queuewarn",			TO_QUEUEWARN			},
5009	{ "queuewarn.*",		TO_QUEUEWARN			},
5010#define TO_QUEUEWARN_NORMAL		0x11
5011	{ "queuewarn.normal",		TO_QUEUEWARN_NORMAL		},
5012#define TO_QUEUEWARN_URGENT		0x12
5013	{ "queuewarn.urgent",		TO_QUEUEWARN_URGENT		},
5014#define TO_QUEUEWARN_NON_URGENT		0x13
5015	{ "queuewarn.non-urgent",	TO_QUEUEWARN_NON_URGENT		},
5016#define TO_QUEUERETURN			0x14
5017	{ "queuereturn",		TO_QUEUERETURN			},
5018	{ "queuereturn.*",		TO_QUEUERETURN			},
5019#define TO_QUEUERETURN_NORMAL		0x15
5020	{ "queuereturn.normal",		TO_QUEUERETURN_NORMAL		},
5021#define TO_QUEUERETURN_URGENT		0x16
5022	{ "queuereturn.urgent",		TO_QUEUERETURN_URGENT		},
5023#define TO_QUEUERETURN_NON_URGENT	0x17
5024	{ "queuereturn.non-urgent",	TO_QUEUERETURN_NON_URGENT	},
5025#define TO_HOSTSTATUS			0x18
5026	{ "hoststatus",			TO_HOSTSTATUS			},
5027#define TO_RESOLVER_RETRANS		0x19
5028	{ "resolver.retrans",		TO_RESOLVER_RETRANS		},
5029#define TO_RESOLVER_RETRANS_NORMAL	0x1A
5030	{ "resolver.retrans.normal",	TO_RESOLVER_RETRANS_NORMAL	},
5031#define TO_RESOLVER_RETRANS_FIRST	0x1B
5032	{ "resolver.retrans.first",	TO_RESOLVER_RETRANS_FIRST	},
5033#define TO_RESOLVER_RETRY		0x1C
5034	{ "resolver.retry",		TO_RESOLVER_RETRY		},
5035#define TO_RESOLVER_RETRY_NORMAL	0x1D
5036	{ "resolver.retry.normal",	TO_RESOLVER_RETRY_NORMAL	},
5037#define TO_RESOLVER_RETRY_FIRST		0x1E
5038	{ "resolver.retry.first",	TO_RESOLVER_RETRY_FIRST		},
5039#define TO_CONTROL			0x1F
5040	{ "control",			TO_CONTROL			},
5041#define TO_LHLO				0x20
5042	{ "lhlo",			TO_LHLO				},
5043#define TO_AUTH				0x21
5044	{ "auth",			TO_AUTH				},
5045#define TO_STARTTLS			0x22
5046	{ "starttls",			TO_STARTTLS			},
5047#define TO_ACONNECT			0x23
5048	{ "aconnect",			TO_ACONNECT			},
5049#define TO_QUEUEWARN_DSN		0x24
5050	{ "queuewarn.dsn",		TO_QUEUEWARN_DSN		},
5051#define TO_QUEUERETURN_DSN		0x25
5052	{ "queuereturn.dsn",		TO_QUEUERETURN_DSN		},
5053	{ NULL,				0				},
5054};
5055
5056
5057static void
5058settimeout(name, val, sticky)
5059	char *name;
5060	char *val;
5061	bool sticky;
5062{
5063	register struct timeoutinfo *to;
5064	int i, addopts;
5065	time_t toval;
5066
5067	if (tTd(37, 2))
5068		sm_dprintf("settimeout(%s = %s)", name, val);
5069
5070	for (to = TimeOutTab; to->to_name != NULL; to++)
5071	{
5072		if (sm_strcasecmp(to->to_name, name) == 0)
5073			break;
5074	}
5075
5076	if (to->to_name == NULL)
5077	{
5078		errno = 0; /* avoid bogus error text */
5079		syserr("settimeout: invalid timeout %s", name);
5080		return;
5081	}
5082
5083	/*
5084	**  See if this option is preset for us.
5085	*/
5086
5087	if (!sticky && bitnset(to->to_code, StickyTimeoutOpt))
5088	{
5089		if (tTd(37, 2))
5090			sm_dprintf(" (ignored)\n");
5091		return;
5092	}
5093
5094	if (tTd(37, 2))
5095		sm_dprintf("\n");
5096
5097	toval = convtime(val, 'm');
5098	addopts = 0;
5099
5100	switch (to->to_code)
5101	{
5102	  case TO_INITIAL:
5103		TimeOuts.to_initial = toval;
5104		break;
5105
5106	  case TO_MAIL:
5107		TimeOuts.to_mail = toval;
5108		break;
5109
5110	  case TO_RCPT:
5111		TimeOuts.to_rcpt = toval;
5112		break;
5113
5114	  case TO_DATAINIT:
5115		TimeOuts.to_datainit = toval;
5116		break;
5117
5118	  case TO_DATABLOCK:
5119		TimeOuts.to_datablock = toval;
5120		break;
5121
5122	  case TO_DATAFINAL:
5123		TimeOuts.to_datafinal = toval;
5124		break;
5125
5126	  case TO_COMMAND:
5127		TimeOuts.to_nextcommand = toval;
5128		break;
5129
5130	  case TO_RSET:
5131		TimeOuts.to_rset = toval;
5132		break;
5133
5134	  case TO_HELO:
5135		TimeOuts.to_helo = toval;
5136		break;
5137
5138	  case TO_QUIT:
5139		TimeOuts.to_quit = toval;
5140		break;
5141
5142	  case TO_MISC:
5143		TimeOuts.to_miscshort = toval;
5144		break;
5145
5146	  case TO_IDENT:
5147		TimeOuts.to_ident = toval;
5148		break;
5149
5150	  case TO_FILEOPEN:
5151		TimeOuts.to_fileopen = toval;
5152		break;
5153
5154	  case TO_CONNECT:
5155		TimeOuts.to_connect = toval;
5156		break;
5157
5158	  case TO_ICONNECT:
5159		TimeOuts.to_iconnect = toval;
5160		break;
5161
5162	  case TO_ACONNECT:
5163		TimeOuts.to_aconnect = toval;
5164		break;
5165
5166	  case TO_QUEUEWARN:
5167		toval = convtime(val, 'h');
5168		TimeOuts.to_q_warning[TOC_NORMAL] = toval;
5169		TimeOuts.to_q_warning[TOC_URGENT] = toval;
5170		TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
5171		TimeOuts.to_q_warning[TOC_DSN] = toval;
5172		addopts = 2;
5173		break;
5174
5175	  case TO_QUEUEWARN_NORMAL:
5176		toval = convtime(val, 'h');
5177		TimeOuts.to_q_warning[TOC_NORMAL] = toval;
5178		break;
5179
5180	  case TO_QUEUEWARN_URGENT:
5181		toval = convtime(val, 'h');
5182		TimeOuts.to_q_warning[TOC_URGENT] = toval;
5183		break;
5184
5185	  case TO_QUEUEWARN_NON_URGENT:
5186		toval = convtime(val, 'h');
5187		TimeOuts.to_q_warning[TOC_NONURGENT] = toval;
5188		break;
5189
5190	  case TO_QUEUEWARN_DSN:
5191		toval = convtime(val, 'h');
5192		TimeOuts.to_q_warning[TOC_DSN] = toval;
5193		break;
5194
5195	  case TO_QUEUERETURN:
5196		toval = convtime(val, 'd');
5197		TimeOuts.to_q_return[TOC_NORMAL] = toval;
5198		TimeOuts.to_q_return[TOC_URGENT] = toval;
5199		TimeOuts.to_q_return[TOC_NONURGENT] = toval;
5200		TimeOuts.to_q_return[TOC_DSN] = toval;
5201		addopts = 2;
5202		break;
5203
5204	  case TO_QUEUERETURN_NORMAL:
5205		toval = convtime(val, 'd');
5206		TimeOuts.to_q_return[TOC_NORMAL] = toval;
5207		break;
5208
5209	  case TO_QUEUERETURN_URGENT:
5210		toval = convtime(val, 'd');
5211		TimeOuts.to_q_return[TOC_URGENT] = toval;
5212		break;
5213
5214	  case TO_QUEUERETURN_NON_URGENT:
5215		toval = convtime(val, 'd');
5216		TimeOuts.to_q_return[TOC_NONURGENT] = toval;
5217		break;
5218
5219	  case TO_QUEUERETURN_DSN:
5220		toval = convtime(val, 'd');
5221		TimeOuts.to_q_return[TOC_DSN] = toval;
5222		break;
5223
5224	  case TO_HOSTSTATUS:
5225		MciInfoTimeout = toval;
5226		break;
5227
5228	  case TO_RESOLVER_RETRANS:
5229		toval = convtime(val, 's');
5230		TimeOuts.res_retrans[RES_TO_DEFAULT] = toval;
5231		TimeOuts.res_retrans[RES_TO_FIRST] = toval;
5232		TimeOuts.res_retrans[RES_TO_NORMAL] = toval;
5233		addopts = 2;
5234		break;
5235
5236	  case TO_RESOLVER_RETRY:
5237		i = atoi(val);
5238		TimeOuts.res_retry[RES_TO_DEFAULT] = i;
5239		TimeOuts.res_retry[RES_TO_FIRST] = i;
5240		TimeOuts.res_retry[RES_TO_NORMAL] = i;
5241		addopts = 2;
5242		break;
5243
5244	  case TO_RESOLVER_RETRANS_NORMAL:
5245		TimeOuts.res_retrans[RES_TO_NORMAL] = convtime(val, 's');
5246		break;
5247
5248	  case TO_RESOLVER_RETRY_NORMAL:
5249		TimeOuts.res_retry[RES_TO_NORMAL] = atoi(val);
5250		break;
5251
5252	  case TO_RESOLVER_RETRANS_FIRST:
5253		TimeOuts.res_retrans[RES_TO_FIRST] = convtime(val, 's');
5254		break;
5255
5256	  case TO_RESOLVER_RETRY_FIRST:
5257		TimeOuts.res_retry[RES_TO_FIRST] = atoi(val);
5258		break;
5259
5260	  case TO_CONTROL:
5261		TimeOuts.to_control = toval;
5262		break;
5263
5264	  case TO_LHLO:
5265		TimeOuts.to_lhlo = toval;
5266		break;
5267
5268#if SASL
5269	  case TO_AUTH:
5270		TimeOuts.to_auth = toval;
5271		break;
5272#endif
5273
5274#if STARTTLS
5275	  case TO_STARTTLS:
5276		TimeOuts.to_starttls = toval;
5277		break;
5278#endif
5279
5280	  default:
5281		syserr("settimeout: invalid timeout %s", name);
5282		break;
5283	}
5284
5285	if (sticky)
5286	{
5287		for (i = 0; i <= addopts; i++)
5288			setbitn(to->to_code + i, StickyTimeoutOpt);
5289	}
5290}
5291/*
5292**  INITTIMEOUTS -- parse and set timeout values
5293**
5294**	Parameters:
5295**		val -- a pointer to the values.  If NULL, do initial
5296**			settings.
5297**		sticky -- if set, don't let other setoptions override
5298**			this suboption value.
5299**
5300**	Returns:
5301**		none.
5302**
5303**	Side Effects:
5304**		Initializes the TimeOuts structure
5305*/
5306
5307void
5308inittimeouts(val, sticky)
5309	register char *val;
5310	bool sticky;
5311{
5312	register char *p;
5313
5314	if (tTd(37, 2))
5315		sm_dprintf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
5316	if (val == NULL)
5317	{
5318		TimeOuts.to_connect = (time_t) 0 SECONDS;
5319		TimeOuts.to_aconnect = (time_t) 0 SECONDS;
5320		TimeOuts.to_iconnect = (time_t) 0 SECONDS;
5321		TimeOuts.to_initial = (time_t) 5 MINUTES;
5322		TimeOuts.to_helo = (time_t) 5 MINUTES;
5323		TimeOuts.to_mail = (time_t) 10 MINUTES;
5324		TimeOuts.to_rcpt = (time_t) 1 HOUR;
5325		TimeOuts.to_datainit = (time_t) 5 MINUTES;
5326		TimeOuts.to_datablock = (time_t) 1 HOUR;
5327		TimeOuts.to_datafinal = (time_t) 1 HOUR;
5328		TimeOuts.to_rset = (time_t) 5 MINUTES;
5329		TimeOuts.to_quit = (time_t) 2 MINUTES;
5330		TimeOuts.to_nextcommand = (time_t) 1 HOUR;
5331		TimeOuts.to_miscshort = (time_t) 2 MINUTES;
5332#if IDENTPROTO
5333		TimeOuts.to_ident = (time_t) 5 SECONDS;
5334#else
5335		TimeOuts.to_ident = (time_t) 0 SECONDS;
5336#endif
5337		TimeOuts.to_fileopen = (time_t) 60 SECONDS;
5338		TimeOuts.to_control = (time_t) 2 MINUTES;
5339		TimeOuts.to_lhlo = (time_t) 2 MINUTES;
5340#if SASL
5341		TimeOuts.to_auth = (time_t) 10 MINUTES;
5342#endif
5343#if STARTTLS
5344		TimeOuts.to_starttls = (time_t) 1 HOUR;
5345#endif
5346		if (tTd(37, 5))
5347		{
5348			sm_dprintf("Timeouts:\n");
5349			sm_dprintf("  connect = %ld\n",
5350				   (long) TimeOuts.to_connect);
5351			sm_dprintf("  aconnect = %ld\n",
5352				   (long) TimeOuts.to_aconnect);
5353			sm_dprintf("  initial = %ld\n",
5354				   (long) TimeOuts.to_initial);
5355			sm_dprintf("  helo = %ld\n", (long) TimeOuts.to_helo);
5356			sm_dprintf("  mail = %ld\n", (long) TimeOuts.to_mail);
5357			sm_dprintf("  rcpt = %ld\n", (long) TimeOuts.to_rcpt);
5358			sm_dprintf("  datainit = %ld\n",
5359				   (long) TimeOuts.to_datainit);
5360			sm_dprintf("  datablock = %ld\n",
5361				   (long) TimeOuts.to_datablock);
5362			sm_dprintf("  datafinal = %ld\n",
5363				   (long) TimeOuts.to_datafinal);
5364			sm_dprintf("  rset = %ld\n", (long) TimeOuts.to_rset);
5365			sm_dprintf("  quit = %ld\n", (long) TimeOuts.to_quit);
5366			sm_dprintf("  nextcommand = %ld\n",
5367				   (long) TimeOuts.to_nextcommand);
5368			sm_dprintf("  miscshort = %ld\n",
5369				   (long) TimeOuts.to_miscshort);
5370			sm_dprintf("  ident = %ld\n", (long) TimeOuts.to_ident);
5371			sm_dprintf("  fileopen = %ld\n",
5372				   (long) TimeOuts.to_fileopen);
5373			sm_dprintf("  lhlo = %ld\n",
5374				   (long) TimeOuts.to_lhlo);
5375			sm_dprintf("  control = %ld\n",
5376				   (long) TimeOuts.to_control);
5377		}
5378		return;
5379	}
5380
5381	for (;; val = p)
5382	{
5383		while (SM_ISSPACE(*val))
5384			val++;
5385		if (*val == '\0')
5386			break;
5387		for (p = val; *p != '\0' && *p != ','; p++)
5388			continue;
5389		if (*p != '\0')
5390			*p++ = '\0';
5391
5392		if (isascii(*val) && isdigit(*val))
5393		{
5394			/* old syntax -- set everything */
5395			TimeOuts.to_mail = convtime(val, 'm');
5396			TimeOuts.to_rcpt = TimeOuts.to_mail;
5397			TimeOuts.to_datainit = TimeOuts.to_mail;
5398			TimeOuts.to_datablock = TimeOuts.to_mail;
5399			TimeOuts.to_datafinal = TimeOuts.to_mail;
5400			TimeOuts.to_nextcommand = TimeOuts.to_mail;
5401			if (sticky)
5402			{
5403				setbitn(TO_MAIL, StickyTimeoutOpt);
5404				setbitn(TO_RCPT, StickyTimeoutOpt);
5405				setbitn(TO_DATAINIT, StickyTimeoutOpt);
5406				setbitn(TO_DATABLOCK, StickyTimeoutOpt);
5407				setbitn(TO_DATAFINAL, StickyTimeoutOpt);
5408				setbitn(TO_COMMAND, StickyTimeoutOpt);
5409			}
5410			continue;
5411		}
5412		else
5413		{
5414			register char *q = strchr(val, ':');
5415
5416			if (q == NULL && (q = strchr(val, '=')) == NULL)
5417			{
5418				/* syntax error */
5419				continue;
5420			}
5421			*q++ = '\0';
5422			settimeout(val, q, sticky);
5423		}
5424	}
5425}
5426