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.3 (Berkeley) 07/16/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 define(bp[1], newstr(munchstring(&bp[2], NULL)), e); 359 break; 360 361 case 'H': /* required header line */ 362 (void) chompheader(&bp[1], TRUE, e); 363 break; 364 365 case 'C': /* word class */ 366 /* scan the list of words and set class for all */ 367 for (p = &bp[2]; *p != '\0'; ) 368 { 369 register char *wd; 370 char delim; 371 372 while (*p != '\0' && isascii(*p) && isspace(*p)) 373 p++; 374 wd = p; 375 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 376 p++; 377 delim = *p; 378 *p = '\0'; 379 if (wd[0] != '\0') 380 { 381 if (tTd(37, 2)) 382 printf("setclass(%c, %s)\n", 383 bp[1], wd); 384 setclass(bp[1], wd); 385 } 386 *p = delim; 387 } 388 break; 389 390 case 'F': /* word class from file */ 391 /* read list of words from argument or file */ 392 /* read from file */ 393 for (p = &bp[2]; 394 *p != '\0' && !(isascii(*p) && isspace(*p)); 395 p++) 396 continue; 397 if (*p == '\0') 398 p = "%s"; 399 else 400 { 401 *p = '\0'; 402 while (isascii(*++p) && isspace(*p)) 403 continue; 404 } 405 fileclass(bp[1], &bp[2], p, safe); 406 break; 407 408 #ifdef XLA 409 case 'L': /* extended load average description */ 410 xla_init(&bp[1]); 411 break; 412 #endif 413 414 case 'M': /* define mailer */ 415 makemailer(&bp[1]); 416 break; 417 418 case 'O': /* set option */ 419 setoption(bp[1], &bp[2], safe, FALSE, e); 420 break; 421 422 case 'P': /* set precedence */ 423 if (NumPriorities >= MAXPRIORITIES) 424 { 425 toomany('P', MAXPRIORITIES); 426 break; 427 } 428 for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 429 continue; 430 if (*p == '\0') 431 goto badline; 432 *p = '\0'; 433 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 434 Priorities[NumPriorities].pri_val = atoi(++p); 435 NumPriorities++; 436 break; 437 438 case 'T': /* trusted user(s) */ 439 /* this option is obsolete, but will be ignored */ 440 break; 441 442 case 'V': /* configuration syntax version */ 443 ConfigLevel = atoi(&bp[1]); 444 break; 445 446 case 'K': 447 makemapentry(&bp[1]); 448 break; 449 450 default: 451 badline: 452 syserr("unknown control line \"%s\"", bp); 453 } 454 if (bp != buf) 455 free(bp); 456 } 457 if (ferror(cf)) 458 { 459 syserr("I/O read error", cfname); 460 exit(EX_OSFILE); 461 } 462 fclose(cf); 463 FileName = NULL; 464 465 if (stab("host", ST_MAP, ST_FIND) == NULL) 466 { 467 /* user didn't initialize: set up host map */ 468 strcpy(buf, "host host"); 469 if (ConfigLevel >= 2) 470 strcat(buf, " -a."); 471 makemapentry(buf); 472 } 473 } 474 /* 475 ** TOOMANY -- signal too many of some option 476 ** 477 ** Parameters: 478 ** id -- the id of the error line 479 ** maxcnt -- the maximum possible values 480 ** 481 ** Returns: 482 ** none. 483 ** 484 ** Side Effects: 485 ** gives a syserr. 486 */ 487 488 toomany(id, maxcnt) 489 char id; 490 int maxcnt; 491 { 492 syserr("too many %c lines, %d max", id, maxcnt); 493 } 494 /* 495 ** FILECLASS -- read members of a class from a file 496 ** 497 ** Parameters: 498 ** class -- class to define. 499 ** filename -- name of file to read. 500 ** fmt -- scanf string to use for match. 501 ** 502 ** Returns: 503 ** none 504 ** 505 ** Side Effects: 506 ** 507 ** puts all lines in filename that match a scanf into 508 ** the named class. 509 */ 510 511 fileclass(class, filename, fmt, safe) 512 int class; 513 char *filename; 514 char *fmt; 515 bool safe; 516 { 517 FILE *f; 518 struct stat stbuf; 519 char buf[MAXLINE]; 520 521 if (stat(filename, &stbuf) < 0) 522 { 523 syserr("fileclass: cannot stat %s", filename); 524 return; 525 } 526 if (!S_ISREG(stbuf.st_mode)) 527 { 528 syserr("fileclass: %s not a regular file", filename); 529 return; 530 } 531 if (!safe && access(filename, R_OK) < 0) 532 { 533 syserr("fileclass: access denied on %s", filename); 534 return; 535 } 536 f = fopen(filename, "r"); 537 if (f == NULL) 538 { 539 syserr("fileclass: cannot open %s", filename); 540 return; 541 } 542 543 while (fgets(buf, sizeof buf, f) != NULL) 544 { 545 register STAB *s; 546 register char *p; 547 # ifdef SCANF 548 char wordbuf[MAXNAME+1]; 549 550 if (sscanf(buf, fmt, wordbuf) != 1) 551 continue; 552 p = wordbuf; 553 # else /* SCANF */ 554 p = buf; 555 # endif /* SCANF */ 556 557 /* 558 ** Break up the match into words. 559 */ 560 561 while (*p != '\0') 562 { 563 register char *q; 564 565 /* strip leading spaces */ 566 while (isascii(*p) && isspace(*p)) 567 p++; 568 if (*p == '\0') 569 break; 570 571 /* find the end of the word */ 572 q = p; 573 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 574 p++; 575 if (*p != '\0') 576 *p++ = '\0'; 577 578 /* enter the word in the symbol table */ 579 s = stab(q, ST_CLASS, ST_ENTER); 580 setbitn(class, s->s_class); 581 } 582 } 583 584 (void) fclose(f); 585 } 586 /* 587 ** MAKEMAILER -- define a new mailer. 588 ** 589 ** Parameters: 590 ** line -- description of mailer. This is in labeled 591 ** fields. The fields are: 592 ** P -- the path to the mailer 593 ** F -- the flags associated with the mailer 594 ** A -- the argv for this mailer 595 ** S -- the sender rewriting set 596 ** R -- the recipient rewriting set 597 ** E -- the eol string 598 ** The first word is the canonical name of the mailer. 599 ** 600 ** Returns: 601 ** none. 602 ** 603 ** Side Effects: 604 ** enters the mailer into the mailer table. 605 */ 606 607 makemailer(line) 608 char *line; 609 { 610 register char *p; 611 register struct mailer *m; 612 register STAB *s; 613 int i; 614 char fcode; 615 auto char *endp; 616 extern int NextMailer; 617 extern char **makeargv(); 618 extern char *munchstring(); 619 extern long atol(); 620 621 /* allocate a mailer and set up defaults */ 622 m = (struct mailer *) xalloc(sizeof *m); 623 bzero((char *) m, sizeof *m); 624 m->m_eol = "\n"; 625 626 /* collect the mailer name */ 627 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 628 continue; 629 if (*p != '\0') 630 *p++ = '\0'; 631 m->m_name = newstr(line); 632 633 /* now scan through and assign info from the fields */ 634 while (*p != '\0') 635 { 636 auto char *delimptr; 637 638 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 639 p++; 640 641 /* p now points to field code */ 642 fcode = *p; 643 while (*p != '\0' && *p != '=' && *p != ',') 644 p++; 645 if (*p++ != '=') 646 { 647 syserr("mailer %s: `=' expected", m->m_name); 648 return; 649 } 650 while (isascii(*p) && isspace(*p)) 651 p++; 652 653 /* p now points to the field body */ 654 p = munchstring(p, &delimptr); 655 656 /* install the field into the mailer struct */ 657 switch (fcode) 658 { 659 case 'P': /* pathname */ 660 m->m_mailer = newstr(p); 661 break; 662 663 case 'F': /* flags */ 664 for (; *p != '\0'; p++) 665 if (!(isascii(*p) && isspace(*p))) 666 setbitn(*p, m->m_flags); 667 break; 668 669 case 'S': /* sender rewriting ruleset */ 670 case 'R': /* recipient rewriting ruleset */ 671 i = strtol(p, &endp, 10); 672 if (i < 0 || i >= MAXRWSETS) 673 { 674 syserr("invalid rewrite set, %d max", MAXRWSETS); 675 return; 676 } 677 if (fcode == 'S') 678 m->m_sh_rwset = m->m_se_rwset = i; 679 else 680 m->m_rh_rwset = m->m_re_rwset = i; 681 682 p = endp; 683 if (*p++ == '/') 684 { 685 i = strtol(p, NULL, 10); 686 if (i < 0 || i >= MAXRWSETS) 687 { 688 syserr("invalid rewrite set, %d max", 689 MAXRWSETS); 690 return; 691 } 692 if (fcode == 'S') 693 m->m_sh_rwset = i; 694 else 695 m->m_rh_rwset = i; 696 } 697 break; 698 699 case 'E': /* end of line string */ 700 m->m_eol = newstr(p); 701 break; 702 703 case 'A': /* argument vector */ 704 m->m_argv = makeargv(p); 705 break; 706 707 case 'M': /* maximum message size */ 708 m->m_maxsize = atol(p); 709 break; 710 711 case 'L': /* maximum line length */ 712 m->m_linelimit = atoi(p); 713 break; 714 715 case 'D': /* working directory */ 716 m->m_execdir = newstr(p); 717 break; 718 } 719 720 p = delimptr; 721 } 722 723 /* do some heuristic cleanup for back compatibility */ 724 if (bitnset(M_LIMITS, m->m_flags)) 725 { 726 if (m->m_linelimit == 0) 727 m->m_linelimit = SMTPLINELIM; 728 if (ConfigLevel < 2) 729 setbitn(M_7BITS, m->m_flags); 730 } 731 732 /* do some rationality checking */ 733 if (m->m_argv == NULL) 734 { 735 syserr("M%s: A= argument required", m->m_name); 736 return; 737 } 738 if (m->m_mailer == NULL) 739 { 740 syserr("M%s: P= argument required", m->m_name); 741 return; 742 } 743 744 if (NextMailer >= MAXMAILERS) 745 { 746 syserr("too many mailers defined (%d max)", MAXMAILERS); 747 return; 748 } 749 750 s = stab(m->m_name, ST_MAILER, ST_ENTER); 751 if (s->s_mailer != NULL) 752 { 753 i = s->s_mailer->m_mno; 754 free(s->s_mailer); 755 } 756 else 757 { 758 i = NextMailer++; 759 } 760 Mailer[i] = s->s_mailer = m; 761 m->m_mno = i; 762 } 763 /* 764 ** MUNCHSTRING -- translate a string into internal form. 765 ** 766 ** Parameters: 767 ** p -- the string to munch. 768 ** delimptr -- if non-NULL, set to the pointer of the 769 ** field delimiter character. 770 ** 771 ** Returns: 772 ** the munched string. 773 */ 774 775 char * 776 munchstring(p, delimptr) 777 register char *p; 778 char **delimptr; 779 { 780 register char *q; 781 bool backslash = FALSE; 782 bool quotemode = FALSE; 783 static char buf[MAXLINE]; 784 785 for (q = buf; *p != '\0'; p++) 786 { 787 if (backslash) 788 { 789 /* everything is roughly literal */ 790 backslash = FALSE; 791 switch (*p) 792 { 793 case 'r': /* carriage return */ 794 *q++ = '\r'; 795 continue; 796 797 case 'n': /* newline */ 798 *q++ = '\n'; 799 continue; 800 801 case 'f': /* form feed */ 802 *q++ = '\f'; 803 continue; 804 805 case 'b': /* backspace */ 806 *q++ = '\b'; 807 continue; 808 } 809 *q++ = *p; 810 } 811 else 812 { 813 if (*p == '\\') 814 backslash = TRUE; 815 else if (*p == '"') 816 quotemode = !quotemode; 817 else if (quotemode || *p != ',') 818 *q++ = *p; 819 else 820 break; 821 } 822 } 823 824 if (delimptr != NULL) 825 *delimptr = p; 826 *q++ = '\0'; 827 return (buf); 828 } 829 /* 830 ** MAKEARGV -- break up a string into words 831 ** 832 ** Parameters: 833 ** p -- the string to break up. 834 ** 835 ** Returns: 836 ** a char **argv (dynamically allocated) 837 ** 838 ** Side Effects: 839 ** munges p. 840 */ 841 842 char ** 843 makeargv(p) 844 register char *p; 845 { 846 char *q; 847 int i; 848 char **avp; 849 char *argv[MAXPV + 1]; 850 851 /* take apart the words */ 852 i = 0; 853 while (*p != '\0' && i < MAXPV) 854 { 855 q = p; 856 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 857 p++; 858 while (isascii(*p) && isspace(*p)) 859 *p++ = '\0'; 860 argv[i++] = newstr(q); 861 } 862 argv[i++] = NULL; 863 864 /* now make a copy of the argv */ 865 avp = (char **) xalloc(sizeof *avp * i); 866 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 867 868 return (avp); 869 } 870 /* 871 ** PRINTRULES -- print rewrite rules (for debugging) 872 ** 873 ** Parameters: 874 ** none. 875 ** 876 ** Returns: 877 ** none. 878 ** 879 ** Side Effects: 880 ** prints rewrite rules. 881 */ 882 883 printrules() 884 { 885 register struct rewrite *rwp; 886 register int ruleset; 887 888 for (ruleset = 0; ruleset < 10; ruleset++) 889 { 890 if (RewriteRules[ruleset] == NULL) 891 continue; 892 printf("\n----Rule Set %d:", ruleset); 893 894 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 895 { 896 printf("\nLHS:"); 897 printav(rwp->r_lhs); 898 printf("RHS:"); 899 printav(rwp->r_rhs); 900 } 901 } 902 } 903 904 /* 905 ** SETOPTION -- set global processing option 906 ** 907 ** Parameters: 908 ** opt -- option name. 909 ** val -- option value (as a text string). 910 ** safe -- set if this came from a configuration file. 911 ** Some options (if set from the command line) will 912 ** reset the user id to avoid security problems. 913 ** sticky -- if set, don't let other setoptions override 914 ** this value. 915 ** e -- the main envelope. 916 ** 917 ** Returns: 918 ** none. 919 ** 920 ** Side Effects: 921 ** Sets options as implied by the arguments. 922 */ 923 924 static BITMAP StickyOpt; /* set if option is stuck */ 925 926 927 #ifdef NAMED_BIND 928 929 struct resolverflags 930 { 931 char *rf_name; /* name of the flag */ 932 long rf_bits; /* bits to set/clear */ 933 } ResolverFlags[] = 934 { 935 "debug", RES_DEBUG, 936 "aaonly", RES_AAONLY, 937 "usevc", RES_USEVC, 938 "primary", RES_PRIMARY, 939 "igntc", RES_IGNTC, 940 "recurse", RES_RECURSE, 941 "defnames", RES_DEFNAMES, 942 "stayopen", RES_STAYOPEN, 943 "dnsrch", RES_DNSRCH, 944 NULL, 0 945 }; 946 947 #endif 948 949 setoption(opt, val, safe, sticky, e) 950 char opt; 951 char *val; 952 bool safe; 953 bool sticky; 954 register ENVELOPE *e; 955 { 956 register char *p; 957 extern bool atobool(); 958 extern time_t convtime(); 959 extern int QueueLA; 960 extern int RefuseLA; 961 extern bool trusteduser(); 962 963 if (tTd(37, 1)) 964 printf("setoption %c=%s", opt, val); 965 966 /* 967 ** See if this option is preset for us. 968 */ 969 970 if (!sticky && bitnset(opt, StickyOpt)) 971 { 972 if (tTd(37, 1)) 973 printf(" (ignored)\n"); 974 return; 975 } 976 977 /* 978 ** Check to see if this option can be specified by this user. 979 */ 980 981 if (!safe && RealUid == 0) 982 safe = TRUE; 983 if (!safe && strchr("bCdeEijLmoprsvw7", opt) == NULL) 984 { 985 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 986 { 987 if (tTd(37, 1)) 988 printf(" (unsafe)"); 989 if (RealUid != geteuid()) 990 { 991 if (tTd(37, 1)) 992 printf("(Resetting uid)"); 993 (void) setgid(RealGid); 994 (void) setuid(RealUid); 995 } 996 } 997 } 998 if (tTd(37, 1)) 999 printf("\n"); 1000 1001 switch (opt) 1002 { 1003 case '7': /* force seven-bit input */ 1004 SevenBit = atobool(val); 1005 break; 1006 1007 case 'A': /* set default alias file */ 1008 if (val[0] == '\0') 1009 setalias("aliases"); 1010 else 1011 setalias(val); 1012 break; 1013 1014 case 'a': /* look N minutes for "@:@" in alias file */ 1015 if (val[0] == '\0') 1016 SafeAlias = 5; 1017 else 1018 SafeAlias = atoi(val); 1019 break; 1020 1021 case 'B': /* substitution for blank character */ 1022 SpaceSub = val[0]; 1023 if (SpaceSub == '\0') 1024 SpaceSub = ' '; 1025 break; 1026 1027 case 'b': /* min blocks free on queue fs/max msg size */ 1028 p = strchr(val, '/'); 1029 if (p != NULL) 1030 { 1031 *p++ = '\0'; 1032 MaxMessageSize = atol(p); 1033 } 1034 MinBlocksFree = atol(val); 1035 break; 1036 1037 case 'c': /* don't connect to "expensive" mailers */ 1038 NoConnect = atobool(val); 1039 break; 1040 1041 case 'C': /* checkpoint every N addresses */ 1042 CheckpointInterval = atoi(val); 1043 break; 1044 1045 case 'd': /* delivery mode */ 1046 switch (*val) 1047 { 1048 case '\0': 1049 e->e_sendmode = SM_DELIVER; 1050 break; 1051 1052 case SM_QUEUE: /* queue only */ 1053 #ifndef QUEUE 1054 syserr("need QUEUE to set -odqueue"); 1055 #endif /* QUEUE */ 1056 /* fall through..... */ 1057 1058 case SM_DELIVER: /* do everything */ 1059 case SM_FORK: /* fork after verification */ 1060 e->e_sendmode = *val; 1061 break; 1062 1063 default: 1064 syserr("Unknown delivery mode %c", *val); 1065 exit(EX_USAGE); 1066 } 1067 break; 1068 1069 case 'D': /* rebuild alias database as needed */ 1070 AutoRebuild = atobool(val); 1071 break; 1072 1073 case 'E': /* error message header/header file */ 1074 if (*val != '\0') 1075 ErrMsgFile = newstr(val); 1076 break; 1077 1078 case 'e': /* set error processing mode */ 1079 switch (*val) 1080 { 1081 case EM_QUIET: /* be silent about it */ 1082 case EM_MAIL: /* mail back */ 1083 case EM_BERKNET: /* do berknet error processing */ 1084 case EM_WRITE: /* write back (or mail) */ 1085 HoldErrs = TRUE; 1086 /* fall through... */ 1087 1088 case EM_PRINT: /* print errors normally (default) */ 1089 e->e_errormode = *val; 1090 break; 1091 } 1092 break; 1093 1094 case 'F': /* file mode */ 1095 FileMode = atooct(val) & 0777; 1096 break; 1097 1098 case 'f': /* save Unix-style From lines on front */ 1099 SaveFrom = atobool(val); 1100 break; 1101 1102 case 'G': /* match recipients against GECOS field */ 1103 MatchGecos = atobool(val); 1104 break; 1105 1106 case 'g': /* default gid */ 1107 DefGid = atoi(val); 1108 break; 1109 1110 case 'H': /* help file */ 1111 if (val[0] == '\0') 1112 HelpFile = "sendmail.hf"; 1113 else 1114 HelpFile = newstr(val); 1115 break; 1116 1117 case 'h': /* maximum hop count */ 1118 MaxHopCount = atoi(val); 1119 break; 1120 1121 case 'I': /* use internet domain name server */ 1122 #ifdef NAMED_BIND 1123 UseNameServer = TRUE; 1124 for (p = val; *p != 0; ) 1125 { 1126 bool clearmode; 1127 char *q; 1128 struct resolverflags *rfp; 1129 1130 while (*p == ' ') 1131 p++; 1132 if (*p == '\0') 1133 break; 1134 clearmode = FALSE; 1135 if (*p == '-') 1136 clearmode = TRUE; 1137 else if (*p != '+') 1138 p--; 1139 p++; 1140 q = p; 1141 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1142 p++; 1143 if (*p != '\0') 1144 *p++ = '\0'; 1145 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1146 { 1147 if (strcasecmp(q, rfp->rf_name) == 0) 1148 break; 1149 } 1150 if (clearmode) 1151 _res.options &= ~rfp->rf_bits; 1152 else 1153 _res.options |= rfp->rf_bits; 1154 } 1155 if (tTd(8, 2)) 1156 printf("_res.options = %x\n", _res.options); 1157 #else 1158 usrerr("name server (I option) specified but BIND not compiled in"); 1159 #endif 1160 break; 1161 1162 case 'i': /* ignore dot lines in message */ 1163 IgnrDot = atobool(val); 1164 break; 1165 1166 case 'j': /* send errors in MIME (RFC 1341) format */ 1167 SendMIMEErrors = atobool(val); 1168 break; 1169 1170 case 'J': /* .forward search path */ 1171 ForwardPath = newstr(val); 1172 break; 1173 1174 case 'k': /* connection cache size */ 1175 MaxMciCache = atoi(val); 1176 if (MaxMciCache < 0) 1177 MaxMciCache = 0; 1178 break; 1179 1180 case 'K': /* connection cache timeout */ 1181 MciCacheTimeout = convtime(val, 'm'); 1182 break; 1183 1184 case 'l': /* use Errors-To: header */ 1185 UseErrorsTo = atobool(val); 1186 break; 1187 1188 case 'L': /* log level */ 1189 LogLevel = atoi(val); 1190 break; 1191 1192 case 'M': /* define macro */ 1193 define(val[0], newstr(&val[1]), CurEnv); 1194 sticky = FALSE; 1195 break; 1196 1197 case 'm': /* send to me too */ 1198 MeToo = atobool(val); 1199 break; 1200 1201 case 'n': /* validate RHS in newaliases */ 1202 CheckAliases = atobool(val); 1203 break; 1204 1205 /* 'N' available -- was "net name" */ 1206 1207 case 'O': /* daemon options */ 1208 setdaemonoptions(val); 1209 break; 1210 1211 case 'o': /* assume old style headers */ 1212 if (atobool(val)) 1213 CurEnv->e_flags |= EF_OLDSTYLE; 1214 else 1215 CurEnv->e_flags &= ~EF_OLDSTYLE; 1216 break; 1217 1218 case 'p': /* select privacy level */ 1219 p = val; 1220 for (;;) 1221 { 1222 register struct prival *pv; 1223 extern struct prival PrivacyValues[]; 1224 1225 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1226 p++; 1227 if (*p == '\0') 1228 break; 1229 val = p; 1230 while (isascii(*p) && isalnum(*p)) 1231 p++; 1232 if (*p != '\0') 1233 *p++ = '\0'; 1234 1235 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1236 { 1237 if (strcasecmp(val, pv->pv_name) == 0) 1238 break; 1239 } 1240 if (pv->pv_name == NULL) 1241 syserr("readcf: Op line: %s unrecognized", val); 1242 PrivacyFlags |= pv->pv_flag; 1243 } 1244 break; 1245 1246 case 'P': /* postmaster copy address for returned mail */ 1247 PostMasterCopy = newstr(val); 1248 break; 1249 1250 case 'q': /* slope of queue only function */ 1251 QueueFactor = atoi(val); 1252 break; 1253 1254 case 'Q': /* queue directory */ 1255 if (val[0] == '\0') 1256 QueueDir = "mqueue"; 1257 else 1258 QueueDir = newstr(val); 1259 if (RealUid != 0 && !safe) 1260 auth_warning(e, "Processed from queue %s", QueueDir); 1261 break; 1262 1263 case 'R': /* don't prune routes */ 1264 DontPruneRoutes = atobool(val); 1265 break; 1266 1267 case 'r': /* read timeout */ 1268 settimeouts(val); 1269 break; 1270 1271 case 'S': /* status file */ 1272 if (val[0] == '\0') 1273 StatFile = "sendmail.st"; 1274 else 1275 StatFile = newstr(val); 1276 break; 1277 1278 case 's': /* be super safe, even if expensive */ 1279 SuperSafe = atobool(val); 1280 break; 1281 1282 case 'T': /* queue timeout */ 1283 p = strchr(val, '/'); 1284 if (p != NULL) 1285 { 1286 *p++ = '\0'; 1287 TimeOuts.to_q_warning = convtime(p, 'd'); 1288 } 1289 TimeOuts.to_q_return = convtime(val, 'h'); 1290 break; 1291 1292 case 't': /* time zone name */ 1293 TimeZoneSpec = newstr(val); 1294 break; 1295 1296 case 'U': /* location of user database */ 1297 UdbSpec = newstr(val); 1298 break; 1299 1300 case 'u': /* set default uid */ 1301 DefUid = atoi(val); 1302 setdefuser(); 1303 break; 1304 1305 case 'V': /* fallback MX host */ 1306 FallBackMX = newstr(val); 1307 break; 1308 1309 case 'v': /* run in verbose mode */ 1310 Verbose = atobool(val); 1311 break; 1312 1313 case 'w': /* if we are best MX, try host directly */ 1314 TryNullMXList = atobool(val); 1315 break; 1316 1317 /* 'W' available -- was wizard password */ 1318 1319 case 'x': /* load avg at which to auto-queue msgs */ 1320 QueueLA = atoi(val); 1321 break; 1322 1323 case 'X': /* load avg at which to auto-reject connections */ 1324 RefuseLA = atoi(val); 1325 break; 1326 1327 case 'y': /* work recipient factor */ 1328 WkRecipFact = atoi(val); 1329 break; 1330 1331 case 'Y': /* fork jobs during queue runs */ 1332 ForkQueueRuns = atobool(val); 1333 break; 1334 1335 case 'z': /* work message class factor */ 1336 WkClassFact = atoi(val); 1337 break; 1338 1339 case 'Z': /* work time factor */ 1340 WkTimeFact = atoi(val); 1341 break; 1342 1343 default: 1344 break; 1345 } 1346 if (sticky) 1347 setbitn(opt, StickyOpt); 1348 return; 1349 } 1350 /* 1351 ** SETCLASS -- set a word into a class 1352 ** 1353 ** Parameters: 1354 ** class -- the class to put the word in. 1355 ** word -- the word to enter 1356 ** 1357 ** Returns: 1358 ** none. 1359 ** 1360 ** Side Effects: 1361 ** puts the word into the symbol table. 1362 */ 1363 1364 setclass(class, word) 1365 int class; 1366 char *word; 1367 { 1368 register STAB *s; 1369 1370 if (tTd(37, 8)) 1371 printf("%s added to class %c\n", word, class); 1372 s = stab(word, ST_CLASS, ST_ENTER); 1373 setbitn(class, s->s_class); 1374 } 1375 /* 1376 ** MAKEMAPENTRY -- create a map entry 1377 ** 1378 ** Parameters: 1379 ** line -- the config file line 1380 ** 1381 ** Returns: 1382 ** TRUE if it successfully entered the map entry. 1383 ** FALSE otherwise (usually syntax error). 1384 ** 1385 ** Side Effects: 1386 ** Enters the map into the dictionary. 1387 */ 1388 1389 void 1390 makemapentry(line) 1391 char *line; 1392 { 1393 register char *p; 1394 char *mapname; 1395 char *classname; 1396 register STAB *map; 1397 STAB *class; 1398 1399 for (p = line; isascii(*p) && isspace(*p); p++) 1400 continue; 1401 if (!(isascii(*p) && isalnum(*p))) 1402 { 1403 syserr("readcf: config K line: no map name"); 1404 return; 1405 } 1406 1407 mapname = p; 1408 while (isascii(*++p) && isalnum(*p)) 1409 continue; 1410 if (*p != '\0') 1411 *p++ = '\0'; 1412 while (isascii(*p) && isspace(*p)) 1413 p++; 1414 if (!(isascii(*p) && isalnum(*p))) 1415 { 1416 syserr("readcf: config K line, map %s: no map class", mapname); 1417 return; 1418 } 1419 classname = p; 1420 while (isascii(*++p) && isalnum(*p)) 1421 continue; 1422 if (*p != '\0') 1423 *p++ = '\0'; 1424 while (isascii(*p) && isspace(*p)) 1425 p++; 1426 1427 /* look up the class */ 1428 class = stab(classname, ST_MAPCLASS, ST_FIND); 1429 if (class == NULL) 1430 { 1431 syserr("readcf: map %s: class %s not available", mapname, classname); 1432 return; 1433 } 1434 1435 /* enter the map */ 1436 map = stab(mapname, ST_MAP, ST_ENTER); 1437 map->s_map.map_class = &class->s_mapclass; 1438 map->s_map.map_mname = newstr(mapname); 1439 1440 if (class->s_mapclass.map_parse(&map->s_map, p)) 1441 map->s_map.map_mflags |= MF_VALID; 1442 } 1443 /* 1444 ** SETTIMEOUTS -- parse and set timeout values 1445 ** 1446 ** Parameters: 1447 ** val -- a pointer to the values. If NULL, do initial 1448 ** settings. 1449 ** 1450 ** Returns: 1451 ** none. 1452 ** 1453 ** Side Effects: 1454 ** Initializes the TimeOuts structure 1455 */ 1456 1457 #define MINUTES * 60 1458 #define HOUR * 3600 1459 1460 settimeouts(val) 1461 register char *val; 1462 { 1463 register char *p; 1464 extern time_t convtime(); 1465 1466 if (val == NULL) 1467 { 1468 TimeOuts.to_initial = (time_t) 5 MINUTES; 1469 TimeOuts.to_helo = (time_t) 5 MINUTES; 1470 TimeOuts.to_mail = (time_t) 10 MINUTES; 1471 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1472 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1473 TimeOuts.to_datablock = (time_t) 1 HOUR; 1474 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1475 TimeOuts.to_rset = (time_t) 5 MINUTES; 1476 TimeOuts.to_quit = (time_t) 2 MINUTES; 1477 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1478 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1479 return; 1480 } 1481 1482 for (;; val = p) 1483 { 1484 while (isascii(*val) && isspace(*val)) 1485 val++; 1486 if (*val == '\0') 1487 break; 1488 for (p = val; *p != '\0' && *p != ','; p++) 1489 continue; 1490 if (*p != '\0') 1491 *p++ = '\0'; 1492 1493 if (isascii(*val) && isdigit(*val)) 1494 { 1495 /* old syntax -- set everything */ 1496 TimeOuts.to_mail = convtime(val, 'm'); 1497 TimeOuts.to_rcpt = TimeOuts.to_mail; 1498 TimeOuts.to_datainit = TimeOuts.to_mail; 1499 TimeOuts.to_datablock = TimeOuts.to_mail; 1500 TimeOuts.to_datafinal = TimeOuts.to_mail; 1501 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1502 continue; 1503 } 1504 else 1505 { 1506 register char *q = strchr(val, '='); 1507 time_t to; 1508 1509 if (q == NULL) 1510 { 1511 /* syntax error */ 1512 continue; 1513 } 1514 *q++ = '\0'; 1515 to = convtime(q, 'm'); 1516 1517 if (strcasecmp(val, "initial") == 0) 1518 TimeOuts.to_initial = to; 1519 else if (strcasecmp(val, "mail") == 0) 1520 TimeOuts.to_mail = to; 1521 else if (strcasecmp(val, "rcpt") == 0) 1522 TimeOuts.to_rcpt = to; 1523 else if (strcasecmp(val, "datainit") == 0) 1524 TimeOuts.to_datainit = to; 1525 else if (strcasecmp(val, "datablock") == 0) 1526 TimeOuts.to_datablock = to; 1527 else if (strcasecmp(val, "datafinal") == 0) 1528 TimeOuts.to_datafinal = to; 1529 else if (strcasecmp(val, "command") == 0) 1530 TimeOuts.to_nextcommand = to; 1531 else if (strcasecmp(val, "rset") == 0) 1532 TimeOuts.to_rset = to; 1533 else if (strcasecmp(val, "helo") == 0) 1534 TimeOuts.to_helo = to; 1535 else if (strcasecmp(val, "quit") == 0) 1536 TimeOuts.to_quit = to; 1537 else if (strcasecmp(val, "misc") == 0) 1538 TimeOuts.to_miscshort = to; 1539 else 1540 syserr("settimeouts: invalid timeout %s", val); 1541 } 1542 } 1543 } 1544