]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - contrib/sendmail/src/readcf.c
Merge sendmail 8.16.1 to HEAD: See contrib/sendmail/RELEASE_NOTES for details
[FreeBSD/FreeBSD.git] / contrib / sendmail / src / readcf.c
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
23 SM_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
35 static void     fileclass __P((int, char *, char *, bool, bool, bool));
36 static char     **makeargv __P((char *));
37 static void     settimeout __P((char *, char *, bool));
38 static void     toomany __P((int, int));
39 static char     *extrquotstr __P((char *, char **, char *, bool *));
40 static void     parse_class_words __P((int, char *));
41
42
43 #if _FFR_BOUNCE_QUEUE
44 static char *bouncequeue = NULL;
45 static 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
60 static void
61 initbouncequeue()
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
82 void 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
137 void
138 readcf(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
819 char *
820 translate_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
922 static void
923 toomany(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
953 static void
954 parse_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
980 static void
981 fileclass(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 */
1221 static char dynmailerp = ' ';
1222
1223 /* list of first characters for cf defined mailers */
1224 static 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
1241 void
1242 setupdynmailers()
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
1278 bool
1279 newmodmailer(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
1371 void
1372 makemailer(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
1839 char *
1840 munchstring(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
1916 static char *
1917 extrquotstr(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
1967 static char **
1968 makeargv(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
2008 void
2009 printrules()
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
2040 void
2041 printmailer(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
2110 static 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
2262 static int readssloptions __P((char *, char *, unsigned long *, int ));
2263
2264 static int
2265 readssloptions(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
2356 int
2357 get_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
2592 static BITMAP256        StickyOpt;              /* set if option is stuck */
2593
2594 #if NAMED_BIND
2595
2596 static 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
2634 static 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
3002 void
3003 setoption(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
4718 void
4719 setclass(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
4762 MAP *
4763 makemapentry(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
4842 int
4843 strtorwset(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 */
4969 static BITMAP256        StickyTimeoutOpt;
4970
4971 static 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
5057 static void
5058 settimeout(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
5307 void
5308 inittimeouts(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 }