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