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