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