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