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