1 /*
2 * Copyright (c) 1983 Eric P. Allman
3 * Copyright (c) 1988, 1993
4 * The Regents of the University of California. All rights reserved.
5 *
6 * Redistribution and use in source and binary forms, with or without
7 * modification, are permitted provided that the following conditions
8 * are met:
9 * 1. Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * 2. Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * 3. All advertising materials mentioning features or use of this software
15 * must display the following acknowledgement:
16 * This product includes software developed by the University of
17 * California, Berkeley and its contributors.
18 * 4. Neither the name of the University nor the names of its contributors
19 * may be used to endorse or promote products derived from this software
20 * without specific prior written permission.
21 *
22 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
23 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
26 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32 * SUCH DAMAGE.
33 */
34
35 #ifndef lint
36 static char sccsid[] = "@(#)readcf.c 8.23 (Berkeley) 3/18/94";
37 #endif /* not lint */
38
39 # include "sendmail.h"
40 # include <pwd.h>
41 # include <grp.h>
42 #if NAMED_BIND
43 # include <arpa/nameser.h>
44 # include <resolv.h>
45 #endif
46
47 /*
48 ** READCF -- read control file.
49 **
50 ** This routine reads the control file and builds the internal
51 ** form.
52 **
53 ** The file is formatted as a sequence of lines, each taken
54 ** atomically. The first character of each line describes how
55 ** the line is to be interpreted. The lines are:
56 ** Dxval Define macro x to have value val.
57 ** Cxword Put word into class x.
58 ** Fxfile [fmt] Read file for lines to put into
59 ** class x. Use scanf string 'fmt'
60 ** or "%s" if not present. Fmt should
61 ** only produce one string-valued result.
62 ** Hname: value Define header with field-name 'name'
63 ** and value as specified; this will be
64 ** macro expanded immediately before
65 ** use.
66 ** Sn Use rewriting set n.
67 ** Rlhs rhs Rewrite addresses that match lhs to
68 ** be rhs.
69 ** Mn arg=val... Define mailer. n is the internal name.
70 ** Args specify mailer parameters.
71 ** Oxvalue Set option x to value.
72 ** Pname=value Set precedence name to value.
73 ** Vversioncode[/vendorcode]
74 ** Version level/vendor name of
75 ** configuration syntax.
76 ** Kmapname mapclass arguments....
77 ** Define keyed lookup of a given class.
78 ** Arguments are class dependent.
79 **
80 ** Parameters:
81 ** cfname -- control file name.
82 ** safe -- TRUE if this is the system config file;
83 ** FALSE otherwise.
84 ** e -- the main envelope.
85 **
86 ** Returns:
87 ** none.
88 **
89 ** Side Effects:
90 ** Builds several internal tables.
91 */
92
readcf(cfname,safe,e)93 readcf(cfname, safe, e)
94 char *cfname;
95 bool safe;
96 register ENVELOPE *e;
97 {
98 FILE *cf;
99 int ruleset = 0;
100 char *q;
101 struct rewrite *rwp = NULL;
102 char *bp;
103 auto char *ep;
104 int nfuzzy;
105 char *file;
106 bool optional;
107 char buf[MAXLINE];
108 register char *p;
109 extern char **copyplist();
110 struct stat statb;
111 char exbuf[MAXLINE];
112 char pvpbuf[MAXLINE + MAXATOM];
113 extern char *munchstring();
114 extern void makemapentry();
115
116 FileName = cfname;
117 LineNumber = 0;
118
119 cf = fopen(cfname, "r");
120 if (cf == NULL)
121 {
122 syserr("cannot open");
123 exit(EX_OSFILE);
124 }
125
126 if (fstat(fileno(cf), &statb) < 0)
127 {
128 syserr("cannot fstat");
129 exit(EX_OSFILE);
130 }
131
132 if (!S_ISREG(statb.st_mode))
133 {
134 syserr("not a plain file");
135 exit(EX_OSFILE);
136 }
137
138 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode))
139 {
140 if (OpMode == MD_DAEMON || OpMode == MD_FREEZE)
141 fprintf(stderr, "%s: WARNING: dangerous write permissions\n",
142 FileName);
143 #ifdef LOG
144 if (LogLevel > 0)
145 syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions",
146 FileName);
147 #endif
148 }
149
150 #ifdef XLA
151 xla_zero();
152 #endif
153
154 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL)
155 {
156 if (bp[0] == '#')
157 {
158 if (bp != buf)
159 free(bp);
160 continue;
161 }
162
163 /* map $ into \201 for macro expansion */
164 for (p = bp; *p != '\0'; p++)
165 {
166 if (*p == '#' && p > bp && ConfigLevel >= 3)
167 {
168 /* this is an on-line comment */
169 register char *e;
170
171 switch (*--p & 0377)
172 {
173 case MACROEXPAND:
174 /* it's from $# -- let it go through */
175 p++;
176 break;
177
178 case '\\':
179 /* it's backslash escaped */
180 (void) strcpy(p, p + 1);
181 break;
182
183 default:
184 /* delete preceeding white space */
185 while (isascii(*p) && isspace(*p) && p > bp)
186 p--;
187 if ((e = strchr(++p, '\n')) != NULL)
188 (void) strcpy(p, e);
189 else
190 p[0] = p[1] = '\0';
191 break;
192 }
193 continue;
194 }
195
196 if (*p != '$')
197 continue;
198
199 if (p[1] == '$')
200 {
201 /* actual dollar sign.... */
202 (void) strcpy(p, p + 1);
203 continue;
204 }
205
206 /* convert to macro expansion character */
207 *p = MACROEXPAND;
208 }
209
210 /* interpret this line */
211 errno = 0;
212 switch (bp[0])
213 {
214 case '\0':
215 case '#': /* comment */
216 break;
217
218 case 'R': /* rewriting rule */
219 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++)
220 continue;
221
222 if (*p == '\0')
223 {
224 syserr("invalid rewrite line \"%s\" (tab expected)", bp);
225 break;
226 }
227
228 /* allocate space for the rule header */
229 if (rwp == NULL)
230 {
231 RewriteRules[ruleset] = rwp =
232 (struct rewrite *) xalloc(sizeof *rwp);
233 }
234 else
235 {
236 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp);
237 rwp = rwp->r_next;
238 }
239 rwp->r_next = NULL;
240
241 /* expand and save the LHS */
242 *p = '\0';
243 expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e);
244 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf,
245 sizeof pvpbuf, NULL);
246 nfuzzy = 0;
247 if (rwp->r_lhs != NULL)
248 {
249 register char **ap;
250
251 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE);
252
253 /* count the number of fuzzy matches in LHS */
254 for (ap = rwp->r_lhs; *ap != NULL; ap++)
255 {
256 char *botch;
257
258 botch = NULL;
259 switch (**ap & 0377)
260 {
261 case MATCHZANY:
262 case MATCHANY:
263 case MATCHONE:
264 case MATCHCLASS:
265 case MATCHNCLASS:
266 nfuzzy++;
267 break;
268
269 case MATCHREPL:
270 botch = "$0-$9";
271 break;
272
273 case CANONNET:
274 botch = "$#";
275 break;
276
277 case CANONUSER:
278 botch = "$:";
279 break;
280
281 case CALLSUBR:
282 botch = "$>";
283 break;
284
285 case CONDIF:
286 botch = "$?";
287 break;
288
289 case CONDELSE:
290 botch = "$|";
291 break;
292
293 case CONDFI:
294 botch = "$.";
295 break;
296
297 case HOSTBEGIN:
298 botch = "$[";
299 break;
300
301 case HOSTEND:
302 botch = "$]";
303 break;
304
305 case LOOKUPBEGIN:
306 botch = "$(";
307 break;
308
309 case LOOKUPEND:
310 botch = "$)";
311 break;
312 }
313 if (botch != NULL)
314 syserr("Inappropriate use of %s on LHS",
315 botch);
316 }
317 }
318 else
319 syserr("R line: null LHS");
320
321 /* expand and save the RHS */
322 while (*++p == '\t')
323 continue;
324 q = p;
325 while (*p != '\0' && *p != '\t')
326 p++;
327 *p = '\0';
328 expand(q, exbuf, &exbuf[sizeof exbuf], e);
329 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf,
330 sizeof pvpbuf, NULL);
331 if (rwp->r_rhs != NULL)
332 {
333 register char **ap;
334
335 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE);
336
337 /* check no out-of-bounds replacements */
338 nfuzzy += '0';
339 for (ap = rwp->r_rhs; *ap != NULL; ap++)
340 {
341 char *botch;
342
343 botch = NULL;
344 switch (**ap & 0377)
345 {
346 case MATCHREPL:
347 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy)
348 {
349 syserr("replacement $%c out of bounds",
350 (*ap)[1]);
351 }
352 break;
353
354 case MATCHZANY:
355 botch = "$*";
356 break;
357
358 case MATCHANY:
359 botch = "$+";
360 break;
361
362 case MATCHONE:
363 botch = "$-";
364 break;
365
366 case MATCHCLASS:
367 botch = "$=";
368 break;
369
370 case MATCHNCLASS:
371 botch = "$~";
372 break;
373 }
374 if (botch != NULL)
375 syserr("Inappropriate use of %s on RHS",
376 botch);
377 }
378 }
379 else
380 syserr("R line: null RHS");
381 break;
382
383 case 'S': /* select rewriting set */
384 for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
385 continue;
386 if (!isascii(*p) || !isdigit(*p))
387 {
388 syserr("invalid argument to S line: \"%.20s\"",
389 &bp[1]);
390 break;
391 }
392 ruleset = atoi(p);
393 if (ruleset >= MAXRWSETS || ruleset < 0)
394 {
395 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS);
396 ruleset = 0;
397 }
398 rwp = NULL;
399 break;
400
401 case 'D': /* macro definition */
402 p = munchstring(&bp[2], NULL);
403 define(bp[1], newstr(p), e);
404 break;
405
406 case 'H': /* required header line */
407 (void) chompheader(&bp[1], TRUE, e);
408 break;
409
410 case 'C': /* word class */
411 /* scan the list of words and set class for all */
412 expand(&bp[2], exbuf, &exbuf[sizeof exbuf], e);
413 for (p = exbuf; *p != '\0'; )
414 {
415 register char *wd;
416 char delim;
417
418 while (*p != '\0' && isascii(*p) && isspace(*p))
419 p++;
420 wd = p;
421 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
422 p++;
423 delim = *p;
424 *p = '\0';
425 if (wd[0] != '\0')
426 setclass(bp[1], wd);
427 *p = delim;
428 }
429 break;
430
431 case 'F': /* word class from file */
432 for (p = &bp[2]; isascii(*p) && isspace(*p); )
433 p++;
434 if (p[0] == '-' && p[1] == 'o')
435 {
436 optional = TRUE;
437 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
438 p++;
439 while (isascii(*p) && isspace(*p))
440 *p++;
441 }
442 else
443 optional = FALSE;
444 file = p;
445 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
446 p++;
447 if (*p == '\0')
448 p = "%s";
449 else
450 {
451 *p = '\0';
452 while (isascii(*++p) && isspace(*p))
453 continue;
454 }
455 fileclass(bp[1], file, p, safe, optional);
456 break;
457
458 #ifdef XLA
459 case 'L': /* extended load average description */
460 xla_init(&bp[1]);
461 break;
462 #endif
463
464 case 'M': /* define mailer */
465 makemailer(&bp[1]);
466 break;
467
468 case 'O': /* set option */
469 setoption(bp[1], &bp[2], safe, FALSE, e);
470 break;
471
472 case 'P': /* set precedence */
473 if (NumPriorities >= MAXPRIORITIES)
474 {
475 toomany('P', MAXPRIORITIES);
476 break;
477 }
478 for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++)
479 continue;
480 if (*p == '\0')
481 goto badline;
482 *p = '\0';
483 Priorities[NumPriorities].pri_name = newstr(&bp[1]);
484 Priorities[NumPriorities].pri_val = atoi(++p);
485 NumPriorities++;
486 break;
487
488 case 'T': /* trusted user(s) */
489 /* this option is obsolete, but will be ignored */
490 break;
491
492 case 'V': /* configuration syntax version */
493 for (p = &bp[1]; isascii(*p) && isspace(*p); p++)
494 continue;
495 if (!isascii(*p) || !isdigit(*p))
496 {
497 syserr("invalid argument to V line: \"%.20s\"",
498 &bp[1]);
499 break;
500 }
501 ConfigLevel = strtol(p, &ep, 10);
502 if (ConfigLevel >= 5)
503 {
504 /* level 5 configs have short name in $w */
505 p = macvalue('w', e);
506 if (p != NULL && (p = strchr(p, '.')) != NULL)
507 *p = '\0';
508 }
509 if (*ep++ == '/')
510 {
511 /* extract vendor code */
512 for (p = ep; isascii(*p) && isalpha(*p); )
513 p++;
514 *p = '\0';
515
516 if (!setvendor(ep))
517 syserr("invalid V line vendor code: \"%s\"",
518 ep);
519 }
520 break;
521
522 case 'K':
523 makemapentry(&bp[1]);
524 break;
525
526 default:
527 badline:
528 syserr("unknown control line \"%s\"", bp);
529 }
530 if (bp != buf)
531 free(bp);
532 }
533 if (ferror(cf))
534 {
535 syserr("I/O read error", cfname);
536 exit(EX_OSFILE);
537 }
538 fclose(cf);
539 FileName = NULL;
540
541 if (stab("host", ST_MAP, ST_FIND) == NULL)
542 {
543 /* user didn't initialize: set up host map */
544 strcpy(buf, "host host");
545 #if NAMED_BIND
546 if (ConfigLevel >= 2)
547 strcat(buf, " -a.");
548 #endif
549 makemapentry(buf);
550 }
551 }
552 /*
553 ** TOOMANY -- signal too many of some option
554 **
555 ** Parameters:
556 ** id -- the id of the error line
557 ** maxcnt -- the maximum possible values
558 **
559 ** Returns:
560 ** none.
561 **
562 ** Side Effects:
563 ** gives a syserr.
564 */
565
toomany(id,maxcnt)566 toomany(id, maxcnt)
567 char id;
568 int maxcnt;
569 {
570 syserr("too many %c lines, %d max", id, maxcnt);
571 }
572 /*
573 ** FILECLASS -- read members of a class from a file
574 **
575 ** Parameters:
576 ** class -- class to define.
577 ** filename -- name of file to read.
578 ** fmt -- scanf string to use for match.
579 ** safe -- if set, this is a safe read.
580 ** optional -- if set, it is not an error for the file to
581 ** not exist.
582 **
583 ** Returns:
584 ** none
585 **
586 ** Side Effects:
587 **
588 ** puts all lines in filename that match a scanf into
589 ** the named class.
590 */
591
fileclass(class,filename,fmt,safe,optional)592 fileclass(class, filename, fmt, safe, optional)
593 int class;
594 char *filename;
595 char *fmt;
596 bool safe;
597 bool optional;
598 {
599 FILE *f;
600 struct stat stbuf;
601 char buf[MAXLINE];
602
603 if (tTd(37, 2))
604 printf("fileclass(%s, fmt=%s)\n", filename, fmt);
605
606 if (filename[0] == '|')
607 {
608 syserr("fileclass: pipes (F%c%s) not supported due to security problems",
609 class, filename);
610 return;
611 }
612 if (stat(filename, &stbuf) < 0)
613 {
614 if (tTd(37, 2))
615 printf(" cannot stat (%s)\n", errstring(errno));
616 if (!optional)
617 syserr("fileclass: cannot stat %s", filename);
618 return;
619 }
620 if (!S_ISREG(stbuf.st_mode))
621 {
622 syserr("fileclass: %s not a regular file", filename);
623 return;
624 }
625 if (!safe && access(filename, R_OK) < 0)
626 {
627 syserr("fileclass: access denied on %s", filename);
628 return;
629 }
630 f = fopen(filename, "r");
631 if (f == NULL)
632 {
633 syserr("fileclass: cannot open %s", filename);
634 return;
635 }
636
637 while (fgets(buf, sizeof buf, f) != NULL)
638 {
639 register STAB *s;
640 register char *p;
641 # ifdef SCANF
642 char wordbuf[MAXNAME+1];
643
644 if (sscanf(buf, fmt, wordbuf) != 1)
645 continue;
646 p = wordbuf;
647 # else /* SCANF */
648 p = buf;
649 # endif /* SCANF */
650
651 /*
652 ** Break up the match into words.
653 */
654
655 while (*p != '\0')
656 {
657 register char *q;
658
659 /* strip leading spaces */
660 while (isascii(*p) && isspace(*p))
661 p++;
662 if (*p == '\0')
663 break;
664
665 /* find the end of the word */
666 q = p;
667 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
668 p++;
669 if (*p != '\0')
670 *p++ = '\0';
671
672 /* enter the word in the symbol table */
673 setclass(class, q);
674 }
675 }
676
677 (void) fclose(f);
678 }
679 /*
680 ** MAKEMAILER -- define a new mailer.
681 **
682 ** Parameters:
683 ** line -- description of mailer. This is in labeled
684 ** fields. The fields are:
685 ** P -- the path to the mailer
686 ** F -- the flags associated with the mailer
687 ** A -- the argv for this mailer
688 ** S -- the sender rewriting set
689 ** R -- the recipient rewriting set
690 ** E -- the eol string
691 ** The first word is the canonical name of the mailer.
692 **
693 ** Returns:
694 ** none.
695 **
696 ** Side Effects:
697 ** enters the mailer into the mailer table.
698 */
699
makemailer(line)700 makemailer(line)
701 char *line;
702 {
703 register char *p;
704 register struct mailer *m;
705 register STAB *s;
706 int i;
707 char fcode;
708 auto char *endp;
709 extern int NextMailer;
710 extern char **makeargv();
711 extern char *munchstring();
712 extern long atol();
713
714 /* allocate a mailer and set up defaults */
715 m = (struct mailer *) xalloc(sizeof *m);
716 bzero((char *) m, sizeof *m);
717 m->m_eol = "\n";
718
719 /* collect the mailer name */
720 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++)
721 continue;
722 if (*p != '\0')
723 *p++ = '\0';
724 m->m_name = newstr(line);
725
726 /* now scan through and assign info from the fields */
727 while (*p != '\0')
728 {
729 auto char *delimptr;
730
731 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p))))
732 p++;
733
734 /* p now points to field code */
735 fcode = *p;
736 while (*p != '\0' && *p != '=' && *p != ',')
737 p++;
738 if (*p++ != '=')
739 {
740 syserr("mailer %s: `=' expected", m->m_name);
741 return;
742 }
743 while (isascii(*p) && isspace(*p))
744 p++;
745
746 /* p now points to the field body */
747 p = munchstring(p, &delimptr);
748
749 /* install the field into the mailer struct */
750 switch (fcode)
751 {
752 case 'P': /* pathname */
753 m->m_mailer = newstr(p);
754 break;
755
756 case 'F': /* flags */
757 for (; *p != '\0'; p++)
758 if (!(isascii(*p) && isspace(*p)))
759 setbitn(*p, m->m_flags);
760 break;
761
762 case 'S': /* sender rewriting ruleset */
763 case 'R': /* recipient rewriting ruleset */
764 i = strtol(p, &endp, 10);
765 if (i < 0 || i >= MAXRWSETS)
766 {
767 syserr("invalid rewrite set, %d max", MAXRWSETS);
768 return;
769 }
770 if (fcode == 'S')
771 m->m_sh_rwset = m->m_se_rwset = i;
772 else
773 m->m_rh_rwset = m->m_re_rwset = i;
774
775 p = endp;
776 if (*p++ == '/')
777 {
778 i = strtol(p, NULL, 10);
779 if (i < 0 || i >= MAXRWSETS)
780 {
781 syserr("invalid rewrite set, %d max",
782 MAXRWSETS);
783 return;
784 }
785 if (fcode == 'S')
786 m->m_sh_rwset = i;
787 else
788 m->m_rh_rwset = i;
789 }
790 break;
791
792 case 'E': /* end of line string */
793 m->m_eol = newstr(p);
794 break;
795
796 case 'A': /* argument vector */
797 m->m_argv = makeargv(p);
798 break;
799
800 case 'M': /* maximum message size */
801 m->m_maxsize = atol(p);
802 break;
803
804 case 'L': /* maximum line length */
805 m->m_linelimit = atoi(p);
806 break;
807
808 case 'D': /* working directory */
809 m->m_execdir = newstr(p);
810 break;
811 }
812
813 p = delimptr;
814 }
815
816 /* do some heuristic cleanup for back compatibility */
817 if (bitnset(M_LIMITS, m->m_flags))
818 {
819 if (m->m_linelimit == 0)
820 m->m_linelimit = SMTPLINELIM;
821 if (ConfigLevel < 2)
822 setbitn(M_7BITS, m->m_flags);
823 }
824
825 /* do some rationality checking */
826 if (m->m_argv == NULL)
827 {
828 syserr("M%s: A= argument required", m->m_name);
829 return;
830 }
831 if (m->m_mailer == NULL)
832 {
833 syserr("M%s: P= argument required", m->m_name);
834 return;
835 }
836
837 if (NextMailer >= MAXMAILERS)
838 {
839 syserr("too many mailers defined (%d max)", MAXMAILERS);
840 return;
841 }
842
843 s = stab(m->m_name, ST_MAILER, ST_ENTER);
844 if (s->s_mailer != NULL)
845 {
846 i = s->s_mailer->m_mno;
847 free(s->s_mailer);
848 }
849 else
850 {
851 i = NextMailer++;
852 }
853 Mailer[i] = s->s_mailer = m;
854 m->m_mno = i;
855 }
856 /*
857 ** MUNCHSTRING -- translate a string into internal form.
858 **
859 ** Parameters:
860 ** p -- the string to munch.
861 ** delimptr -- if non-NULL, set to the pointer of the
862 ** field delimiter character.
863 **
864 ** Returns:
865 ** the munched string.
866 */
867
868 char *
munchstring(p,delimptr)869 munchstring(p, delimptr)
870 register char *p;
871 char **delimptr;
872 {
873 register char *q;
874 bool backslash = FALSE;
875 bool quotemode = FALSE;
876 static char buf[MAXLINE];
877
878 for (q = buf; *p != '\0'; p++)
879 {
880 if (backslash)
881 {
882 /* everything is roughly literal */
883 backslash = FALSE;
884 switch (*p)
885 {
886 case 'r': /* carriage return */
887 *q++ = '\r';
888 continue;
889
890 case 'n': /* newline */
891 *q++ = '\n';
892 continue;
893
894 case 'f': /* form feed */
895 *q++ = '\f';
896 continue;
897
898 case 'b': /* backspace */
899 *q++ = '\b';
900 continue;
901 }
902 *q++ = *p;
903 }
904 else
905 {
906 if (*p == '\\')
907 backslash = TRUE;
908 else if (*p == '"')
909 quotemode = !quotemode;
910 else if (quotemode || *p != ',')
911 *q++ = *p;
912 else
913 break;
914 }
915 }
916
917 if (delimptr != NULL)
918 *delimptr = p;
919 *q++ = '\0';
920 return (buf);
921 }
922 /*
923 ** MAKEARGV -- break up a string into words
924 **
925 ** Parameters:
926 ** p -- the string to break up.
927 **
928 ** Returns:
929 ** a char **argv (dynamically allocated)
930 **
931 ** Side Effects:
932 ** munges p.
933 */
934
935 char **
makeargv(p)936 makeargv(p)
937 register char *p;
938 {
939 char *q;
940 int i;
941 char **avp;
942 char *argv[MAXPV + 1];
943
944 /* take apart the words */
945 i = 0;
946 while (*p != '\0' && i < MAXPV)
947 {
948 q = p;
949 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
950 p++;
951 while (isascii(*p) && isspace(*p))
952 *p++ = '\0';
953 argv[i++] = newstr(q);
954 }
955 argv[i++] = NULL;
956
957 /* now make a copy of the argv */
958 avp = (char **) xalloc(sizeof *avp * i);
959 bcopy((char *) argv, (char *) avp, sizeof *avp * i);
960
961 return (avp);
962 }
963 /*
964 ** PRINTRULES -- print rewrite rules (for debugging)
965 **
966 ** Parameters:
967 ** none.
968 **
969 ** Returns:
970 ** none.
971 **
972 ** Side Effects:
973 ** prints rewrite rules.
974 */
975
printrules()976 printrules()
977 {
978 register struct rewrite *rwp;
979 register int ruleset;
980
981 for (ruleset = 0; ruleset < 10; ruleset++)
982 {
983 if (RewriteRules[ruleset] == NULL)
984 continue;
985 printf("\n----Rule Set %d:", ruleset);
986
987 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next)
988 {
989 printf("\nLHS:");
990 printav(rwp->r_lhs);
991 printf("RHS:");
992 printav(rwp->r_rhs);
993 }
994 }
995 }
996
997 /*
998 ** SETOPTION -- set global processing option
999 **
1000 ** Parameters:
1001 ** opt -- option name.
1002 ** val -- option value (as a text string).
1003 ** safe -- set if this came from a configuration file.
1004 ** Some options (if set from the command line) will
1005 ** reset the user id to avoid security problems.
1006 ** sticky -- if set, don't let other setoptions override
1007 ** this value.
1008 ** e -- the main envelope.
1009 **
1010 ** Returns:
1011 ** none.
1012 **
1013 ** Side Effects:
1014 ** Sets options as implied by the arguments.
1015 */
1016
1017 static BITMAP StickyOpt; /* set if option is stuck */
1018
1019
1020 #if NAMED_BIND
1021
1022 struct resolverflags
1023 {
1024 char *rf_name; /* name of the flag */
1025 long rf_bits; /* bits to set/clear */
1026 } ResolverFlags[] =
1027 {
1028 "debug", RES_DEBUG,
1029 "aaonly", RES_AAONLY,
1030 "usevc", RES_USEVC,
1031 "primary", RES_PRIMARY,
1032 "igntc", RES_IGNTC,
1033 "recurse", RES_RECURSE,
1034 "defnames", RES_DEFNAMES,
1035 "stayopen", RES_STAYOPEN,
1036 "dnsrch", RES_DNSRCH,
1037 "true", 0, /* to avoid error on old syntax */
1038 NULL, 0
1039 };
1040
1041 #endif
1042
setoption(opt,val,safe,sticky,e)1043 setoption(opt, val, safe, sticky, e)
1044 char opt;
1045 char *val;
1046 bool safe;
1047 bool sticky;
1048 register ENVELOPE *e;
1049 {
1050 register char *p;
1051 extern bool atobool();
1052 extern time_t convtime();
1053 extern int QueueLA;
1054 extern int RefuseLA;
1055 extern bool Warn_Q_option;
1056 extern bool trusteduser();
1057
1058 if (tTd(37, 1))
1059 printf("setoption %c=%s", opt, val);
1060
1061 /*
1062 ** See if this option is preset for us.
1063 */
1064
1065 if (!sticky && bitnset(opt, StickyOpt))
1066 {
1067 if (tTd(37, 1))
1068 printf(" (ignored)\n");
1069 return;
1070 }
1071
1072 /*
1073 ** Check to see if this option can be specified by this user.
1074 */
1075
1076 if (!safe && RealUid == 0)
1077 safe = TRUE;
1078 if (!safe && strchr("bCdeijLmoprsvw7", opt) == NULL)
1079 {
1080 if (opt != 'M' || (val[0] != 'r' && val[0] != 's'))
1081 {
1082 if (tTd(37, 1))
1083 printf(" (unsafe)");
1084 if (RealUid != geteuid())
1085 {
1086 if (tTd(37, 1))
1087 printf("(Resetting uid)");
1088 (void) setgid(RealGid);
1089 (void) setuid(RealUid);
1090 }
1091 }
1092 }
1093 if (tTd(37, 1))
1094 printf("\n");
1095
1096 switch (opt)
1097 {
1098 case '7': /* force seven-bit input */
1099 SevenBit = atobool(val);
1100 break;
1101
1102 case 'A': /* set default alias file */
1103 if (val[0] == '\0')
1104 setalias("aliases");
1105 else
1106 setalias(val);
1107 break;
1108
1109 case 'a': /* look N minutes for "@:@" in alias file */
1110 if (val[0] == '\0')
1111 SafeAlias = 5 * 60; /* five minutes */
1112 else
1113 SafeAlias = convtime(val, 'm');
1114 break;
1115
1116 case 'B': /* substitution for blank character */
1117 SpaceSub = val[0];
1118 if (SpaceSub == '\0')
1119 SpaceSub = ' ';
1120 break;
1121
1122 case 'b': /* min blocks free on queue fs/max msg size */
1123 p = strchr(val, '/');
1124 if (p != NULL)
1125 {
1126 *p++ = '\0';
1127 MaxMessageSize = atol(p);
1128 }
1129 MinBlocksFree = atol(val);
1130 break;
1131
1132 case 'c': /* don't connect to "expensive" mailers */
1133 NoConnect = atobool(val);
1134 break;
1135
1136 case 'C': /* checkpoint every N addresses */
1137 CheckpointInterval = atoi(val);
1138 break;
1139
1140 case 'd': /* delivery mode */
1141 switch (*val)
1142 {
1143 case '\0':
1144 e->e_sendmode = SM_DELIVER;
1145 break;
1146
1147 case SM_QUEUE: /* queue only */
1148 #ifndef QUEUE
1149 syserr("need QUEUE to set -odqueue");
1150 #endif /* QUEUE */
1151 /* fall through..... */
1152
1153 case SM_DELIVER: /* do everything */
1154 case SM_FORK: /* fork after verification */
1155 e->e_sendmode = *val;
1156 break;
1157
1158 default:
1159 syserr("Unknown delivery mode %c", *val);
1160 exit(EX_USAGE);
1161 }
1162 break;
1163
1164 case 'D': /* rebuild alias database as needed */
1165 AutoRebuild = atobool(val);
1166 break;
1167
1168 case 'E': /* error message header/header file */
1169 if (*val != '\0')
1170 ErrMsgFile = newstr(val);
1171 break;
1172
1173 case 'e': /* set error processing mode */
1174 switch (*val)
1175 {
1176 case EM_QUIET: /* be silent about it */
1177 case EM_MAIL: /* mail back */
1178 case EM_BERKNET: /* do berknet error processing */
1179 case EM_WRITE: /* write back (or mail) */
1180 HoldErrs = TRUE;
1181 /* fall through... */
1182
1183 case EM_PRINT: /* print errors normally (default) */
1184 e->e_errormode = *val;
1185 break;
1186 }
1187 break;
1188
1189 case 'F': /* file mode */
1190 FileMode = atooct(val) & 0777;
1191 break;
1192
1193 case 'f': /* save Unix-style From lines on front */
1194 SaveFrom = atobool(val);
1195 break;
1196
1197 case 'G': /* match recipients against GECOS field */
1198 MatchGecos = atobool(val);
1199 break;
1200
1201 case 'g': /* default gid */
1202 if (isascii(*val) && isdigit(*val))
1203 DefGid = atoi(val);
1204 else
1205 {
1206 register struct group *gr;
1207
1208 DefGid = -1;
1209 gr = getgrnam(val);
1210 if (gr == NULL)
1211 syserr("readcf: option g: unknown group %s", val);
1212 else
1213 DefGid = gr->gr_gid;
1214 }
1215 break;
1216
1217 case 'H': /* help file */
1218 if (val[0] == '\0')
1219 HelpFile = "sendmail.hf";
1220 else
1221 HelpFile = newstr(val);
1222 break;
1223
1224 case 'h': /* maximum hop count */
1225 MaxHopCount = atoi(val);
1226 break;
1227
1228 case 'I': /* use internet domain name server */
1229 #if NAMED_BIND
1230 UseNameServer = TRUE;
1231 for (p = val; *p != 0; )
1232 {
1233 bool clearmode;
1234 char *q;
1235 struct resolverflags *rfp;
1236
1237 while (*p == ' ')
1238 p++;
1239 if (*p == '\0')
1240 break;
1241 clearmode = FALSE;
1242 if (*p == '-')
1243 clearmode = TRUE;
1244 else if (*p != '+')
1245 p--;
1246 p++;
1247 q = p;
1248 while (*p != '\0' && !(isascii(*p) && isspace(*p)))
1249 p++;
1250 if (*p != '\0')
1251 *p++ = '\0';
1252 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++)
1253 {
1254 if (strcasecmp(q, rfp->rf_name) == 0)
1255 break;
1256 }
1257 if (rfp->rf_name == NULL)
1258 syserr("readcf: I option value %s unrecognized", q);
1259 else if (clearmode)
1260 _res.options &= ~rfp->rf_bits;
1261 else
1262 _res.options |= rfp->rf_bits;
1263 }
1264 if (tTd(8, 2))
1265 printf("_res.options = %x\n", _res.options);
1266 #else
1267 usrerr("name server (I option) specified but BIND not compiled in");
1268 #endif
1269 break;
1270
1271 case 'i': /* ignore dot lines in message */
1272 IgnrDot = atobool(val);
1273 break;
1274
1275 case 'j': /* send errors in MIME (RFC 1341) format */
1276 SendMIMEErrors = atobool(val);
1277 break;
1278
1279 case 'J': /* .forward search path */
1280 ForwardPath = newstr(val);
1281 break;
1282
1283 case 'k': /* connection cache size */
1284 MaxMciCache = atoi(val);
1285 if (MaxMciCache < 0)
1286 MaxMciCache = 0;
1287 break;
1288
1289 case 'K': /* connection cache timeout */
1290 MciCacheTimeout = convtime(val, 'm');
1291 break;
1292
1293 case 'l': /* use Errors-To: header */
1294 UseErrorsTo = atobool(val);
1295 break;
1296
1297 case 'L': /* log level */
1298 if (safe || LogLevel < atoi(val))
1299 LogLevel = atoi(val);
1300 break;
1301
1302 case 'M': /* define macro */
1303 define(val[0], newstr(&val[1]), CurEnv);
1304 sticky = FALSE;
1305 break;
1306
1307 case 'm': /* send to me too */
1308 MeToo = atobool(val);
1309 break;
1310
1311 case 'n': /* validate RHS in newaliases */
1312 CheckAliases = atobool(val);
1313 break;
1314
1315 /* 'N' available -- was "net name" */
1316
1317 case 'O': /* daemon options */
1318 setdaemonoptions(val);
1319 break;
1320
1321 case 'o': /* assume old style headers */
1322 if (atobool(val))
1323 CurEnv->e_flags |= EF_OLDSTYLE;
1324 else
1325 CurEnv->e_flags &= ~EF_OLDSTYLE;
1326 break;
1327
1328 case 'p': /* select privacy level */
1329 p = val;
1330 for (;;)
1331 {
1332 register struct prival *pv;
1333 extern struct prival PrivacyValues[];
1334
1335 while (isascii(*p) && (isspace(*p) || ispunct(*p)))
1336 p++;
1337 if (*p == '\0')
1338 break;
1339 val = p;
1340 while (isascii(*p) && isalnum(*p))
1341 p++;
1342 if (*p != '\0')
1343 *p++ = '\0';
1344
1345 for (pv = PrivacyValues; pv->pv_name != NULL; pv++)
1346 {
1347 if (strcasecmp(val, pv->pv_name) == 0)
1348 break;
1349 }
1350 if (pv->pv_name == NULL)
1351 syserr("readcf: Op line: %s unrecognized", val);
1352 PrivacyFlags |= pv->pv_flag;
1353 }
1354 break;
1355
1356 case 'P': /* postmaster copy address for returned mail */
1357 PostMasterCopy = newstr(val);
1358 break;
1359
1360 case 'q': /* slope of queue only function */
1361 QueueFactor = atoi(val);
1362 break;
1363
1364 case 'Q': /* queue directory */
1365 if (val[0] == '\0')
1366 QueueDir = "mqueue";
1367 else
1368 QueueDir = newstr(val);
1369 if (RealUid != 0 && !safe)
1370 Warn_Q_option = TRUE;
1371 break;
1372
1373 case 'R': /* don't prune routes */
1374 DontPruneRoutes = atobool(val);
1375 break;
1376
1377 case 'r': /* read timeout */
1378 settimeouts(val);
1379 break;
1380
1381 case 'S': /* status file */
1382 if (val[0] == '\0')
1383 StatFile = "sendmail.st";
1384 else
1385 StatFile = newstr(val);
1386 break;
1387
1388 case 's': /* be super safe, even if expensive */
1389 SuperSafe = atobool(val);
1390 break;
1391
1392 case 'T': /* queue timeout */
1393 p = strchr(val, '/');
1394 if (p != NULL)
1395 {
1396 *p++ = '\0';
1397 TimeOuts.to_q_warning = convtime(p, 'd');
1398 }
1399 TimeOuts.to_q_return = convtime(val, 'h');
1400 break;
1401
1402 case 't': /* time zone name */
1403 TimeZoneSpec = newstr(val);
1404 break;
1405
1406 case 'U': /* location of user database */
1407 UdbSpec = newstr(val);
1408 break;
1409
1410 case 'u': /* set default uid */
1411 if (isascii(*val) && isdigit(*val))
1412 DefUid = atoi(val);
1413 else
1414 {
1415 register struct passwd *pw;
1416
1417 DefUid = -1;
1418 pw = getpwnam(val);
1419 if (pw == NULL)
1420 syserr("readcf: option u: unknown user %s", val);
1421 else
1422 DefUid = pw->pw_uid;
1423 }
1424 setdefuser();
1425 break;
1426
1427 case 'V': /* fallback MX host */
1428 FallBackMX = newstr(val);
1429 break;
1430
1431 case 'v': /* run in verbose mode */
1432 Verbose = atobool(val);
1433 break;
1434
1435 case 'w': /* if we are best MX, try host directly */
1436 TryNullMXList = atobool(val);
1437 break;
1438
1439 /* 'W' available -- was wizard password */
1440
1441 case 'x': /* load avg at which to auto-queue msgs */
1442 QueueLA = atoi(val);
1443 break;
1444
1445 case 'X': /* load avg at which to auto-reject connections */
1446 RefuseLA = atoi(val);
1447 break;
1448
1449 case 'y': /* work recipient factor */
1450 WkRecipFact = atoi(val);
1451 break;
1452
1453 case 'Y': /* fork jobs during queue runs */
1454 ForkQueueRuns = atobool(val);
1455 break;
1456
1457 case 'z': /* work message class factor */
1458 WkClassFact = atoi(val);
1459 break;
1460
1461 case 'Z': /* work time factor */
1462 WkTimeFact = atoi(val);
1463 break;
1464
1465 default:
1466 break;
1467 }
1468 if (sticky)
1469 setbitn(opt, StickyOpt);
1470 return;
1471 }
1472 /*
1473 ** SETCLASS -- set a word into a class
1474 **
1475 ** Parameters:
1476 ** class -- the class to put the word in.
1477 ** word -- the word to enter
1478 **
1479 ** Returns:
1480 ** none.
1481 **
1482 ** Side Effects:
1483 ** puts the word into the symbol table.
1484 */
1485
setclass(class,word)1486 setclass(class, word)
1487 int class;
1488 char *word;
1489 {
1490 register STAB *s;
1491
1492 if (tTd(37, 8))
1493 printf("setclass(%c, %s)\n", class, word);
1494 s = stab(word, ST_CLASS, ST_ENTER);
1495 setbitn(class, s->s_class);
1496 }
1497 /*
1498 ** MAKEMAPENTRY -- create a map entry
1499 **
1500 ** Parameters:
1501 ** line -- the config file line
1502 **
1503 ** Returns:
1504 ** TRUE if it successfully entered the map entry.
1505 ** FALSE otherwise (usually syntax error).
1506 **
1507 ** Side Effects:
1508 ** Enters the map into the dictionary.
1509 */
1510
1511 void
makemapentry(line)1512 makemapentry(line)
1513 char *line;
1514 {
1515 register char *p;
1516 char *mapname;
1517 char *classname;
1518 register STAB *s;
1519 STAB *class;
1520
1521 for (p = line; isascii(*p) && isspace(*p); p++)
1522 continue;
1523 if (!(isascii(*p) && isalnum(*p)))
1524 {
1525 syserr("readcf: config K line: no map name");
1526 return;
1527 }
1528
1529 mapname = p;
1530 while (isascii(*++p) && isalnum(*p))
1531 continue;
1532 if (*p != '\0')
1533 *p++ = '\0';
1534 while (isascii(*p) && isspace(*p))
1535 p++;
1536 if (!(isascii(*p) && isalnum(*p)))
1537 {
1538 syserr("readcf: config K line, map %s: no map class", mapname);
1539 return;
1540 }
1541 classname = p;
1542 while (isascii(*++p) && isalnum(*p))
1543 continue;
1544 if (*p != '\0')
1545 *p++ = '\0';
1546 while (isascii(*p) && isspace(*p))
1547 p++;
1548
1549 /* look up the class */
1550 class = stab(classname, ST_MAPCLASS, ST_FIND);
1551 if (class == NULL)
1552 {
1553 syserr("readcf: map %s: class %s not available", mapname, classname);
1554 return;
1555 }
1556
1557 /* enter the map */
1558 s = stab(mapname, ST_MAP, ST_ENTER);
1559 s->s_map.map_class = &class->s_mapclass;
1560 s->s_map.map_mname = newstr(mapname);
1561
1562 if (class->s_mapclass.map_parse(&s->s_map, p))
1563 s->s_map.map_mflags |= MF_VALID;
1564
1565 if (tTd(37, 5))
1566 {
1567 printf("map %s, class %s, flags %x, file %s,\n",
1568 s->s_map.map_mname, s->s_map.map_class->map_cname,
1569 s->s_map.map_mflags,
1570 s->s_map.map_file == NULL ? "(null)" : s->s_map.map_file);
1571 printf("\tapp %s, domain %s, rebuild %s\n",
1572 s->s_map.map_app == NULL ? "(null)" : s->s_map.map_app,
1573 s->s_map.map_domain == NULL ? "(null)" : s->s_map.map_domain,
1574 s->s_map.map_rebuild == NULL ? "(null)" : s->s_map.map_rebuild);
1575 }
1576 }
1577 /*
1578 ** SETTIMEOUTS -- parse and set timeout values
1579 **
1580 ** Parameters:
1581 ** val -- a pointer to the values. If NULL, do initial
1582 ** settings.
1583 **
1584 ** Returns:
1585 ** none.
1586 **
1587 ** Side Effects:
1588 ** Initializes the TimeOuts structure
1589 */
1590
1591 #define SECONDS
1592 #define MINUTES * 60
1593 #define HOUR * 3600
1594
settimeouts(val)1595 settimeouts(val)
1596 register char *val;
1597 {
1598 register char *p;
1599 extern time_t convtime();
1600
1601 if (val == NULL)
1602 {
1603 TimeOuts.to_initial = (time_t) 5 MINUTES;
1604 TimeOuts.to_helo = (time_t) 5 MINUTES;
1605 TimeOuts.to_mail = (time_t) 10 MINUTES;
1606 TimeOuts.to_rcpt = (time_t) 1 HOUR;
1607 TimeOuts.to_datainit = (time_t) 5 MINUTES;
1608 TimeOuts.to_datablock = (time_t) 1 HOUR;
1609 TimeOuts.to_datafinal = (time_t) 1 HOUR;
1610 TimeOuts.to_rset = (time_t) 5 MINUTES;
1611 TimeOuts.to_quit = (time_t) 2 MINUTES;
1612 TimeOuts.to_nextcommand = (time_t) 1 HOUR;
1613 TimeOuts.to_miscshort = (time_t) 2 MINUTES;
1614 TimeOuts.to_ident = (time_t) 30 SECONDS;
1615 return;
1616 }
1617
1618 for (;; val = p)
1619 {
1620 while (isascii(*val) && isspace(*val))
1621 val++;
1622 if (*val == '\0')
1623 break;
1624 for (p = val; *p != '\0' && *p != ','; p++)
1625 continue;
1626 if (*p != '\0')
1627 *p++ = '\0';
1628
1629 if (isascii(*val) && isdigit(*val))
1630 {
1631 /* old syntax -- set everything */
1632 TimeOuts.to_mail = convtime(val, 'm');
1633 TimeOuts.to_rcpt = TimeOuts.to_mail;
1634 TimeOuts.to_datainit = TimeOuts.to_mail;
1635 TimeOuts.to_datablock = TimeOuts.to_mail;
1636 TimeOuts.to_datafinal = TimeOuts.to_mail;
1637 TimeOuts.to_nextcommand = TimeOuts.to_mail;
1638 continue;
1639 }
1640 else
1641 {
1642 register char *q = strchr(val, '=');
1643 time_t to;
1644
1645 if (q == NULL)
1646 {
1647 /* syntax error */
1648 continue;
1649 }
1650 *q++ = '\0';
1651 to = convtime(q, 'm');
1652
1653 if (strcasecmp(val, "initial") == 0)
1654 TimeOuts.to_initial = to;
1655 else if (strcasecmp(val, "mail") == 0)
1656 TimeOuts.to_mail = to;
1657 else if (strcasecmp(val, "rcpt") == 0)
1658 TimeOuts.to_rcpt = to;
1659 else if (strcasecmp(val, "datainit") == 0)
1660 TimeOuts.to_datainit = to;
1661 else if (strcasecmp(val, "datablock") == 0)
1662 TimeOuts.to_datablock = to;
1663 else if (strcasecmp(val, "datafinal") == 0)
1664 TimeOuts.to_datafinal = to;
1665 else if (strcasecmp(val, "command") == 0)
1666 TimeOuts.to_nextcommand = to;
1667 else if (strcasecmp(val, "rset") == 0)
1668 TimeOuts.to_rset = to;
1669 else if (strcasecmp(val, "helo") == 0)
1670 TimeOuts.to_helo = to;
1671 else if (strcasecmp(val, "quit") == 0)
1672 TimeOuts.to_quit = to;
1673 else if (strcasecmp(val, "misc") == 0)
1674 TimeOuts.to_miscshort = to;
1675 else if (strcasecmp(val, "ident") == 0)
1676 TimeOuts.to_ident = to;
1677 else
1678 syserr("settimeouts: invalid timeout %s", val);
1679 }
1680 }
1681 }
1682