source: trunk/third/sendmail/src/readcf.c @ 12554

Revision 12554, 62.2 KB checked in by danw, 26 years ago (diff)
This commit was generated by cvs2svn to compensate for changes in r12553, which included commits to RCS files with non-trunk default branches.
Line 
1/*
2 * Copyright (c) 1998 Sendmail, Inc.  All rights reserved.
3 * Copyright (c) 1983, 1995-1997 Eric P. Allman.  All rights reserved.
4 * Copyright (c) 1988, 1993
5 *      The Regents of the University of California.  All rights reserved.
6 *
7 * By using this file, you agree to the terms and conditions set
8 * forth in the LICENSE file which can be found at the top level of
9 * the sendmail distribution.
10 *
11 */
12
13#ifndef lint
14static char sccsid[] = "@(#)readcf.c    8.238 (Berkeley) 1/28/1999";
15#endif /* not lint */
16
17# include "sendmail.h"
18# include <grp.h>
19#if NAMED_BIND
20# include <resolv.h>
21#endif
22
23/*
24**  READCF -- read configuration file.
25**
26**      This routine reads the configuration file and builds the internal
27**      form.
28**
29**      The file is formatted as a sequence of lines, each taken
30**      atomically.  The first character of each line describes how
31**      the line is to be interpreted.  The lines are:
32**              Dxval           Define macro x to have value val.
33**              Cxword          Put word into class x.
34**              Fxfile [fmt]    Read file for lines to put into
35**                              class x.  Use scanf string 'fmt'
36**                              or "%s" if not present.  Fmt should
37**                              only produce one string-valued result.
38**              Hname: value    Define header with field-name 'name'
39**                              and value as specified; this will be
40**                              macro expanded immediately before
41**                              use.
42**              Sn              Use rewriting set n.
43**              Rlhs rhs        Rewrite addresses that match lhs to
44**                              be rhs.
45**              Mn arg=val...   Define mailer.  n is the internal name.
46**                              Args specify mailer parameters.
47**              Oxvalue         Set option x to value.
48**              Pname=value     Set precedence name to value.
49**              Vversioncode[/vendorcode]
50**                              Version level/vendor name of
51**                              configuration syntax.
52**              Kmapname mapclass arguments....
53**                              Define keyed lookup of a given class.
54**                              Arguments are class dependent.
55**              Eenvar=value    Set the environment value to the given value.
56**
57**      Parameters:
58**              cfname -- configuration file name.
59**              safe -- TRUE if this is the system config file;
60**                      FALSE otherwise.
61**              e -- the main envelope.
62**
63**      Returns:
64**              none.
65**
66**      Side Effects:
67**              Builds several internal tables.
68*/
69
70void
71readcf(cfname, safe, e)
72        char *cfname;
73        bool safe;
74        register ENVELOPE *e;
75{
76        FILE *cf;
77        int ruleset = 0;
78        char *q;
79        struct rewrite *rwp = NULL;
80        char *bp;
81        auto char *ep;
82        int nfuzzy;
83        char *file;
84        bool optional;
85        int mid;
86        register char *p;
87        int sff = SFF_OPENASROOT;
88        struct stat statb;
89        char buf[MAXLINE];
90        char exbuf[MAXLINE];
91        char pvpbuf[MAXLINE + MAXATOM];
92        static char *null_list[1] = { NULL };
93        extern char **copyplist __P((char **, bool));
94        extern char *munchstring __P((char *, char **, int));
95        extern void fileclass __P((int, char *, char *, bool, bool));
96        extern void toomany __P((int, int));
97        extern void translate_dollars __P((char *));
98        extern void inithostmaps __P((void));
99
100        FileName = cfname;
101        LineNumber = 0;
102
103        if (DontLockReadFiles)
104                sff |= SFF_NOLOCK;
105        cf = safefopen(cfname, O_RDONLY, 0444, sff);
106        if (cf == NULL)
107        {
108                syserr("cannot open");
109                finis(FALSE, EX_OSFILE);
110        }
111
112        if (fstat(fileno(cf), &statb) < 0)
113        {
114                syserr("cannot fstat");
115                finis(FALSE, EX_OSFILE);
116        }
117
118        if (!S_ISREG(statb.st_mode))
119        {
120                syserr("not a plain file");
121                finis(FALSE, EX_OSFILE);
122        }
123
124        if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
125        {
126                if (OpMode == MD_DAEMON || OpMode == MD_INITALIAS)
127                        fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
128                                FileName);
129                if (LogLevel > 0)
130                        sm_syslog(LOG_CRIT, NOQID,
131                                "%s: WARNING: dangerous write permissions",
132                                FileName);
133        }
134
135#ifdef XLA
136        xla_zero();
137#endif
138
139        while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
140        {
141                if (bp[0] == '#')
142                {
143                        if (bp != buf)
144                                free(bp);
145                        continue;
146                }
147
148                /* do macro expansion mappings */
149                translate_dollars(bp);
150
151                /* interpret this line */
152                errno = 0;
153                switch (bp[0])
154                {
155                  case '\0':
156                  case '#':             /* comment */
157                        break;
158
159                  case 'R':             /* rewriting rule */
160                        for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
161                                continue;
162
163                        if (*p == '\0')
164                        {
165                                syserr("invalid rewrite line \"%s\" (tab expected)", bp);
166                                break;
167                        }
168
169                        /* allocate space for the rule header */
170                        if (rwp == NULL)
171                        {
172                                RewriteRules[ruleset] = rwp =
173                                        (struct rewrite *) xalloc(sizeof *rwp);
174                        }
175                        else
176                        {
177                                rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
178                                rwp = rwp->r_next;
179                        }
180                        rwp->r_next = NULL;
181
182                        /* expand and save the LHS */
183                        *p = '\0';
184                        expand(&bp[1], exbuf, sizeof exbuf, e);
185                        rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
186                                             sizeof pvpbuf, NULL, NULL);
187                        nfuzzy = 0;
188                        if (rwp->r_lhs != NULL)
189                        {
190                                register char **ap;
191
192                                rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
193
194                                /* count the number of fuzzy matches in LHS */
195                                for (ap = rwp->r_lhs; *ap != NULL; ap++)
196                                {
197                                        char *botch;
198
199                                        botch = NULL;
200                                        switch (**ap & 0377)
201                                        {
202                                          case MATCHZANY:
203                                          case MATCHANY:
204                                          case MATCHONE:
205                                          case MATCHCLASS:
206                                          case MATCHNCLASS:
207                                                nfuzzy++;
208                                                break;
209
210                                          case MATCHREPL:
211                                                botch = "$0-$9";
212                                                break;
213
214                                          case CANONUSER:
215                                                botch = "$:";
216                                                break;
217
218                                          case CALLSUBR:
219                                                botch = "$>";
220                                                break;
221
222                                          case CONDIF:
223                                                botch = "$?";
224                                                break;
225
226                                          case CONDFI:
227                                                botch = "$.";
228                                                break;
229
230                                          case HOSTBEGIN:
231                                                botch = "$[";
232                                                break;
233
234                                          case HOSTEND:
235                                                botch = "$]";
236                                                break;
237
238                                          case LOOKUPBEGIN:
239                                                botch = "$(";
240                                                break;
241
242                                          case LOOKUPEND:
243                                                botch = "$)";
244                                                break;
245                                        }
246                                        if (botch != NULL)
247                                                syserr("Inappropriate use of %s on LHS",
248                                                        botch);
249                                }
250                        }
251                        else
252                        {
253                                syserr("R line: null LHS");
254                                rwp->r_lhs = null_list;
255                        }
256
257                        /* expand and save the RHS */
258                        while (*++p == '\t')
259                                continue;
260                        q = p;
261                        while (*p != '\0' && *p != '\t')
262                                p++;
263                        *p = '\0';
264                        expand(q, exbuf, sizeof exbuf, e);
265                        rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
266                                             sizeof pvpbuf, NULL, NULL);
267                        if (rwp->r_rhs != NULL)
268                        {
269                                register char **ap;
270
271                                rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
272
273                                /* check no out-of-bounds replacements */
274                                nfuzzy += '0';
275                                for (ap = rwp->r_rhs; *ap != NULL; ap++)
276                                {
277                                        char *botch;
278
279                                        botch = NULL;
280                                        switch (**ap & 0377)
281                                        {
282                                          case MATCHREPL:
283                                                if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
284                                                {
285                                                        syserr("replacement $%c out of bounds",
286                                                                (*ap)[1]);
287                                                }
288                                                break;
289
290                                          case MATCHZANY:
291                                                botch = "$*";
292                                                break;
293
294                                          case MATCHANY:
295                                                botch = "$+";
296                                                break;
297
298                                          case MATCHONE:
299                                                botch = "$-";
300                                                break;
301
302                                          case MATCHCLASS:
303                                                botch = "$=";
304                                                break;
305
306                                          case MATCHNCLASS:
307                                                botch = "$~";
308                                                break;
309                                        }
310                                        if (botch != NULL)
311                                                syserr("Inappropriate use of %s on RHS",
312                                                        botch);
313                                }
314                        }
315                        else
316                        {
317                                syserr("R line: null RHS");
318                                rwp->r_rhs = null_list;
319                        }
320                        break;
321
322                  case 'S':             /* select rewriting set */
323                        expand(&bp[1], exbuf, sizeof exbuf, e);
324                        ruleset = strtorwset(exbuf, NULL, ST_ENTER);
325                        if (ruleset < 0)
326                                break;
327                        rwp = RewriteRules[ruleset];
328                        if (rwp != NULL)
329                        {
330                                if (OpMode == MD_TEST || tTd(37, 1))
331                                        printf("WARNING: Ruleset %s has multiple definitions\n",
332                                                &bp[1]);
333                                while (rwp->r_next != NULL)
334                                        rwp = rwp->r_next;
335                        }
336                        break;
337
338                  case 'D':             /* macro definition */
339                        mid = macid(&bp[1], &ep);
340                        p = munchstring(ep, NULL, '\0');
341                        define(mid, newstr(p), e);
342                        break;
343
344                  case 'H':             /* required header line */
345                        (void) chompheader(&bp[1], TRUE, NULL, e);
346                        break;
347
348                  case 'C':             /* word class */
349                  case 'T':             /* trusted user (set class `t') */
350                        if (bp[0] == 'C')
351                        {
352                                mid = macid(&bp[1], &ep);
353                                expand(ep, exbuf, sizeof exbuf, e);
354                                p = exbuf;
355                        }
356                        else
357                        {
358                                mid = 't';
359                                p = &bp[1];
360                        }
361                        while (*p != '\0')
362                        {
363                                register char *wd;
364                                char delim;
365
366                                while (*p != '\0' && isascii(*p) && isspace(*p))
367                                        p++;
368                                wd = p;
369                                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
370                                        p++;
371                                delim = *p;
372                                *p = '\0';
373                                if (wd[0] != '\0')
374                                        setclass(mid, wd);
375                                *p = delim;
376                        }
377                        break;
378
379                  case 'F':             /* word class from file */
380                        mid = macid(&bp[1], &ep);
381                        for (p = ep; isascii(*p) && isspace(*p); )
382                                p++;
383                        if (p[0] == '-' && p[1] == 'o')
384                        {
385                                optional = TRUE;
386                                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
387                                        p++;
388                                while (isascii(*p) && isspace(*p))
389                                        p++;
390                        }
391                        else
392                                optional = FALSE;
393                        file = p;
394                        if (*file == '|')
395                                p = "%s";
396                        else
397                        {
398                                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
399                                        p++;
400                                if (*p == '\0')
401                                        p = "%s";
402                                else
403                                {
404                                        *p = '\0';
405                                        while (isascii(*++p) && isspace(*p))
406                                                continue;
407                                }
408                        }
409                        fileclass(mid, file, p, safe, optional);
410                        break;
411
412#ifdef XLA
413                  case 'L':             /* extended load average description */
414                        xla_init(&bp[1]);
415                        break;
416#endif
417
418#if defined(SUN_EXTENSIONS) && defined(SUN_LOOKUP_MACRO)
419                  case 'L':             /* lookup macro */
420                  case 'G':             /* lookup class */
421                        /* reserved for Sun -- NIS+ database lookup */
422                        if (VendorCode != VENDOR_SUN)
423                                goto badline;
424                        sun_lg_config_line(bp, e);
425                        break;
426#endif
427
428                  case 'M':             /* define mailer */
429                        makemailer(&bp[1]);
430                        break;
431
432                  case 'O':             /* set option */
433                        setoption(bp[1], &bp[2], safe, FALSE, e);
434                        break;
435
436                  case 'P':             /* set precedence */
437                        if (NumPriorities >= MAXPRIORITIES)
438                        {
439                                toomany('P', MAXPRIORITIES);
440                                break;
441                        }
442                        for (p = &bp[1]; *p != '\0' && *p != '='; p++)
443                                continue;
444                        if (*p == '\0')
445                                goto badline;
446                        *p = '\0';
447                        Priorities[NumPriorities].pri_name = newstr(&bp[1]);
448                        Priorities[NumPriorities].pri_val = atoi(++p);
449                        NumPriorities++;
450                        break;
451
452                  case 'V':             /* configuration syntax version */
453                        for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
454                                continue;
455                        if (!isascii(*p) || !isdigit(*p))
456                        {
457                                syserr("invalid argument to V line: \"%.20s\"",
458                                        &bp[1]);
459                                break;
460                        }
461                        ConfigLevel = strtol(p, &ep, 10);
462
463                        /*
464                        **  Do heuristic tweaking for back compatibility.
465                        */
466
467                        if (ConfigLevel >= 5)
468                        {
469                                /* level 5 configs have short name in $w */
470                                p = macvalue('w', e);
471                                if (p != NULL && (p = strchr(p, '.')) != NULL)
472                                        *p = '\0';
473                                define('w', macvalue('w', e), e);
474                        }
475                        if (ConfigLevel >= 6)
476                        {
477                                ColonOkInAddr = FALSE;
478                        }
479
480                        /*
481                        **  Look for vendor code.
482                        */
483
484                        if (*ep++ == '/')
485                        {
486                                extern bool setvendor __P((char *));
487
488                                /* extract vendor code */
489                                for (p = ep; isascii(*p) && isalpha(*p); )
490                                        p++;
491                                *p = '\0';
492
493                                if (!setvendor(ep))
494                                        syserr("invalid V line vendor code: \"%s\"",
495                                                ep);
496                        }
497                        break;
498
499                  case 'K':
500                        expand(&bp[1], exbuf, sizeof exbuf, e);
501                        (void) makemapentry(exbuf);
502                        break;
503
504                  case 'E':
505                        p = strchr(bp, '=');
506                        if (p != NULL)
507                                *p++ = '\0';
508                        setuserenv(&bp[1], p);
509                        break;
510
511                  default:
512                  badline:
513                        syserr("unknown configuration line \"%s\"", bp);
514                }
515                if (bp != buf)
516                        free(bp);
517        }
518        if (ferror(cf))
519        {
520                syserr("I/O read error");
521                finis(FALSE, EX_OSFILE);
522        }
523        fclose(cf);
524        FileName = NULL;
525
526        /* initialize host maps from local service tables */
527        inithostmaps();
528
529        /* determine if we need to do special name-server frotz */
530        {
531                int nmaps;
532                char *maptype[MAXMAPSTACK];
533                short mapreturn[MAXMAPACTIONS];
534
535                nmaps = switch_map_find("hosts", maptype, mapreturn);
536                UseNameServer = FALSE;
537                if (nmaps > 0 && nmaps <= MAXMAPSTACK)
538                {
539                        register int mapno;
540
541                        for (mapno = 0; mapno < nmaps && !UseNameServer; mapno++)
542                        {
543                                if (strcmp(maptype[mapno], "dns") == 0)
544                                        UseNameServer = TRUE;
545                        }
546                }
547
548#ifdef HESIOD
549                nmaps = switch_map_find("passwd", maptype, mapreturn);
550                UseHesiod = FALSE;
551                if (nmaps > 0 && nmaps <= MAXMAPSTACK)
552                {
553                        register int mapno;
554
555                        for (mapno = 0; mapno < nmaps && !UseHesiod; mapno++)
556                        {
557                                if (strcmp(maptype[mapno], "hesiod") == 0)
558                                        UseHesiod = TRUE;
559                        }
560                }
561#endif
562        }
563}
564/*
565**  TRANSLATE_DOLLARS -- convert $x into internal form
566**
567**      Actually does all appropriate pre-processing of a config line
568**      to turn it into internal form.
569**
570**      Parameters:
571**              bp -- the buffer to translate.
572**
573**      Returns:
574**              None.  The buffer is translated in place.  Since the
575**              translations always make the buffer shorter, this is
576**              safe without a size parameter.
577*/
578
579void
580translate_dollars(bp)
581        char *bp;
582{
583        register char *p;
584        auto char *ep;
585
586        for (p = bp; *p != '\0'; p++)
587        {
588                if (*p == '#' && p > bp && ConfigLevel >= 3)
589                {
590                        /* this is an on-line comment */
591                        register char *e;
592
593                        switch (*--p & 0377)
594                        {
595                          case MACROEXPAND:
596                                /* it's from $# -- let it go through */
597                                p++;
598                                break;
599
600                          case '\\':
601                                /* it's backslash escaped */
602                                (void) strcpy(p, p + 1);
603                                break;
604
605                          default:
606                                /* delete preceeding white space */
607                                while (isascii(*p) && isspace(*p) &&
608                                       *p != '\n' && p > bp)
609                                        p--;
610                                if ((e = strchr(++p, '\n')) != NULL)
611                                        (void) strcpy(p, e);
612                                else
613                                        *p-- = '\0';
614                                break;
615                        }
616                        continue;
617                }
618
619                if (*p != '$' || p[1] == '\0')
620                        continue;
621
622                if (p[1] == '$')
623                {
624                        /* actual dollar sign.... */
625                        (void) strcpy(p, p + 1);
626                        continue;
627                }
628
629                /* convert to macro expansion character */
630                *p++ = MACROEXPAND;
631
632                /* special handling for $=, $~, $&, and $? */
633                if (*p == '=' || *p == '~' || *p == '&' || *p == '?')
634                        p++;
635
636                /* convert macro name to code */
637                *p = macid(p, &ep);
638                if (ep != p)
639                        strcpy(p + 1, ep);
640        }
641
642        /* strip trailing white space from the line */
643        while (--p > bp && isascii(*p) && isspace(*p))
644                *p = '\0';
645}
646/*
647**  TOOMANY -- signal too many of some option
648**
649**      Parameters:
650**              id -- the id of the error line
651**              maxcnt -- the maximum possible values
652**
653**      Returns:
654**              none.
655**
656**      Side Effects:
657**              gives a syserr.
658*/
659
660void
661toomany(id, maxcnt)
662        int id;
663        int maxcnt;
664{
665        syserr("too many %c lines, %d max", id, maxcnt);
666}
667/*
668**  FILECLASS -- read members of a class from a file
669**
670**      Parameters:
671**              class -- class to define.
672**              filename -- name of file to read.
673**              fmt -- scanf string to use for match.
674**              safe -- if set, this is a safe read.
675**              optional -- if set, it is not an error for the file to
676**                      not exist.
677**
678**      Returns:
679**              none
680**
681**      Side Effects:
682**
683**              puts all lines in filename that match a scanf into
684**                      the named class.
685*/
686
687void
688fileclass(class, filename, fmt, safe, optional)
689        int class;
690        char *filename;
691        char *fmt;
692        bool safe;
693        bool optional;
694{
695        FILE *f;
696        int sff;
697        pid_t pid;
698        register char *p;
699        char buf[MAXLINE];
700
701        if (tTd(37, 2))
702                printf("fileclass(%s, fmt=%s)\n", filename, fmt);
703
704        if (filename[0] == '|')
705        {
706                auto int fd;
707                int i;
708                char *argv[MAXPV + 1];
709
710                i = 0;
711                for (p = strtok(&filename[1], " \t"); p != NULL; p = strtok(NULL, " \t"))
712                {
713                        if (i >= MAXPV)
714                                break;
715                        argv[i++] = p;
716                }
717                argv[i] = NULL;
718                pid = prog_open(argv, &fd, CurEnv);
719                if (pid < 0)
720                        f = NULL;
721                else
722                        f = fdopen(fd, "r");
723        }
724        else
725        {
726                pid = -1;
727                sff = SFF_REGONLY;
728                if (!bitset(DBS_CLASSFILEINUNSAFEDIRPATH, DontBlameSendmail))
729                        sff |= SFF_SAFEDIRPATH;
730                if (!bitset(DBS_LINKEDCLASSFILEINWRITABLEDIR, DontBlameSendmail))
731                        sff |= SFF_NOWLINK;
732                if (safe)
733                        sff |= SFF_OPENASROOT;
734                if (DontLockReadFiles)
735                        sff |= SFF_NOLOCK;
736                f = safefopen(filename, O_RDONLY, 0, sff);
737        }
738        if (f == NULL)
739        {
740                if (!optional)
741                        syserr("fileclass: cannot open %s", filename);
742                return;
743        }
744
745        while (fgets(buf, sizeof buf, f) != NULL)
746        {
747                register char *p;
748# if SCANF
749                char wordbuf[MAXLINE + 1];
750# endif
751
752                if (buf[0] == '#')
753                        continue;
754# if SCANF
755                if (sscanf(buf, fmt, wordbuf) != 1)
756                        continue;
757                p = wordbuf;
758# else /* SCANF */
759                p = buf;
760# endif /* SCANF */
761
762                /*
763                **  Break up the match into words.
764                */
765
766                while (*p != '\0')
767                {
768                        register char *q;
769
770                        /* strip leading spaces */
771                        while (isascii(*p) && isspace(*p))
772                                p++;
773                        if (*p == '\0')
774                                break;
775
776                        /* find the end of the word */
777                        q = p;
778                        while (*p != '\0' && !(isascii(*p) && isspace(*p)))
779                                p++;
780                        if (*p != '\0')
781                                *p++ = '\0';
782
783                        /* enter the word in the symbol table */
784                        setclass(class, q);
785                }
786        }
787
788        (void) fclose(f);
789        if (pid > 0)
790                (void) waitfor(pid);
791}
792/*
793**  MAKEMAILER -- define a new mailer.
794**
795**      Parameters:
796**              line -- description of mailer.  This is in labeled
797**                      fields.  The fields are:
798**                         A -- the argv for this mailer
799**                         C -- the character set for MIME conversions
800**                         D -- the directory to run in
801**                         E -- the eol string
802**                         F -- the flags associated with the mailer
803**                         L -- the maximum line length
804**                         M -- the maximum message size
805**                         N -- the niceness at which to run
806**                         P -- the path to the mailer
807**                         R -- the recipient rewriting set
808**                         S -- the sender rewriting set
809**                         T -- the mailer type (for DSNs)
810**                         U -- the uid to run as
811**                      The first word is the canonical name of the mailer.
812**
813**      Returns:
814**              none.
815**
816**      Side Effects:
817**              enters the mailer into the mailer table.
818*/
819
820void
821makemailer(line)
822        char *line;
823{
824        register char *p;
825        register struct mailer *m;
826        register STAB *s;
827        int i;
828        char fcode;
829        auto char *endp;
830        extern int NextMailer;
831        extern char **makeargv __P((char *));
832        extern char *munchstring __P((char *, char **, int));
833
834        /* allocate a mailer and set up defaults */
835        m = (struct mailer *) xalloc(sizeof *m);
836        bzero((char *) m, sizeof *m);
837
838        /* collect the mailer name */
839        for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
840                continue;
841        if (*p != '\0')
842                *p++ = '\0';
843        if (line[0] == '\0')
844                syserr("name required for mailer");
845        m->m_name = newstr(line);
846
847        /* now scan through and assign info from the fields */
848        while (*p != '\0')
849        {
850                auto char *delimptr;
851
852                while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
853                        p++;
854
855                /* p now points to field code */
856                fcode = *p;
857                while (*p != '\0' && *p != '=' && *p != ',')
858                        p++;
859                if (*p++ != '=')
860                {
861                        syserr("mailer %s: `=' expected", m->m_name);
862                        return;
863                }
864                while (isascii(*p) && isspace(*p))
865                        p++;
866
867                /* p now points to the field body */
868                p = munchstring(p, &delimptr, ',');
869
870                /* install the field into the mailer struct */
871                switch (fcode)
872                {
873                  case 'P':             /* pathname */
874                        if (*p == '\0')
875                                syserr("mailer %s: empty path name", m->m_name);
876                        m->m_mailer = newstr(p);
877                        break;
878
879                  case 'F':             /* flags */
880                        for (; *p != '\0'; p++)
881                                if (!(isascii(*p) && isspace(*p)))
882                                        setbitn(*p, m->m_flags);
883                        break;
884
885                  case 'S':             /* sender rewriting ruleset */
886                  case 'R':             /* recipient rewriting ruleset */
887                        i = strtorwset(p, &endp, ST_ENTER);
888                        if (i < 0)
889                                return;
890                        if (fcode == 'S')
891                                m->m_sh_rwset = m->m_se_rwset = i;
892                        else
893                                m->m_rh_rwset = m->m_re_rwset = i;
894
895                        p = endp;
896                        if (*p++ == '/')
897                        {
898                                i = strtorwset(p, NULL, ST_ENTER);
899                                if (i < 0)
900                                        return;
901                                if (fcode == 'S')
902                                        m->m_sh_rwset = i;
903                                else
904                                        m->m_rh_rwset = i;
905                        }
906                        break;
907
908                  case 'E':             /* end of line string */
909                        if (*p == '\0')
910                                syserr("mailer %s: null end-of-line string",
911                                        m->m_name);
912                        m->m_eol = newstr(p);
913                        break;
914
915                  case 'A':             /* argument vector */
916                        if (*p == '\0')
917                                syserr("mailer %s: null argument vector",
918                                        m->m_name);
919                        m->m_argv = makeargv(p);
920                        break;
921
922                  case 'M':             /* maximum message size */
923                        m->m_maxsize = atol(p);
924                        break;
925
926                  case 'L':             /* maximum line length */
927                        m->m_linelimit = atoi(p);
928                        if (m->m_linelimit < 0)
929                                m->m_linelimit = 0;
930                        break;
931
932                  case 'N':             /* run niceness */
933                        m->m_nice = atoi(p);
934                        break;
935
936                  case 'D':             /* working directory */
937                        if (*p == '\0')
938                                syserr("mailer %s: null working directory",
939                                        m->m_name);
940                        m->m_execdir = newstr(p);
941                        break;
942
943                  case 'C':             /* default charset */
944                        if (*p == '\0')
945                                syserr("mailer %s: null charset", m->m_name);
946                        m->m_defcharset = newstr(p);
947                        break;
948
949                  case 'T':             /* MTA-Name/Address/Diagnostic types */
950                        /* extract MTA name type; default to "dns" */
951                        m->m_mtatype = newstr(p);
952                        p = strchr(m->m_mtatype, '/');
953                        if (p != NULL)
954                        {
955                                *p++ = '\0';
956                                if (*p == '\0')
957                                        p = NULL;
958                        }
959                        if (*m->m_mtatype == '\0')
960                                m->m_mtatype = "dns";
961
962                        /* extract address type; default to "rfc822" */
963                        m->m_addrtype = p;
964                        if (p != NULL)
965                                p = strchr(p, '/');
966                        if (p != NULL)
967                        {
968                                *p++ = '\0';
969                                if (*p == '\0')
970                                        p = NULL;
971                        }
972                        if (m->m_addrtype == NULL || *m->m_addrtype == '\0')
973                                m->m_addrtype = "rfc822";
974
975                        /* extract diagnostic type; default to "smtp" */
976                        m->m_diagtype = p;
977                        if (m->m_diagtype == NULL || *m->m_diagtype == '\0')
978                                m->m_diagtype = "smtp";
979                        break;
980
981                  case 'U':             /* user id */
982                        if (isascii(*p) && !isdigit(*p))
983                        {
984                                char *q = p;
985                                struct passwd *pw;
986
987                                while (*p != '\0' && isascii(*p) &&
988                                       (isalnum(*p) || strchr("-_", *p) != NULL))
989                                        p++;
990                                while (isascii(*p) && isspace(*p))
991                                        *p++ = '\0';
992                                if (*p != '\0')
993                                        *p++ = '\0';
994                                if (*q == '\0')
995                                        syserr("mailer %s: null user name",
996                                                m->m_name);
997                                pw = sm_getpwnam(q);
998                                if (pw == NULL)
999                                        syserr("readcf: mailer U= flag: unknown user %s", q);
1000                                else
1001                                {
1002                                        m->m_uid = pw->pw_uid;
1003                                        m->m_gid = pw->pw_gid;
1004                                }
1005                        }
1006                        else
1007                        {
1008                                auto char *q;
1009
1010                                m->m_uid = strtol(p, &q, 0);
1011                                p = q;
1012                                while (isascii(*p) && isspace(*p))
1013                                        p++;
1014                                if (*p != '\0')
1015                                        p++;
1016                        }
1017                        while (isascii(*p) && isspace(*p))
1018                                p++;
1019                        if (*p == '\0')
1020                                break;
1021                        if (isascii(*p) && !isdigit(*p))
1022                        {
1023                                char *q = p;
1024                                struct group *gr;
1025
1026                                while (isascii(*p) && isalnum(*p))
1027                                        p++;
1028                                *p++ = '\0';
1029                                if (*q == '\0')
1030                                        syserr("mailer %s: null group name",
1031                                                m->m_name);
1032                                gr = getgrnam(q);
1033                                if (gr == NULL)
1034                                        syserr("readcf: mailer U= flag: unknown group %s", q);
1035                                else
1036                                        m->m_gid = gr->gr_gid;
1037                        }
1038                        else
1039                        {
1040                                m->m_gid = strtol(p, NULL, 0);
1041                        }
1042                        break;
1043                }
1044
1045                p = delimptr;
1046        }
1047
1048        /* do some rationality checking */
1049        if (m->m_argv == NULL)
1050        {
1051                syserr("M%s: A= argument required", m->m_name);
1052                return;
1053        }
1054        if (m->m_mailer == NULL)
1055        {
1056                syserr("M%s: P= argument required", m->m_name);
1057                return;
1058        }
1059
1060        if (NextMailer >= MAXMAILERS)
1061        {
1062                syserr("too many mailers defined (%d max)", MAXMAILERS);
1063                return;
1064        }
1065
1066        /* do some heuristic cleanup for back compatibility */
1067        if (bitnset(M_LIMITS, m->m_flags))
1068        {
1069                if (m->m_linelimit == 0)
1070                        m->m_linelimit = SMTPLINELIM;
1071                if (ConfigLevel < 2)
1072                        setbitn(M_7BITS, m->m_flags);
1073        }
1074
1075        if (strcmp(m->m_mailer, "[IPC]") == 0 ||
1076            strcmp(m->m_mailer, "[TCP]") == 0)
1077        {
1078                if (m->m_mtatype == NULL)
1079                        m->m_mtatype = "dns";
1080                if (m->m_addrtype == NULL)
1081                        m->m_addrtype = "rfc822";
1082                if (m->m_diagtype == NULL)
1083                        m->m_diagtype = "smtp";
1084        }
1085
1086        if (strcmp(m->m_mailer, "[FILE]") == 0)
1087        {
1088                /* Use the second argument for filename */
1089                if (m->m_argv[0] == NULL || m->m_argv[1] == NULL ||
1090                    m->m_argv[2] != NULL)
1091                {
1092                        syserr("M%s: too %s parameters for [FILE] mailer",
1093                               m->m_name,
1094                               (m->m_argv[0] == NULL ||
1095                                m->m_argv[1] == NULL) ? "few" : "many");
1096                }
1097                else if (strcmp(m->m_argv[0], "FILE") != 0)
1098                {
1099                        syserr("M%s: first argument in [FILE] mailer must be FILE",
1100                               m->m_name);
1101                }
1102        }
1103
1104        if (m->m_eol == NULL)
1105        {
1106                char **pp;
1107
1108                /* default for SMTP is \r\n; use \n for local delivery */
1109                for (pp = m->m_argv; *pp != NULL; pp++)
1110                {
1111                        char *p;
1112
1113                        for (p = *pp; *p != '\0'; )
1114                        {
1115                                if ((*p++ & 0377) == MACROEXPAND && *p == 'u')
1116                                        break;
1117                        }
1118                        if (*p != '\0')
1119                                break;
1120                }
1121                if (*pp == NULL)
1122                        m->m_eol = "\r\n";
1123                else
1124                        m->m_eol = "\n";
1125        }
1126
1127        /* enter the mailer into the symbol table */
1128        s = stab(m->m_name, ST_MAILER, ST_ENTER);
1129        if (s->s_mailer != NULL)
1130        {
1131                i = s->s_mailer->m_mno;
1132                free(s->s_mailer);
1133        }
1134        else
1135        {
1136                i = NextMailer++;
1137        }
1138        Mailer[i] = s->s_mailer = m;
1139        m->m_mno = i;
1140}
1141/*
1142**  MUNCHSTRING -- translate a string into internal form.
1143**
1144**      Parameters:
1145**              p -- the string to munch.
1146**              delimptr -- if non-NULL, set to the pointer of the
1147**                      field delimiter character.
1148**              delim -- the delimiter for the field.
1149**
1150**      Returns:
1151**              the munched string.
1152*/
1153
1154char *
1155munchstring(p, delimptr, delim)
1156        register char *p;
1157        char **delimptr;
1158        int delim;
1159{
1160        register char *q;
1161        bool backslash = FALSE;
1162        bool quotemode = FALSE;
1163        static char buf[MAXLINE];
1164
1165        for (q = buf; *p != '\0' && q < &buf[sizeof buf - 1]; p++)
1166        {
1167                if (backslash)
1168                {
1169                        /* everything is roughly literal */
1170                        backslash = FALSE;
1171                        switch (*p)
1172                        {
1173                          case 'r':             /* carriage return */
1174                                *q++ = '\r';
1175                                continue;
1176
1177                          case 'n':             /* newline */
1178                                *q++ = '\n';
1179                                continue;
1180
1181                          case 'f':             /* form feed */
1182                                *q++ = '\f';
1183                                continue;
1184
1185                          case 'b':             /* backspace */
1186                                *q++ = '\b';
1187                                continue;
1188                        }
1189                        *q++ = *p;
1190                }
1191                else
1192                {
1193                        if (*p == '\\')
1194                                backslash = TRUE;
1195                        else if (*p == '"')
1196                                quotemode = !quotemode;
1197                        else if (quotemode || *p != delim)
1198                                *q++ = *p;
1199                        else
1200                                break;
1201                }
1202        }
1203
1204        if (delimptr != NULL)
1205                *delimptr = p;
1206        *q++ = '\0';
1207        return (buf);
1208}
1209/*
1210**  MAKEARGV -- break up a string into words
1211**
1212**      Parameters:
1213**              p -- the string to break up.
1214**
1215**      Returns:
1216**              a char **argv (dynamically allocated)
1217**
1218**      Side Effects:
1219**              munges p.
1220*/
1221
1222char **
1223makeargv(p)
1224        register char *p;
1225{
1226        char *q;
1227        int i;
1228        char **avp;
1229        char *argv[MAXPV + 1];
1230
1231        /* take apart the words */
1232        i = 0;
1233        while (*p != '\0' && i < MAXPV)
1234        {
1235                q = p;
1236                while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1237                        p++;
1238                while (isascii(*p) && isspace(*p))
1239                        *p++ = '\0';
1240                argv[i++] = newstr(q);
1241        }
1242        argv[i++] = NULL;
1243
1244        /* now make a copy of the argv */
1245        avp = (char **) xalloc(sizeof *avp * i);
1246        bcopy((char *) argv, (char *) avp, sizeof *avp * i);
1247
1248        return (avp);
1249}
1250/*
1251**  PRINTRULES -- print rewrite rules (for debugging)
1252**
1253**      Parameters:
1254**              none.
1255**
1256**      Returns:
1257**              none.
1258**
1259**      Side Effects:
1260**              prints rewrite rules.
1261*/
1262
1263void
1264printrules()
1265{
1266        register struct rewrite *rwp;
1267        register int ruleset;
1268
1269        for (ruleset = 0; ruleset < 10; ruleset++)
1270        {
1271                if (RewriteRules[ruleset] == NULL)
1272                        continue;
1273                printf("\n----Rule Set %d:", ruleset);
1274
1275                for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
1276                {
1277                        printf("\nLHS:");
1278                        printav(rwp->r_lhs);
1279                        printf("RHS:");
1280                        printav(rwp->r_rhs);
1281                }
1282        }
1283}
1284/*
1285**  PRINTMAILER -- print mailer structure (for debugging)
1286**
1287**      Parameters:
1288**              m -- the mailer to print
1289**
1290**      Returns:
1291**              none.
1292*/
1293
1294void
1295printmailer(m)
1296        register MAILER *m;
1297{
1298        int j;
1299
1300        printf("mailer %d (%s): P=%s S=%d/%d R=%d/%d M=%ld U=%d:%d F=",
1301                m->m_mno, m->m_name,
1302                m->m_mailer, m->m_se_rwset, m->m_sh_rwset,
1303                m->m_re_rwset, m->m_rh_rwset, m->m_maxsize,
1304                (int) m->m_uid, (int) m->m_gid);
1305        for (j = '\0'; j <= '\177'; j++)
1306                if (bitnset(j, m->m_flags))
1307                        (void) putchar(j);
1308        printf(" L=%d E=", m->m_linelimit);
1309        xputs(m->m_eol);
1310        if (m->m_defcharset != NULL)
1311                printf(" C=%s", m->m_defcharset);
1312        printf(" T=%s/%s/%s",
1313                m->m_mtatype == NULL ? "<undefined>" : m->m_mtatype,
1314                m->m_addrtype == NULL ? "<undefined>" : m->m_addrtype,
1315                m->m_diagtype == NULL ? "<undefined>" : m->m_diagtype);
1316        if (m->m_argv != NULL)
1317        {
1318                char **a = m->m_argv;
1319
1320                printf(" A=");
1321                while (*a != NULL)
1322                {
1323                        if (a != m->m_argv)
1324                                printf(" ");
1325                        xputs(*a++);
1326                }
1327        }
1328        printf("\n");
1329}
1330/*
1331**  SETOPTION -- set global processing option
1332**
1333**      Parameters:
1334**              opt -- option name.
1335**              val -- option value (as a text string).
1336**              safe -- set if this came from a configuration file.
1337**                      Some options (if set from the command line) will
1338**                      reset the user id to avoid security problems.
1339**              sticky -- if set, don't let other setoptions override
1340**                      this value.
1341**              e -- the main envelope.
1342**
1343**      Returns:
1344**              none.
1345**
1346**      Side Effects:
1347**              Sets options as implied by the arguments.
1348*/
1349
1350static BITMAP   StickyOpt;              /* set if option is stuck */
1351extern void     settimeout __P((char *, char *));
1352
1353
1354#if NAMED_BIND
1355
1356struct resolverflags
1357{
1358        char    *rf_name;       /* name of the flag */
1359        long    rf_bits;        /* bits to set/clear */
1360} ResolverFlags[] =
1361{
1362        { "debug",      RES_DEBUG       },
1363        { "aaonly",     RES_AAONLY      },
1364        { "usevc",      RES_USEVC       },
1365        { "primary",    RES_PRIMARY     },
1366        { "igntc",      RES_IGNTC       },
1367        { "recurse",    RES_RECURSE     },
1368        { "defnames",   RES_DEFNAMES    },
1369        { "stayopen",   RES_STAYOPEN    },
1370        { "dnsrch",     RES_DNSRCH      },
1371        { "true",       0               },      /* avoid error on old syntax */
1372        { NULL,         0               }
1373};
1374
1375#endif
1376
1377struct optioninfo
1378{
1379        char    *o_name;        /* long name of option */
1380        u_char  o_code;         /* short name of option */
1381        bool    o_safe;         /* safe for random people to use */
1382} OptionTab[] =
1383{
1384        { "SevenBitInput",              '7',            TRUE    },
1385#if MIME8TO7
1386        { "EightBitMode",               '8',            TRUE    },
1387#endif
1388        { "AliasFile",                  'A',            FALSE   },
1389        { "AliasWait",                  'a',            FALSE   },
1390        { "BlankSub",                   'B',            FALSE   },
1391        { "MinFreeBlocks",              'b',            TRUE    },
1392        { "CheckpointInterval",         'C',            TRUE    },
1393        { "HoldExpensive",              'c',            FALSE   },
1394        { "AutoRebuildAliases",         'D',            FALSE   },
1395        { "DeliveryMode",               'd',            TRUE    },
1396        { "ErrorHeader",                'E',            FALSE   },
1397        { "ErrorMode",                  'e',            TRUE    },
1398        { "TempFileMode",               'F',            FALSE   },
1399        { "SaveFromLine",               'f',            FALSE   },
1400        { "MatchGECOS",                 'G',            FALSE   },
1401        { "HelpFile",                   'H',            FALSE   },
1402        { "MaxHopCount",                'h',            FALSE   },
1403        { "ResolverOptions",            'I',            FALSE   },
1404        { "IgnoreDots",                 'i',            TRUE    },
1405        { "ForwardPath",                'J',            FALSE   },
1406        { "SendMimeErrors",             'j',            TRUE    },
1407        { "ConnectionCacheSize",        'k',            FALSE   },
1408        { "ConnectionCacheTimeout",     'K',            FALSE   },
1409        { "UseErrorsTo",                'l',            FALSE   },
1410        { "LogLevel",                   'L',            TRUE    },
1411        { "MeToo",                      'm',            TRUE    },
1412        { "CheckAliases",               'n',            FALSE   },
1413        { "OldStyleHeaders",            'o',            TRUE    },
1414        { "DaemonPortOptions",          'O',            FALSE   },
1415        { "PrivacyOptions",             'p',            TRUE    },
1416        { "PostmasterCopy",             'P',            FALSE   },
1417        { "QueueFactor",                'q',            FALSE   },
1418        { "QueueDirectory",             'Q',            FALSE   },
1419        { "DontPruneRoutes",            'R',            FALSE   },
1420        { "Timeout",                    'r',            FALSE   },
1421        { "StatusFile",                 'S',            FALSE   },
1422        { "SuperSafe",                  's',            TRUE    },
1423        { "QueueTimeout",               'T',            FALSE   },
1424        { "TimeZoneSpec",               't',            FALSE   },
1425        { "UserDatabaseSpec",           'U',            FALSE   },
1426        { "DefaultUser",                'u',            FALSE   },
1427        { "FallbackMXhost",             'V',            FALSE   },
1428        { "Verbose",                    'v',            TRUE    },
1429        { "TryNullMXList",              'w',            FALSE   },
1430        { "QueueLA",                    'x',            FALSE   },
1431        { "RefuseLA",                   'X',            FALSE   },
1432        { "RecipientFactor",            'y',            FALSE   },
1433        { "ForkEachJob",                'Y',            FALSE   },
1434        { "ClassFactor",                'z',            FALSE   },
1435        { "RetryFactor",                'Z',            FALSE   },
1436#define O_QUEUESORTORD  0x81
1437        { "QueueSortOrder",             O_QUEUESORTORD, TRUE    },
1438#define O_HOSTSFILE     0x82
1439        { "HostsFile",                  O_HOSTSFILE,    FALSE   },
1440#define O_MQA           0x83
1441        { "MinQueueAge",                O_MQA,          TRUE    },
1442#define O_DEFCHARSET    0x85
1443        { "DefaultCharSet",             O_DEFCHARSET,   TRUE    },
1444#define O_SSFILE        0x86
1445        { "ServiceSwitchFile",          O_SSFILE,       FALSE   },
1446#define O_DIALDELAY     0x87
1447        { "DialDelay",                  O_DIALDELAY,    TRUE    },
1448#define O_NORCPTACTION  0x88
1449        { "NoRecipientAction",          O_NORCPTACTION, TRUE    },
1450#define O_SAFEFILEENV   0x89
1451        { "SafeFileEnvironment",        O_SAFEFILEENV,  FALSE   },
1452#define O_MAXMSGSIZE    0x8a
1453        { "MaxMessageSize",             O_MAXMSGSIZE,   FALSE   },
1454#define O_COLONOKINADDR 0x8b
1455        { "ColonOkInAddr",              O_COLONOKINADDR, TRUE   },
1456#define O_MAXQUEUERUN   0x8c
1457        { "MaxQueueRunSize",            O_MAXQUEUERUN,  TRUE    },
1458#define O_MAXCHILDREN   0x8d
1459        { "MaxDaemonChildren",          O_MAXCHILDREN,  FALSE   },
1460#define O_KEEPCNAMES    0x8e
1461        { "DontExpandCnames",           O_KEEPCNAMES,   FALSE   },
1462#define O_MUSTQUOTE     0x8f
1463        { "MustQuoteChars",             O_MUSTQUOTE,    FALSE   },
1464#define O_SMTPGREETING  0x90
1465        { "SmtpGreetingMessage",        O_SMTPGREETING, FALSE   },
1466#define O_UNIXFROM      0x91
1467        { "UnixFromLine",               O_UNIXFROM,     FALSE   },
1468#define O_OPCHARS       0x92
1469        { "OperatorChars",              O_OPCHARS,      FALSE   },
1470#define O_DONTINITGRPS  0x93
1471        { "DontInitGroups",             O_DONTINITGRPS, FALSE   },
1472#define O_SLFH          0x94
1473        { "SingleLineFromHeader",       O_SLFH,         TRUE    },
1474#define O_ABH           0x95
1475        { "AllowBogusHELO",             O_ABH,          TRUE    },
1476#define O_CONNTHROT     0x97
1477        { "ConnectionRateThrottle",     O_CONNTHROT,    FALSE   },
1478#define O_UGW           0x99
1479        { "UnsafeGroupWrites",          O_UGW,          FALSE   },
1480#define O_DBLBOUNCE     0x9a
1481        { "DoubleBounceAddress",        O_DBLBOUNCE,    FALSE   },
1482#define O_HSDIR         0x9b
1483        { "HostStatusDirectory",        O_HSDIR,        FALSE   },
1484#define O_SINGTHREAD    0x9c
1485        { "SingleThreadDelivery",       O_SINGTHREAD,   FALSE   },
1486#define O_RUNASUSER     0x9d
1487        { "RunAsUser",                  O_RUNASUSER,    FALSE   },
1488#if _FFR_DSN_RRT_OPTION
1489#define O_DSN_RRT       0x9e
1490        { "RrtImpliesDsn",              O_DSN_RRT,      FALSE   },
1491#endif
1492#if _FFR_PIDFILE_OPTION
1493#define O_PIDFILE       0x9f
1494        { "PidFile",                    O_PIDFILE,      FALSE   },
1495#endif
1496#define O_DONTBLAMESENDMAIL     0xa0
1497        { "DontBlameSendmail",          O_DONTBLAMESENDMAIL,    FALSE   },
1498#define O_DPI           0xa1
1499        { "DontProbeInterfaces",        O_DPI,          FALSE   },
1500#define O_MAXRCPT       0xa2
1501        { "MaxRecipientsPerMessage",    O_MAXRCPT,      FALSE   },
1502#if _FFR_DEADLETTERDROP_OPTION
1503#define O_DEADLETTER    0xa3
1504        { "DeadLetterDrop",             O_DEADLETTER,   FALSE   },
1505#endif
1506#if _FFR_DONTLOCKFILESFORREAD_OPTION
1507#define O_DONTLOCK      0xa4
1508        { "DontLockFilesForRead",       O_DONTLOCK,     FALSE   },
1509#endif
1510#if _FFR_MAXALIASRECURSION_OPTION
1511#define O_MAXALIASRCSN  0xa5
1512        { "MaxAliasRecursion",          O_MAXALIASRCSN, FALSE   },
1513#endif
1514#if _FFR_CONNECTONLYTO_OPTION
1515#define O_CNCTONLYTO    0xa6
1516        { "ConnectOnlyTo",              O_CNCTONLYTO,   FALSE   },
1517#endif
1518#if _FFR_TRUSTED_USER
1519#define O_TRUSTUSER     0xa7
1520        { "TrustedUser",                O_TRUSTUSER,    FALSE   },
1521#endif
1522#if _FFR_MAX_MIME_HEADER_LENGTH
1523#define O_MAXMIMEHDRLEN 0xa8
1524        { "MaxMimeHeaderLength",        O_MAXMIMEHDRLEN,        FALSE   },
1525#endif
1526#if _FFR_CONTROL_SOCKET
1527#define O_CONTROLSOCKET 0xa9
1528        { "ControlSocketName",          O_CONTROLSOCKET,        FALSE   },
1529#endif
1530#if _FFR_MAX_HEADERS_LENGTH
1531#define O_MAXHDRSLEN    0xaa
1532        { "MaxHeadersLength",           O_MAXHDRSLEN,   FALSE   },
1533#endif
1534        { NULL,                         '\0',           FALSE   }
1535};
1536
1537
1538
1539void
1540setoption(opt, val, safe, sticky, e)
1541        int opt;
1542        char *val;
1543        bool safe;
1544        bool sticky;
1545        register ENVELOPE *e;
1546{
1547        register char *p;
1548        register struct optioninfo *o;
1549        char *subopt;
1550        int mid;
1551        bool can_setuid = RunAsUid == 0;
1552        auto char *ep;
1553        char buf[50];
1554        extern bool atobool __P((char *));
1555        extern time_t convtime __P((char *, char));
1556        extern int QueueLA;
1557        extern int RefuseLA;
1558        extern bool Warn_Q_option;
1559        extern void setalias __P((char *));
1560        extern int atooct __P((char *));
1561        extern void setdefuser __P((void));
1562        extern void setdaemonoptions __P((char *));
1563
1564        errno = 0;
1565        if (opt == ' ')
1566        {
1567                /* full word options */
1568                struct optioninfo *sel;
1569
1570                p = strchr(val, '=');
1571                if (p == NULL)
1572                        p = &val[strlen(val)];
1573                while (*--p == ' ')
1574                        continue;
1575                while (*++p == ' ')
1576                        *p = '\0';
1577                if (p == val)
1578                {
1579                        syserr("readcf: null option name");
1580                        return;
1581                }
1582                if (*p == '=')
1583                        *p++ = '\0';
1584                while (*p == ' ')
1585                        p++;
1586                subopt = strchr(val, '.');
1587                if (subopt != NULL)
1588                        *subopt++ = '\0';
1589                sel = NULL;
1590                for (o = OptionTab; o->o_name != NULL; o++)
1591                {
1592                        if (strncasecmp(o->o_name, val, strlen(val)) != 0)
1593                                continue;
1594                        if (strlen(o->o_name) == strlen(val))
1595                        {
1596                                /* completely specified -- this must be it */
1597                                sel = NULL;
1598                                break;
1599                        }
1600                        if (sel != NULL)
1601                                break;
1602                        sel = o;
1603                }
1604                if (sel != NULL && o->o_name == NULL)
1605                        o = sel;
1606                else if (o->o_name == NULL)
1607                {
1608                        syserr("readcf: unknown option name %s", val);
1609                        return;
1610                }
1611                else if (sel != NULL)
1612                {
1613                        syserr("readcf: ambiguous option name %s (matches %s and %s)",
1614                                val, sel->o_name, o->o_name);
1615                        return;
1616                }
1617                if (strlen(val) != strlen(o->o_name))
1618                {
1619                        int oldVerbose = Verbose;
1620
1621                        Verbose = 1;
1622                        message("Option %s used as abbreviation for %s",
1623                                val, o->o_name);
1624                        Verbose = oldVerbose;
1625                }
1626                opt = o->o_code;
1627                val = p;
1628        }
1629        else
1630        {
1631                for (o = OptionTab; o->o_name != NULL; o++)
1632                {
1633                        if (o->o_code == opt)
1634                                break;
1635                }
1636                subopt = NULL;
1637        }
1638
1639        if (tTd(37, 1))
1640        {
1641                printf(isascii(opt) && isprint(opt) ?
1642                            "setoption %s (%c).%s=" :
1643                            "setoption %s (0x%x).%s=",
1644                        o->o_name == NULL ? "<unknown>" : o->o_name,
1645                        opt,
1646                        subopt == NULL ? "" : subopt);
1647                xputs(val);
1648        }
1649
1650        /*
1651        **  See if this option is preset for us.
1652        */
1653
1654        if (!sticky && bitnset(opt, StickyOpt))
1655        {
1656                if (tTd(37, 1))
1657                        printf(" (ignored)\n");
1658                return;
1659        }
1660
1661        /*
1662        **  Check to see if this option can be specified by this user.
1663        */
1664
1665        if (!safe && RealUid == 0)
1666                safe = TRUE;
1667        if (!safe && !o->o_safe)
1668        {
1669                if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1670                {
1671                        if (tTd(37, 1))
1672                                printf(" (unsafe)");
1673                        (void) drop_privileges(TRUE);
1674                }
1675        }
1676        if (tTd(37, 1))
1677                printf("\n");
1678
1679        switch (opt & 0xff)
1680        {
1681          case '7':             /* force seven-bit input */
1682                SevenBitInput = atobool(val);
1683                break;
1684
1685#if MIME8TO7
1686          case '8':             /* handling of 8-bit input */
1687                switch (*val)
1688                {
1689                  case 'm':             /* convert 8-bit, convert MIME */
1690                        MimeMode = MM_CVTMIME|MM_MIME8BIT;
1691                        break;
1692
1693                  case 'p':             /* pass 8 bit, convert MIME */
1694                        MimeMode = MM_CVTMIME|MM_PASS8BIT;
1695                        break;
1696
1697                  case 's':             /* strict adherence */
1698                        MimeMode = MM_CVTMIME;
1699                        break;
1700
1701#if 0
1702                  case 'r':             /* reject 8-bit, don't convert MIME */
1703                        MimeMode = 0;
1704                        break;
1705
1706                  case 'j':             /* "just send 8" */
1707                        MimeMode = MM_PASS8BIT;
1708                        break;
1709
1710                  case 'a':             /* encode 8 bit if available */
1711                        MimeMode = MM_MIME8BIT|MM_PASS8BIT|MM_CVTMIME;
1712                        break;
1713
1714                  case 'c':             /* convert 8 bit to MIME, never 7 bit */
1715                        MimeMode = MM_MIME8BIT;
1716                        break;
1717#endif
1718
1719                  default:
1720                        syserr("Unknown 8-bit mode %c", *val);
1721                        finis(FALSE, EX_USAGE);
1722                }
1723                break;
1724#endif
1725
1726          case 'A':             /* set default alias file */
1727                if (val[0] == '\0')
1728                        setalias("aliases");
1729                else
1730                        setalias(val);
1731                break;
1732
1733          case 'a':             /* look N minutes for "@:@" in alias file */
1734                if (val[0] == '\0')
1735                        SafeAlias = 5 * 60;             /* five minutes */
1736                else
1737                        SafeAlias = convtime(val, 'm');
1738                break;
1739
1740          case 'B':             /* substitution for blank character */
1741                SpaceSub = val[0];
1742                if (SpaceSub == '\0')
1743                        SpaceSub = ' ';
1744                break;
1745
1746          case 'b':             /* min blocks free on queue fs/max msg size */
1747                p = strchr(val, '/');
1748                if (p != NULL)
1749                {
1750                        *p++ = '\0';
1751                        MaxMessageSize = atol(p);
1752                }
1753                MinBlocksFree = atol(val);
1754                break;
1755
1756          case 'c':             /* don't connect to "expensive" mailers */
1757                NoConnect = atobool(val);
1758                break;
1759
1760          case 'C':             /* checkpoint every N addresses */
1761                CheckpointInterval = atoi(val);
1762                break;
1763
1764          case 'd':             /* delivery mode */
1765                switch (*val)
1766                {
1767                  case '\0':
1768                        e->e_sendmode = SM_DELIVER;
1769                        break;
1770
1771                  case SM_QUEUE:        /* queue only */
1772                  case SM_DEFER:        /* queue only and defer map lookups */
1773#if !QUEUE
1774                        syserr("need QUEUE to set -odqueue or -oddefer");
1775#endif /* QUEUE */
1776                        /* fall through..... */
1777
1778                  case SM_DELIVER:      /* do everything */
1779                  case SM_FORK:         /* fork after verification */
1780                        e->e_sendmode = *val;
1781                        break;
1782
1783                  default:
1784                        syserr("Unknown delivery mode %c", *val);
1785                        finis(FALSE, EX_USAGE);
1786                }
1787                buf[0] = (char)e->e_sendmode;
1788                buf[1] = '\0';
1789                define(macid("{deliveryMode}", NULL), newstr(buf), e);
1790                break;
1791
1792          case 'D':             /* rebuild alias database as needed */
1793                AutoRebuild = atobool(val);
1794                break;
1795
1796          case 'E':             /* error message header/header file */
1797                if (*val != '\0')
1798                        ErrMsgFile = newstr(val);
1799                break;
1800
1801          case 'e':             /* set error processing mode */
1802                switch (*val)
1803                {
1804                  case EM_QUIET:        /* be silent about it */
1805                  case EM_MAIL:         /* mail back */
1806                  case EM_BERKNET:      /* do berknet error processing */
1807                  case EM_WRITE:        /* write back (or mail) */
1808                  case EM_PRINT:        /* print errors normally (default) */
1809                        e->e_errormode = *val;
1810                        break;
1811                }
1812                break;
1813
1814          case 'F':             /* file mode */
1815                FileMode = atooct(val) & 0777;
1816                break;
1817
1818          case 'f':             /* save Unix-style From lines on front */
1819                SaveFrom = atobool(val);
1820                break;
1821
1822          case 'G':             /* match recipients against GECOS field */
1823                MatchGecos = atobool(val);
1824                break;
1825
1826          case 'g':             /* default gid */
1827  g_opt:
1828                if (isascii(*val) && isdigit(*val))
1829                        DefGid = atoi(val);
1830                else
1831                {
1832                        register struct group *gr;
1833
1834                        DefGid = -1;
1835                        gr = getgrnam(val);
1836                        if (gr == NULL)
1837                                syserr("readcf: option %c: unknown group %s",
1838                                        opt, val);
1839                        else
1840                                DefGid = gr->gr_gid;
1841                }
1842                break;
1843
1844          case 'H':             /* help file */
1845                if (val[0] == '\0')
1846                        HelpFile = "sendmail.hf";
1847                else
1848                        HelpFile = newstr(val);
1849                break;
1850
1851          case 'h':             /* maximum hop count */
1852                MaxHopCount = atoi(val);
1853                break;
1854
1855          case 'I':             /* use internet domain name server */
1856#if NAMED_BIND
1857                for (p = val; *p != 0; )
1858                {
1859                        bool clearmode;
1860                        char *q;
1861                        struct resolverflags *rfp;
1862
1863                        while (*p == ' ')
1864                                p++;
1865                        if (*p == '\0')
1866                                break;
1867                        clearmode = FALSE;
1868                        if (*p == '-')
1869                                clearmode = TRUE;
1870                        else if (*p != '+')
1871                                p--;
1872                        p++;
1873                        q = p;
1874                        while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1875                                p++;
1876                        if (*p != '\0')
1877                                *p++ = '\0';
1878                        if (strcasecmp(q, "HasWildcardMX") == 0)
1879                        {
1880                                HasWildcardMX = !clearmode;
1881                                continue;
1882                        }
1883                        for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1884                        {
1885                                if (strcasecmp(q, rfp->rf_name) == 0)
1886                                        break;
1887                        }
1888                        if (rfp->rf_name == NULL)
1889                                syserr("readcf: I option value %s unrecognized", q);
1890                        else if (clearmode)
1891                                _res.options &= ~rfp->rf_bits;
1892                        else
1893                                _res.options |= rfp->rf_bits;
1894                }
1895                if (tTd(8, 2))
1896                        printf("_res.options = %x, HasWildcardMX = %d\n",
1897                                (u_int) _res.options, HasWildcardMX);
1898#else
1899                usrerr("name server (I option) specified but BIND not compiled in");
1900#endif
1901                break;
1902
1903          case 'i':             /* ignore dot lines in message */
1904                IgnrDot = atobool(val);
1905                break;
1906
1907          case 'j':             /* send errors in MIME (RFC 1341) format */
1908                SendMIMEErrors = atobool(val);
1909                break;
1910
1911          case 'J':             /* .forward search path */
1912                ForwardPath = newstr(val);
1913                break;
1914
1915          case 'k':             /* connection cache size */
1916                MaxMciCache = atoi(val);
1917                if (MaxMciCache < 0)
1918                        MaxMciCache = 0;
1919                break;
1920
1921          case 'K':             /* connection cache timeout */
1922                MciCacheTimeout = convtime(val, 'm');
1923                break;
1924
1925          case 'l':             /* use Errors-To: header */
1926                UseErrorsTo = atobool(val);
1927                break;
1928
1929          case 'L':             /* log level */
1930                if (safe || LogLevel < atoi(val))
1931                        LogLevel = atoi(val);
1932                break;
1933
1934          case 'M':             /* define macro */
1935                mid = macid(val, &ep);
1936                p = newstr(ep);
1937                if (!safe)
1938                        cleanstrcpy(p, p, MAXNAME);
1939                define(mid, p, CurEnv);
1940                sticky = FALSE;
1941                break;
1942
1943          case 'm':             /* send to me too */
1944                MeToo = atobool(val);
1945                break;
1946
1947          case 'n':             /* validate RHS in newaliases */
1948                CheckAliases = atobool(val);
1949                break;
1950
1951            /* 'N' available -- was "net name" */
1952
1953          case 'O':             /* daemon options */
1954#if DAEMON
1955                setdaemonoptions(val);
1956#else
1957                syserr("DaemonPortOptions (O option) set but DAEMON not compiled in");
1958#endif
1959                break;
1960
1961          case 'o':             /* assume old style headers */
1962                if (atobool(val))
1963                        CurEnv->e_flags |= EF_OLDSTYLE;
1964                else
1965                        CurEnv->e_flags &= ~EF_OLDSTYLE;
1966                break;
1967
1968          case 'p':             /* select privacy level */
1969                p = val;
1970                for (;;)
1971                {
1972                        register struct prival *pv;
1973                        extern struct prival PrivacyValues[];
1974
1975                        while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1976                                p++;
1977                        if (*p == '\0')
1978                                break;
1979                        val = p;
1980                        while (isascii(*p) && isalnum(*p))
1981                                p++;
1982                        if (*p != '\0')
1983                                *p++ = '\0';
1984
1985                        for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1986                        {
1987                                if (strcasecmp(val, pv->pv_name) == 0)
1988                                        break;
1989                        }
1990                        if (pv->pv_name == NULL)
1991                                syserr("readcf: Op line: %s unrecognized", val);
1992                        PrivacyFlags |= pv->pv_flag;
1993                }
1994                sticky = FALSE;
1995                break;
1996
1997          case 'P':             /* postmaster copy address for returned mail */
1998                PostMasterCopy = newstr(val);
1999                break;
2000
2001          case 'q':             /* slope of queue only function */
2002                QueueFactor = atoi(val);
2003                break;
2004
2005          case 'Q':             /* queue directory */
2006                if (val[0] == '\0')
2007                        QueueDir = "mqueue";
2008                else
2009                        QueueDir = newstr(val);
2010                if (RealUid != 0 && !safe)
2011                        Warn_Q_option = TRUE;
2012                break;
2013
2014          case 'R':             /* don't prune routes */
2015                DontPruneRoutes = atobool(val);
2016                break;
2017
2018          case 'r':             /* read timeout */
2019                if (subopt == NULL)
2020                        inittimeouts(val);
2021                else
2022                        settimeout(subopt, val);
2023                break;
2024
2025          case 'S':             /* status file */
2026                if (val[0] == '\0')
2027                        StatFile = "sendmail.st";
2028                else
2029                        StatFile = newstr(val);
2030                break;
2031
2032          case 's':             /* be super safe, even if expensive */
2033                SuperSafe = atobool(val);
2034                break;
2035
2036          case 'T':             /* queue timeout */
2037                p = strchr(val, '/');
2038                if (p != NULL)
2039                {
2040                        *p++ = '\0';
2041                        settimeout("queuewarn", p);
2042                }
2043                settimeout("queuereturn", val);
2044                break;
2045
2046          case 't':             /* time zone name */
2047                TimeZoneSpec = newstr(val);
2048                break;
2049
2050          case 'U':             /* location of user database */
2051                UdbSpec = newstr(val);
2052                break;
2053
2054          case 'u':             /* set default uid */
2055                for (p = val; *p != '\0'; p++)
2056                {
2057                        if (*p == '.' || *p == '/' || *p == ':')
2058                        {
2059                                *p++ = '\0';
2060                                break;
2061                        }
2062                }
2063                if (isascii(*val) && isdigit(*val))
2064                {
2065                        DefUid = atoi(val);
2066                        setdefuser();
2067                }
2068                else
2069                {
2070                        register struct passwd *pw;
2071
2072                        DefUid = -1;
2073                        pw = sm_getpwnam(val);
2074                        if (pw == NULL)
2075                                syserr("readcf: option u: unknown user %s", val);
2076                        else
2077                        {
2078                                DefUid = pw->pw_uid;
2079                                DefGid = pw->pw_gid;
2080                                DefUser = newstr(pw->pw_name);
2081                        }
2082                }
2083
2084#ifdef UID_MAX
2085                if (DefUid > UID_MAX)
2086                {
2087                        syserr("readcf: option u: uid value (%ld) > UID_MAX (%ld); ignored",
2088                                DefUid, UID_MAX);
2089                }
2090#endif
2091
2092                /* handle the group if it is there */
2093                if (*p == '\0')
2094                        break;
2095                val = p;
2096                goto g_opt;
2097
2098          case 'V':             /* fallback MX host */
2099                if (val[0] != '\0')
2100                        FallBackMX = newstr(val);
2101                break;
2102
2103          case 'v':             /* run in verbose mode */
2104                Verbose = atobool(val) ? 1 : 0;
2105                break;
2106
2107          case 'w':             /* if we are best MX, try host directly */
2108                TryNullMXList = atobool(val);
2109                break;
2110
2111            /* 'W' available -- was wizard password */
2112
2113          case 'x':             /* load avg at which to auto-queue msgs */
2114                QueueLA = atoi(val);
2115                break;
2116
2117          case 'X':             /* load avg at which to auto-reject connections */
2118                RefuseLA = atoi(val);
2119                break;
2120
2121          case 'y':             /* work recipient factor */
2122                WkRecipFact = atoi(val);
2123                break;
2124
2125          case 'Y':             /* fork jobs during queue runs */
2126                ForkQueueRuns = atobool(val);
2127                break;
2128
2129          case 'z':             /* work message class factor */
2130                WkClassFact = atoi(val);
2131                break;
2132
2133          case 'Z':             /* work time factor */
2134                WkTimeFact = atoi(val);
2135                break;
2136
2137          case O_QUEUESORTORD:  /* queue sorting order */
2138                switch (*val)
2139                {
2140                  case 'h':     /* Host first */
2141                  case 'H':
2142                        QueueSortOrder = QS_BYHOST;
2143                        break;
2144
2145                  case 'p':     /* Priority order */
2146                  case 'P':
2147                        QueueSortOrder = QS_BYPRIORITY;
2148                        break;
2149
2150                  case 't':     /* Submission time */
2151                  case 'T':
2152                        QueueSortOrder = QS_BYTIME;
2153                        break;
2154
2155                  default:
2156                        syserr("Invalid queue sort order \"%s\"", val);
2157                }
2158                break;
2159
2160          case O_HOSTSFILE:     /* pathname of /etc/hosts file */
2161                HostsFile = newstr(val);
2162                break;
2163
2164          case O_MQA:           /* minimum queue age between deliveries */
2165                MinQueueAge = convtime(val, 'm');
2166                break;
2167
2168          case O_DEFCHARSET:    /* default character set for mimefying */
2169                DefaultCharSet = newstr(denlstring(val, TRUE, TRUE));
2170                break;
2171
2172          case O_SSFILE:        /* service switch file */
2173                ServiceSwitchFile = newstr(val);
2174                break;
2175
2176          case O_DIALDELAY:     /* delay for dial-on-demand operation */
2177                DialDelay = convtime(val, 's');
2178                break;
2179
2180          case O_NORCPTACTION:  /* what to do if no recipient */
2181                if (strcasecmp(val, "none") == 0)
2182                        NoRecipientAction = NRA_NO_ACTION;
2183                else if (strcasecmp(val, "add-to") == 0)
2184                        NoRecipientAction = NRA_ADD_TO;
2185                else if (strcasecmp(val, "add-apparently-to") == 0)
2186                        NoRecipientAction = NRA_ADD_APPARENTLY_TO;
2187                else if (strcasecmp(val, "add-bcc") == 0)
2188                        NoRecipientAction = NRA_ADD_BCC;
2189                else if (strcasecmp(val, "add-to-undisclosed") == 0)
2190                        NoRecipientAction = NRA_ADD_TO_UNDISCLOSED;
2191                else
2192                        syserr("Invalid NoRecipientAction: %s", val);
2193                break;
2194
2195          case O_SAFEFILEENV:   /* chroot() environ for writing to files */
2196                SafeFileEnv = newstr(val);
2197                break;
2198
2199          case O_MAXMSGSIZE:    /* maximum message size */
2200                MaxMessageSize = atol(val);
2201                break;
2202
2203          case O_COLONOKINADDR: /* old style handling of colon addresses */
2204                ColonOkInAddr = atobool(val);
2205                break;
2206
2207          case O_MAXQUEUERUN:   /* max # of jobs in a single queue run */
2208                MaxQueueRun = atol(val);
2209                break;
2210
2211          case O_MAXCHILDREN:   /* max # of children of daemon */
2212                MaxChildren = atoi(val);
2213                break;
2214
2215          case O_KEEPCNAMES:    /* don't expand CNAME records */
2216                DontExpandCnames = atobool(val);
2217                break;
2218
2219          case O_MUSTQUOTE:     /* must quote these characters in phrases */
2220                strcpy(buf, "@,;:\\()[]");
2221                if (strlen(val) < (SIZE_T) sizeof buf - 10)
2222                        strcat(buf, val);
2223                MustQuoteChars = newstr(buf);
2224                break;
2225
2226          case O_SMTPGREETING:  /* SMTP greeting message (old $e macro) */
2227                SmtpGreeting = newstr(munchstring(val, NULL, '\0'));
2228                break;
2229
2230          case O_UNIXFROM:      /* UNIX From_ line (old $l macro) */
2231                UnixFromLine = newstr(munchstring(val, NULL, '\0'));
2232                break;
2233
2234          case O_OPCHARS:       /* operator characters (old $o macro) */
2235                OperatorChars = newstr(munchstring(val, NULL, '\0'));
2236                break;
2237
2238          case O_DONTINITGRPS:  /* don't call initgroups(3) */
2239                DontInitGroups = atobool(val);
2240                break;
2241
2242          case O_SLFH:          /* make sure from fits on one line */
2243                SingleLineFromHeader = atobool(val);
2244                break;
2245
2246          case O_ABH:           /* allow HELO commands with syntax errors */
2247                AllowBogusHELO = atobool(val);
2248                break;
2249
2250          case O_CONNTHROT:     /* connection rate throttle */
2251                ConnRateThrottle = atoi(val);
2252                break;
2253
2254          case O_UGW:           /* group writable files are unsafe */
2255                if (!atobool(val))
2256                        DontBlameSendmail |= DBS_GROUPWRITABLEFORWARDFILESAFE|DBS_GROUPWRITABLEINCLUDEFILESAFE;
2257                break;
2258
2259          case O_DBLBOUNCE:     /* address to which to send double bounces */
2260                if (val[0] != '\0')
2261                        DoubleBounceAddr = newstr(val);
2262                else
2263                        syserr("readcf: option DoubleBounceAddress: value required");
2264                break;
2265
2266          case O_HSDIR:         /* persistent host status directory */
2267                if (val[0] != '\0')
2268                        HostStatDir = newstr(val);
2269                break;
2270
2271          case O_SINGTHREAD:    /* single thread deliveries (requires hsdir) */
2272                SingleThreadDelivery = atobool(val);
2273                break;
2274
2275          case O_RUNASUSER:     /* run bulk of code as this user */
2276                for (p = val; *p != '\0'; p++)
2277                {
2278                        if (*p == '.' || *p == '/' || *p == ':')
2279                        {
2280                                *p++ = '\0';
2281                                break;
2282                        }
2283                }
2284                if (isascii(*val) && isdigit(*val))
2285                {
2286                        if (can_setuid)
2287                                RunAsUid = atoi(val);
2288                }
2289                else
2290                {
2291                        register struct passwd *pw;
2292
2293                        pw = sm_getpwnam(val);
2294                        if (pw == NULL)
2295                                syserr("readcf: option RunAsUser: unknown user %s", val);
2296                        else if (can_setuid)
2297                        {
2298                                if (*p == '\0')
2299                                        RunAsUserName = newstr(val);
2300                                RunAsUid = pw->pw_uid;
2301                                RunAsGid = pw->pw_gid;
2302                        }
2303                }
2304#ifdef UID_MAX
2305                if (RunAsUid > UID_MAX)
2306                {
2307                        syserr("readcf: option RunAsUser: uid value (%ld) > UID_MAX (%ld); ignored",
2308                                RunAsUid, UID_MAX);
2309                }
2310#endif
2311                if (*p != '\0')
2312                {
2313                        if (isascii(*p) && isdigit(*p))
2314                        {
2315                                if (can_setuid)
2316                                        RunAsGid = atoi(p);
2317                        }
2318                        else
2319                        {
2320                                register struct group *gr;
2321       
2322                                gr = getgrnam(p);
2323                                if (gr == NULL)
2324                                        syserr("readcf: option RunAsUser: unknown group %s",
2325                                                p);
2326                                else if (can_setuid)
2327                                        RunAsGid = gr->gr_gid;
2328                        }
2329                }
2330                if (tTd(47, 5))
2331                        printf("readcf: RunAsUser = %d:%d\n", (int)RunAsUid, (int)RunAsGid);
2332                break;
2333
2334#if _FFR_DSN_RRT_OPTION
2335          case O_DSN_RRT:
2336                RrtImpliesDsn = atobool(val);
2337                break;
2338#endif
2339
2340#if _FFR_PIDFILE_OPTION
2341          case O_PIDFILE:
2342                free(PidFile);
2343                PidFile = newstr(val);
2344                break;
2345#endif
2346
2347        case O_DONTBLAMESENDMAIL:
2348                p = val;
2349                for (;;)
2350                {
2351                        register struct dbsval *dbs;
2352                        extern struct dbsval DontBlameSendmailValues[];
2353
2354                        while (isascii(*p) && (isspace(*p) || ispunct(*p)))
2355                                p++;
2356                        if (*p == '\0')
2357                                break;
2358                        val = p;
2359                        while (isascii(*p) && isalnum(*p))
2360                                p++;
2361                        if (*p != '\0')
2362                                *p++ = '\0';
2363
2364                        for (dbs = DontBlameSendmailValues;
2365                             dbs->dbs_name != NULL; dbs++)
2366                        {
2367                                if (strcasecmp(val, dbs->dbs_name) == 0)
2368                                        break;
2369                        }
2370                        if (dbs->dbs_name == NULL)
2371                                syserr("readcf: DontBlameSendmail option: %s unrecognized", val);
2372                        else if (dbs->dbs_flag == DBS_SAFE)
2373                                DontBlameSendmail = DBS_SAFE;
2374                        else
2375                                DontBlameSendmail |= dbs->dbs_flag;
2376                }
2377                sticky = FALSE;
2378                break;
2379
2380          case O_DPI:
2381                DontProbeInterfaces = atobool(val);
2382                break;
2383
2384          case O_MAXRCPT:
2385                MaxRcptPerMsg = atoi(val);
2386                break;
2387
2388#if _FFR_DEADLETTERDROP_OPTION
2389          case O_DEADLETTER:
2390                if (DeadLetterDrop != NULL)
2391                        free(DeadLetterDrop);
2392                DeadLetterDrop = newstr(val);
2393                break;
2394#endif
2395
2396#if _FFR_DONTLOCKFILESFORREAD_OPTION
2397          case O_DONTLOCK:
2398                DontLockReadFiles = atobool(val);
2399                break;
2400#endif
2401
2402#if _FFR_MAXALIASRECURSION_OPTION
2403          case O_MAXALIASRCSN:
2404                MaxAliasRecursion = atoi(val);
2405                break;
2406#endif
2407
2408#if _FFR_CONNECTONLYTO_OPTION
2409          case O_CNCTONLYTO:
2410                /* XXX should probably use gethostbyname */
2411                ConnectOnlyTo = inet_addr(val);
2412                break;
2413#endif
2414
2415#if _FFR_TRUSTED_USER
2416          case O_TRUSTUSER:
2417                if (isascii(*val) && isdigit(*val))
2418                        TrustedUid = atoi(val);
2419                else
2420                {
2421                        register struct passwd *pw;
2422
2423                        TrustedUid = 0;
2424                        pw = sm_getpwnam(val);
2425                        if (pw == NULL)
2426                                syserr("readcf: option TrustedUser: unknown user %s", val);
2427                        else
2428                                TrustedUid = pw->pw_uid;
2429                }
2430
2431#ifdef UID_MAX
2432                if (TrustedUid > UID_MAX)
2433                {
2434                        syserr("readcf: option TrustedUser: uid value (%ld) > UID_MAX (%ld)",
2435                                TrustedUid, UID_MAX);
2436                        TrustedUid = 0;
2437                }
2438#endif
2439                break;
2440#endif
2441
2442#if _FFR_MAX_MIME_HEADER_LENGTH
2443          case O_MAXMIMEHDRLEN:
2444                p = strchr(val, '/');
2445                if (p != NULL)
2446                        *p++ = '\0';
2447                MaxMimeHeaderLength = atoi(val);
2448                if (p != NULL && *p != '\0')
2449                        MaxMimeFieldLength = atoi(p);
2450                else
2451                        MaxMimeFieldLength = MaxMimeHeaderLength / 2;
2452
2453                if (MaxMimeHeaderLength < 0)
2454                        MaxMimeHeaderLength = 0;
2455                else if (MaxMimeHeaderLength < 128)
2456                        printf("Warning: MaxMimeHeaderLength: header length limit set lower than 128\n");
2457
2458                if (MaxMimeFieldLength < 0)
2459                        MaxMimeFieldLength = 0;
2460                else if (MaxMimeFieldLength < 40)
2461                        printf("Warning: MaxMimeHeaderLength: field length limit set lower than 40\n");
2462                break;
2463#endif
2464
2465#if _FFR_CONTROL_SOCKET
2466          case O_CONTROLSOCKET:
2467                if (ControlSocketName != NULL)
2468                        free(ControlSocketName);
2469                ControlSocketName = newstr(val);
2470                break;
2471#endif
2472
2473#if _FFR_MAX_HEADERS_LENGTH
2474          case O_MAXHDRSLEN:
2475                MaxHeadersLength = atoi(val);
2476
2477                if (MaxHeadersLength > 0 &&
2478                    MaxHeadersLength < (MAXHDRSLEN / 2))
2479                        printf("Warning: MaxHeadersLength: headers length limit set lower than %d\n", MAXHDRSLEN);
2480                break;
2481#endif
2482
2483          default:
2484                if (tTd(37, 1))
2485                {
2486                        if (isascii(opt) && isprint(opt))
2487                                printf("Warning: option %c unknown\n", opt);
2488                        else
2489                                printf("Warning: option 0x%x unknown\n", opt);
2490                }
2491                break;
2492        }
2493        if (sticky)
2494                setbitn(opt, StickyOpt);
2495}
2496/*
2497**  SETCLASS -- set a string into a class
2498**
2499**      Parameters:
2500**              class -- the class to put the string in.
2501**              str -- the string to enter
2502**
2503**      Returns:
2504**              none.
2505**
2506**      Side Effects:
2507**              puts the word into the symbol table.
2508*/
2509
2510void
2511setclass(class, str)
2512        int class;
2513        char *str;
2514{
2515        register STAB *s;
2516
2517        if (tTd(37, 8))
2518                printf("setclass(%s, %s)\n", macname(class), str);
2519        s = stab(str, ST_CLASS, ST_ENTER);
2520        setbitn(class, s->s_class);
2521}
2522/*
2523**  MAKEMAPENTRY -- create a map entry
2524**
2525**      Parameters:
2526**              line -- the config file line
2527**
2528**      Returns:
2529**              A pointer to the map that has been created.
2530**              NULL if there was a syntax error.
2531**
2532**      Side Effects:
2533**              Enters the map into the dictionary.
2534*/
2535
2536MAP *
2537makemapentry(line)
2538        char *line;
2539{
2540        register char *p;
2541        char *mapname;
2542        char *classname;
2543        register STAB *s;
2544        STAB *class;
2545
2546        for (p = line; isascii(*p) && isspace(*p); p++)
2547                continue;
2548        if (!(isascii(*p) && isalnum(*p)))
2549        {
2550                syserr("readcf: config K line: no map name");
2551                return NULL;
2552        }
2553
2554        mapname = p;
2555        while ((isascii(*++p) && isalnum(*p)) || *p == '_' || *p == '.')
2556                continue;
2557        if (*p != '\0')
2558                *p++ = '\0';
2559        while (isascii(*p) && isspace(*p))
2560                p++;
2561        if (!(isascii(*p) && isalnum(*p)))
2562        {
2563                syserr("readcf: config K line, map %s: no map class", mapname);
2564                return NULL;
2565        }
2566        classname = p;
2567        while (isascii(*++p) && isalnum(*p))
2568                continue;
2569        if (*p != '\0')
2570                *p++ = '\0';
2571        while (isascii(*p) && isspace(*p))
2572                p++;
2573
2574        /* look up the class */
2575        class = stab(classname, ST_MAPCLASS, ST_FIND);
2576        if (class == NULL)
2577        {
2578                syserr("readcf: map %s: class %s not available", mapname, classname);
2579                return NULL;
2580        }
2581
2582        /* enter the map */
2583        s = stab(mapname, ST_MAP, ST_ENTER);
2584        s->s_map.map_class = &class->s_mapclass;
2585        s->s_map.map_mname = newstr(mapname);
2586
2587        if (class->s_mapclass.map_parse(&s->s_map, p))
2588                s->s_map.map_mflags |= MF_VALID;
2589
2590        if (tTd(37, 5))
2591        {
2592                printf("map %s, class %s, flags %lx, file %s,\n",
2593                        s->s_map.map_mname, s->s_map.map_class->map_cname,
2594                        s->s_map.map_mflags,
2595                        s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
2596                printf("\tapp %s, domain %s, rebuild %s\n",
2597                        s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
2598                        s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
2599                        s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
2600        }
2601
2602        return &s->s_map;
2603}
2604/*
2605**  STRTORWSET -- convert string to rewriting set number
2606**
2607**      Parameters:
2608**              p -- the pointer to the string to decode.
2609**              endp -- if set, store the trailing delimiter here.
2610**              stabmode -- ST_ENTER to create this entry, ST_FIND if
2611**                      it must already exist.
2612**
2613**      Returns:
2614**              The appropriate ruleset number.
2615**              -1 if it is not valid (error already printed)
2616*/
2617
2618int
2619strtorwset(p, endp, stabmode)
2620        char *p;
2621        char **endp;
2622        int stabmode;
2623{
2624        int ruleset;
2625        static int nextruleset = MAXRWSETS;
2626
2627        while (isascii(*p) && isspace(*p))
2628                p++;
2629        if (!isascii(*p))
2630        {
2631                syserr("invalid ruleset name: \"%.20s\"", p);
2632                return -1;
2633        }
2634        if (isdigit(*p))
2635        {
2636                ruleset = strtol(p, endp, 10);
2637                if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
2638                {
2639                        syserr("bad ruleset %d (%d max)",
2640                                ruleset, MAXRWSETS / 2);
2641                        ruleset = -1;
2642                }
2643        }
2644        else
2645        {
2646                STAB *s;
2647                char delim;
2648                char *q;
2649
2650                q = p;
2651                while (*p != '\0' && isascii(*p) &&
2652                       (isalnum(*p) || *p == '_'))
2653                        p++;
2654                if (q == p || !(isascii(*q) && isalpha(*q)))
2655                {
2656                        /* no valid characters */
2657                        syserr("invalid ruleset name: \"%.20s\"", q);
2658                        return -1;
2659                }
2660                while (isascii(*p) && isspace(*p))
2661                        *p++ = '\0';
2662                delim = *p;
2663                if (delim != '\0')
2664                        *p = '\0';
2665                s = stab(q, ST_RULESET, stabmode);
2666                if (delim != '\0')
2667                        *p = delim;
2668
2669                if (s == NULL)
2670                        return -1;
2671
2672                if (stabmode == ST_ENTER && delim == '=')
2673                {
2674                        while (isascii(*++p) && isspace(*p))
2675                                continue;
2676                        if (!(isascii(*p) && isdigit(*p)))
2677                        {
2678                                syserr("bad ruleset definition \"%s\" (number required after `=')", q);
2679                                ruleset = -1;
2680                        }
2681                        else
2682                        {
2683                                ruleset = strtol(p, endp, 10);
2684                                if (ruleset >= MAXRWSETS / 2 || ruleset < 0)
2685                                {
2686                                        syserr("bad ruleset number %d in \"%s\" (%d max)",
2687                                                ruleset, q, MAXRWSETS / 2);
2688                                        ruleset = -1;
2689                                }
2690                        }
2691                }
2692                else
2693                {
2694                        if (endp != NULL)
2695                                *endp = p;
2696                        if (s->s_ruleset > 0)
2697                                ruleset = s->s_ruleset;
2698                        else if ((ruleset = --nextruleset) < MAXRWSETS / 2)
2699                        {
2700                                syserr("%s: too many named rulesets (%d max)",
2701                                        q, MAXRWSETS / 2);
2702                                ruleset = -1;
2703                        }
2704                }
2705                if (s->s_ruleset > 0 && ruleset >= 0 && ruleset != s->s_ruleset)
2706                {
2707                        syserr("%s: ruleset changed value (old %d, new %d)",
2708                                q, s->s_ruleset, ruleset);
2709                        ruleset = s->s_ruleset;
2710                }
2711                else if (ruleset > 0)
2712                {
2713                        s->s_ruleset = ruleset;
2714                }
2715        }
2716        return ruleset;
2717}
2718/*
2719**  INITTIMEOUTS -- parse and set timeout values
2720**
2721**      Parameters:
2722**              val -- a pointer to the values.  If NULL, do initial
2723**                      settings.
2724**
2725**      Returns:
2726**              none.
2727**
2728**      Side Effects:
2729**              Initializes the TimeOuts structure
2730*/
2731
2732#define SECONDS
2733#define MINUTES * 60
2734#define HOUR    * 3600
2735
2736void
2737inittimeouts(val)
2738        register char *val;
2739{
2740        register char *p;
2741        extern time_t convtime __P((char *, char));
2742
2743        if (tTd(37, 2))
2744                printf("inittimeouts(%s)\n", val == NULL ? "<NULL>" : val);
2745        if (val == NULL)
2746        {
2747                TimeOuts.to_connect = (time_t) 0 SECONDS;
2748                TimeOuts.to_initial = (time_t) 5 MINUTES;
2749                TimeOuts.to_helo = (time_t) 5 MINUTES;
2750                TimeOuts.to_mail = (time_t) 10 MINUTES;
2751                TimeOuts.to_rcpt = (time_t) 1 HOUR;
2752                TimeOuts.to_datainit = (time_t) 5 MINUTES;
2753                TimeOuts.to_datablock = (time_t) 1 HOUR;
2754                TimeOuts.to_datafinal = (time_t) 1 HOUR;
2755                TimeOuts.to_rset = (time_t) 5 MINUTES;
2756                TimeOuts.to_quit = (time_t) 2 MINUTES;
2757                TimeOuts.to_nextcommand = (time_t) 1 HOUR;
2758                TimeOuts.to_miscshort = (time_t) 2 MINUTES;
2759#if IDENTPROTO
2760                TimeOuts.to_ident = (time_t) 30 SECONDS;
2761#else
2762                TimeOuts.to_ident = (time_t) 0 SECONDS;
2763#endif
2764                TimeOuts.to_fileopen = (time_t) 60 SECONDS;
2765                if (tTd(37, 5))
2766                {
2767                        printf("Timeouts:\n");
2768                        printf("  connect = %ld\n", (long)TimeOuts.to_connect);
2769                        printf("  initial = %ld\n", (long)TimeOuts.to_initial);
2770                        printf("  helo = %ld\n", (long)TimeOuts.to_helo);
2771                        printf("  mail = %ld\n", (long)TimeOuts.to_mail);
2772                        printf("  rcpt = %ld\n", (long)TimeOuts.to_rcpt);
2773                        printf("  datainit = %ld\n", (long)TimeOuts.to_datainit);
2774                        printf("  datablock = %ld\n", (long)TimeOuts.to_datablock);
2775                        printf("  datafinal = %ld\n", (long)TimeOuts.to_datafinal);
2776                        printf("  rset = %ld\n", (long)TimeOuts.to_rset);
2777                        printf("  quit = %ld\n", (long)TimeOuts.to_quit);
2778                        printf("  nextcommand = %ld\n", (long)TimeOuts.to_nextcommand);
2779                        printf("  miscshort = %ld\n", (long)TimeOuts.to_miscshort);
2780                        printf("  ident = %ld\n", (long)TimeOuts.to_ident);
2781                        printf("  fileopen = %ld\n", (long)TimeOuts.to_fileopen);
2782                }
2783                return;
2784        }
2785
2786        for (;; val = p)
2787        {
2788                while (isascii(*val) && isspace(*val))
2789                        val++;
2790                if (*val == '\0')
2791                        break;
2792                for (p = val; *p != '\0' && *p != ','; p++)
2793                        continue;
2794                if (*p != '\0')
2795                        *p++ = '\0';
2796
2797                if (isascii(*val) && isdigit(*val))
2798                {
2799                        /* old syntax -- set everything */
2800                        TimeOuts.to_mail = convtime(val, 'm');
2801                        TimeOuts.to_rcpt = TimeOuts.to_mail;
2802                        TimeOuts.to_datainit = TimeOuts.to_mail;
2803                        TimeOuts.to_datablock = TimeOuts.to_mail;
2804                        TimeOuts.to_datafinal = TimeOuts.to_mail;
2805                        TimeOuts.to_nextcommand = TimeOuts.to_mail;
2806                        continue;
2807                }
2808                else
2809                {
2810                        register char *q = strchr(val, ':');
2811
2812                        if (q == NULL && (q = strchr(val, '=')) == NULL)
2813                        {
2814                                /* syntax error */
2815                                continue;
2816                        }
2817                        *q++ = '\0';
2818                        settimeout(val, q);
2819                }
2820        }
2821}
2822/*
2823**  SETTIMEOUT -- set an individual timeout
2824**
2825**      Parameters:
2826**              name -- the name of the timeout.
2827**              val -- the value of the timeout.
2828**
2829**      Returns:
2830**              none.
2831*/
2832
2833void
2834settimeout(name, val)
2835        char *name;
2836        char *val;
2837{
2838        register char *p;
2839        time_t to;
2840        extern time_t convtime __P((char *, char));
2841
2842        if (tTd(37, 2))
2843                printf("settimeout(%s = %s)\n", name, val);
2844
2845        to = convtime(val, 'm');
2846        p = strchr(name, '.');
2847        if (p != NULL)
2848                *p++ = '\0';
2849
2850        if (strcasecmp(name, "initial") == 0)
2851                TimeOuts.to_initial = to;
2852        else if (strcasecmp(name, "mail") == 0)
2853                TimeOuts.to_mail = to;
2854        else if (strcasecmp(name, "rcpt") == 0)
2855                TimeOuts.to_rcpt = to;
2856        else if (strcasecmp(name, "datainit") == 0)
2857                TimeOuts.to_datainit = to;
2858        else if (strcasecmp(name, "datablock") == 0)
2859                TimeOuts.to_datablock = to;
2860        else if (strcasecmp(name, "datafinal") == 0)
2861                TimeOuts.to_datafinal = to;
2862        else if (strcasecmp(name, "command") == 0)
2863                TimeOuts.to_nextcommand = to;
2864        else if (strcasecmp(name, "rset") == 0)
2865                TimeOuts.to_rset = to;
2866        else if (strcasecmp(name, "helo") == 0)
2867                TimeOuts.to_helo = to;
2868        else if (strcasecmp(name, "quit") == 0)
2869                TimeOuts.to_quit = to;
2870        else if (strcasecmp(name, "misc") == 0)
2871                TimeOuts.to_miscshort = to;
2872        else if (strcasecmp(name, "ident") == 0)
2873                TimeOuts.to_ident = to;
2874        else if (strcasecmp(name, "fileopen") == 0)
2875                TimeOuts.to_fileopen = to;
2876        else if (strcasecmp(name, "connect") == 0)
2877                TimeOuts.to_connect = to;
2878        else if (strcasecmp(name, "iconnect") == 0)
2879                TimeOuts.to_iconnect = to;
2880        else if (strcasecmp(name, "queuewarn") == 0)
2881        {
2882                to = convtime(val, 'h');
2883                if (p == NULL || strcmp(p, "*") == 0)
2884                {
2885                        TimeOuts.to_q_warning[TOC_NORMAL] = to;
2886                        TimeOuts.to_q_warning[TOC_URGENT] = to;
2887                        TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2888                }
2889                else if (strcasecmp(p, "normal") == 0)
2890                        TimeOuts.to_q_warning[TOC_NORMAL] = to;
2891                else if (strcasecmp(p, "urgent") == 0)
2892                        TimeOuts.to_q_warning[TOC_URGENT] = to;
2893                else if (strcasecmp(p, "non-urgent") == 0)
2894                        TimeOuts.to_q_warning[TOC_NONURGENT] = to;
2895                else
2896                        syserr("settimeout: invalid queuewarn subtimeout %s", p);
2897        }
2898        else if (strcasecmp(name, "queuereturn") == 0)
2899        {
2900                to = convtime(val, 'd');
2901                if (p == NULL || strcmp(p, "*") == 0)
2902                {
2903                        TimeOuts.to_q_return[TOC_NORMAL] = to;
2904                        TimeOuts.to_q_return[TOC_URGENT] = to;
2905                        TimeOuts.to_q_return[TOC_NONURGENT] = to;
2906                }
2907                else if (strcasecmp(p, "normal") == 0)
2908                        TimeOuts.to_q_return[TOC_NORMAL] = to;
2909                else if (strcasecmp(p, "urgent") == 0)
2910                        TimeOuts.to_q_return[TOC_URGENT] = to;
2911                else if (strcasecmp(p, "non-urgent") == 0)
2912                        TimeOuts.to_q_return[TOC_NONURGENT] = to;
2913                else
2914                        syserr("settimeout: invalid queuereturn subtimeout %s", p);
2915        }
2916        else if (strcasecmp(name, "hoststatus") == 0)
2917                MciInfoTimeout = convtime(val, 'm');
2918        else
2919                syserr("settimeout: invalid timeout %s", name);
2920}
Note: See TracBrowser for help on using the repository browser.