1 /* 2 * Copyright (c) 1983 Eric P. Allman 3 * Copyright (c) 1988 Regents of the University of California. 4 * All rights reserved. 5 * 6 * %sccs.include.redist.c% 7 */ 8 9 #ifndef lint 10 static char sccsid[] = "@(#)readcf.c 6.41 (Berkeley) 05/28/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 char **pv; 72 struct rewrite *rwp = NULL; 73 char *bp; 74 int nfuzzy; 75 char buf[MAXLINE]; 76 register char *p; 77 extern char **copyplist(); 78 struct stat statb; 79 char exbuf[MAXLINE]; 80 char pvpbuf[PSBUFSIZE]; 81 extern char *munchstring(); 82 extern void makemapentry(); 83 84 FileName = cfname; 85 LineNumber = 0; 86 87 cf = fopen(cfname, "r"); 88 if (cf == NULL) 89 { 90 syserr("cannot open"); 91 exit(EX_OSFILE); 92 } 93 94 if (fstat(fileno(cf), &statb) < 0) 95 { 96 syserr("cannot fstat"); 97 exit(EX_OSFILE); 98 } 99 100 if (!S_ISREG(statb.st_mode)) 101 { 102 syserr("not a plain file"); 103 exit(EX_OSFILE); 104 } 105 106 if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 107 { 108 if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) 109 fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 110 FileName); 111 #ifdef LOG 112 if (LogLevel > 0) 113 syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 114 FileName); 115 #endif 116 } 117 118 #ifdef XLA 119 xla_zero(); 120 #endif 121 122 while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 123 { 124 if (bp[0] == '#') 125 { 126 if (bp != buf) 127 free(bp); 128 continue; 129 } 130 131 /* map $ into \201 for macro expansion */ 132 for (p = bp; *p != '\0'; p++) 133 { 134 if (*p == '#' && p > bp && ConfigLevel >= 3) 135 { 136 /* this is an on-line comment */ 137 register char *e; 138 139 switch (*--p & 0377) 140 { 141 case MACROEXPAND: 142 /* it's from $# -- let it go through */ 143 p++; 144 break; 145 146 case '\\': 147 /* it's backslash escaped */ 148 (void) strcpy(p, p + 1); 149 break; 150 151 default: 152 /* delete preceeding white space */ 153 while (isascii(*p) && isspace(*p) && p > bp) 154 p--; 155 if ((e = strchr(++p, '\n')) != NULL) 156 (void) strcpy(p, e); 157 else 158 p[0] = p[1] = '\0'; 159 break; 160 } 161 continue; 162 } 163 164 if (*p != '$') 165 continue; 166 167 if (p[1] == '$') 168 { 169 /* actual dollar sign.... */ 170 (void) strcpy(p, p + 1); 171 continue; 172 } 173 174 /* convert to macro expansion character */ 175 *p = MACROEXPAND; 176 } 177 178 /* interpret this line */ 179 switch (bp[0]) 180 { 181 case '\0': 182 case '#': /* comment */ 183 break; 184 185 case 'R': /* rewriting rule */ 186 for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 187 continue; 188 189 if (*p == '\0') 190 { 191 syserr("invalid rewrite line \"%s\"", bp); 192 break; 193 } 194 195 /* allocate space for the rule header */ 196 if (rwp == NULL) 197 { 198 RewriteRules[ruleset] = rwp = 199 (struct rewrite *) xalloc(sizeof *rwp); 200 } 201 else 202 { 203 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 204 rwp = rwp->r_next; 205 } 206 rwp->r_next = NULL; 207 208 /* expand and save the LHS */ 209 *p = '\0'; 210 expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); 211 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf, NULL); 212 nfuzzy = 0; 213 if (rwp->r_lhs != NULL) 214 { 215 register char **ap; 216 217 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 218 219 /* count the number of fuzzy matches in LHS */ 220 for (ap = rwp->r_lhs; *ap != NULL; ap++) 221 { 222 char *botch; 223 224 botch = NULL; 225 switch (**ap & 0377) 226 { 227 case MATCHZANY: 228 case MATCHANY: 229 case MATCHONE: 230 case MATCHCLASS: 231 case MATCHNCLASS: 232 nfuzzy++; 233 break; 234 235 case MATCHREPL: 236 botch = "$0-$9"; 237 break; 238 239 case CANONNET: 240 botch = "$#"; 241 break; 242 243 case CANONUSER: 244 botch = "$:"; 245 break; 246 247 case CALLSUBR: 248 botch = "$>"; 249 break; 250 251 case CONDIF: 252 botch = "$?"; 253 break; 254 255 case CONDELSE: 256 botch = "$|"; 257 break; 258 259 case CONDFI: 260 botch = "$."; 261 break; 262 263 case HOSTBEGIN: 264 botch = "$["; 265 break; 266 267 case HOSTEND: 268 botch = "$]"; 269 break; 270 271 case LOOKUPBEGIN: 272 botch = "$("; 273 break; 274 275 case LOOKUPEND: 276 botch = "$)"; 277 break; 278 } 279 if (botch != NULL) 280 syserr("Inappropriate use of %s on LHS", 281 botch); 282 } 283 } 284 else 285 syserr("R line: null LHS"); 286 287 /* expand and save the RHS */ 288 while (*++p == '\t') 289 continue; 290 q = p; 291 while (*p != '\0' && *p != '\t') 292 p++; 293 *p = '\0'; 294 expand(q, exbuf, &exbuf[sizeof exbuf], e); 295 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, NULL); 296 if (rwp->r_rhs != NULL) 297 { 298 register char **ap; 299 300 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 301 302 /* check no out-of-bounds replacements */ 303 nfuzzy += '0'; 304 for (ap = rwp->r_rhs; *ap != NULL; ap++) 305 { 306 char *botch; 307 308 botch = NULL; 309 switch (**ap & 0377) 310 { 311 case MATCHREPL: 312 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 313 { 314 syserr("replacement $%c out of bounds", 315 (*ap)[1]); 316 } 317 break; 318 319 case MATCHZANY: 320 botch = "$*"; 321 break; 322 323 case MATCHANY: 324 botch = "$+"; 325 break; 326 327 case MATCHONE: 328 botch = "$-"; 329 break; 330 331 case MATCHCLASS: 332 botch = "$="; 333 break; 334 335 case MATCHNCLASS: 336 botch = "$~"; 337 break; 338 } 339 if (botch != NULL) 340 syserr("Inappropriate use of %s on RHS", 341 botch); 342 } 343 } 344 else 345 syserr("R line: null RHS"); 346 break; 347 348 case 'S': /* select rewriting set */ 349 ruleset = atoi(&bp[1]); 350 if (ruleset >= MAXRWSETS || ruleset < 0) 351 { 352 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 353 ruleset = 0; 354 } 355 rwp = NULL; 356 break; 357 358 case 'D': /* macro definition */ 359 define(bp[1], newstr(munchstring(&bp[2], NULL)), 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 && getuid() == 0) 983 safe = TRUE; 984 if (!safe && strchr("bdeEijLmoprsvC7", opt) == NULL) 985 { 986 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 987 { 988 if (tTd(37, 1)) 989 printf(" (unsafe)"); 990 if (getuid() != geteuid()) 991 { 992 if (tTd(37, 1)) 993 printf("(Resetting uid)"); 994 (void) setgid(getgid()); 995 (void) setuid(getuid()); 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': /* log level */ 1186 LogLevel = atoi(val); 1187 break; 1188 1189 case 'M': /* define macro */ 1190 define(val[0], newstr(&val[1]), CurEnv); 1191 sticky = FALSE; 1192 break; 1193 1194 case 'm': /* send to me too */ 1195 MeToo = atobool(val); 1196 break; 1197 1198 case 'n': /* validate RHS in newaliases */ 1199 CheckAliases = atobool(val); 1200 break; 1201 1202 case 'O': /* daemon options */ 1203 setdaemonoptions(val); 1204 break; 1205 1206 case 'o': /* assume old style headers */ 1207 if (atobool(val)) 1208 CurEnv->e_flags |= EF_OLDSTYLE; 1209 else 1210 CurEnv->e_flags &= ~EF_OLDSTYLE; 1211 break; 1212 1213 case 'p': /* select privacy level */ 1214 p = val; 1215 for (;;) 1216 { 1217 register struct prival *pv; 1218 extern struct prival PrivacyValues[]; 1219 1220 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1221 p++; 1222 if (*p == '\0') 1223 break; 1224 val = p; 1225 while (isascii(*p) && isalnum(*p)) 1226 p++; 1227 if (*p != '\0') 1228 *p++ = '\0'; 1229 1230 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1231 { 1232 if (strcasecmp(val, pv->pv_name) == 0) 1233 break; 1234 } 1235 if (pv->pv_name == NULL) 1236 syserr("readcf: Op line: %s unrecognized", val); 1237 PrivacyFlags |= pv->pv_flag; 1238 } 1239 break; 1240 1241 case 'P': /* postmaster copy address for returned mail */ 1242 PostMasterCopy = newstr(val); 1243 break; 1244 1245 case 'q': /* slope of queue only function */ 1246 QueueFactor = atoi(val); 1247 break; 1248 1249 case 'Q': /* queue directory */ 1250 if (val[0] == '\0') 1251 QueueDir = "mqueue"; 1252 else 1253 QueueDir = newstr(val); 1254 if (RealUid != 0 && !safe) 1255 auth_warning(e, "Processed from queue %s", QueueDir); 1256 break; 1257 1258 case 'R': /* don't prune routes */ 1259 DontPruneRoutes = atobool(val); 1260 break; 1261 1262 case 'r': /* read timeout */ 1263 settimeouts(val); 1264 break; 1265 1266 case 'S': /* status file */ 1267 if (val[0] == '\0') 1268 StatFile = "sendmail.st"; 1269 else 1270 StatFile = newstr(val); 1271 break; 1272 1273 case 's': /* be super safe, even if expensive */ 1274 SuperSafe = atobool(val); 1275 break; 1276 1277 case 'T': /* queue timeout */ 1278 p = strchr(val, '/'); 1279 if (p != NULL) 1280 { 1281 *p++ = '\0'; 1282 TimeOuts.to_q_warning = convtime(p, 'd'); 1283 } 1284 TimeOuts.to_q_return = convtime(val, 'h'); 1285 break; 1286 1287 case 't': /* time zone name */ 1288 TimeZoneSpec = newstr(val); 1289 break; 1290 1291 case 'U': /* location of user database */ 1292 UdbSpec = newstr(val); 1293 break; 1294 1295 case 'u': /* set default uid */ 1296 DefUid = atoi(val); 1297 setdefuser(); 1298 break; 1299 1300 case 'V': /* fallback MX host */ 1301 FallBackMX = newstr(val); 1302 break; 1303 1304 case 'v': /* run in verbose mode */ 1305 Verbose = atobool(val); 1306 break; 1307 1308 case 'x': /* load avg at which to auto-queue msgs */ 1309 QueueLA = atoi(val); 1310 break; 1311 1312 case 'X': /* load avg at which to auto-reject connections */ 1313 RefuseLA = atoi(val); 1314 break; 1315 1316 case 'y': /* work recipient factor */ 1317 WkRecipFact = atoi(val); 1318 break; 1319 1320 case 'Y': /* fork jobs during queue runs */ 1321 ForkQueueRuns = atobool(val); 1322 break; 1323 1324 case 'z': /* work message class factor */ 1325 WkClassFact = atoi(val); 1326 break; 1327 1328 case 'Z': /* work time factor */ 1329 WkTimeFact = atoi(val); 1330 break; 1331 1332 default: 1333 break; 1334 } 1335 if (sticky) 1336 setbitn(opt, StickyOpt); 1337 return; 1338 } 1339 /* 1340 ** SETCLASS -- set a word into a class 1341 ** 1342 ** Parameters: 1343 ** class -- the class to put the word in. 1344 ** word -- the word to enter 1345 ** 1346 ** Returns: 1347 ** none. 1348 ** 1349 ** Side Effects: 1350 ** puts the word into the symbol table. 1351 */ 1352 1353 setclass(class, word) 1354 int class; 1355 char *word; 1356 { 1357 register STAB *s; 1358 1359 if (tTd(37, 8)) 1360 printf("%s added to class %c\n", word, class); 1361 s = stab(word, ST_CLASS, ST_ENTER); 1362 setbitn(class, s->s_class); 1363 } 1364 /* 1365 ** MAKEMAPENTRY -- create a map entry 1366 ** 1367 ** Parameters: 1368 ** line -- the config file line 1369 ** 1370 ** Returns: 1371 ** TRUE if it successfully entered the map entry. 1372 ** FALSE otherwise (usually syntax error). 1373 ** 1374 ** Side Effects: 1375 ** Enters the map into the dictionary. 1376 */ 1377 1378 void 1379 makemapentry(line) 1380 char *line; 1381 { 1382 register char *p; 1383 char *mapname; 1384 char *classname; 1385 register STAB *map; 1386 STAB *class; 1387 1388 for (p = line; isascii(*p) && isspace(*p); p++) 1389 continue; 1390 if (!(isascii(*p) && isalnum(*p))) 1391 { 1392 syserr("readcf: config K line: no map name"); 1393 return; 1394 } 1395 1396 mapname = p; 1397 while (isascii(*++p) && isalnum(*p)) 1398 continue; 1399 if (*p != '\0') 1400 *p++ = '\0'; 1401 while (isascii(*p) && isspace(*p)) 1402 p++; 1403 if (!(isascii(*p) && isalnum(*p))) 1404 { 1405 syserr("readcf: config K line, map %s: no map class", mapname); 1406 return; 1407 } 1408 classname = 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 1416 /* look up the class */ 1417 class = stab(classname, ST_MAPCLASS, ST_FIND); 1418 if (class == NULL) 1419 { 1420 syserr("readcf: map %s: class %s not available", mapname, classname); 1421 return; 1422 } 1423 1424 /* enter the map */ 1425 map = stab(mapname, ST_MAP, ST_ENTER); 1426 map->s_map.map_class = &class->s_mapclass; 1427 map->s_map.map_mname = newstr(mapname); 1428 1429 if (class->s_mapclass.map_parse(&map->s_map, p)) 1430 map->s_map.map_mflags |= MF_VALID; 1431 } 1432 /* 1433 ** SETTIMEOUTS -- parse and set timeout values 1434 ** 1435 ** Parameters: 1436 ** val -- a pointer to the values. If NULL, do initial 1437 ** settings. 1438 ** 1439 ** Returns: 1440 ** none. 1441 ** 1442 ** Side Effects: 1443 ** Initializes the TimeOuts structure 1444 */ 1445 1446 #define MINUTES * 60 1447 #define HOUR * 3600 1448 1449 settimeouts(val) 1450 register char *val; 1451 { 1452 register char *p; 1453 extern time_t convtime(); 1454 1455 if (val == NULL) 1456 { 1457 TimeOuts.to_initial = (time_t) 5 MINUTES; 1458 TimeOuts.to_helo = (time_t) 5 MINUTES; 1459 TimeOuts.to_mail = (time_t) 10 MINUTES; 1460 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1461 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1462 TimeOuts.to_datablock = (time_t) 1 HOUR; 1463 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1464 TimeOuts.to_rset = (time_t) 5 MINUTES; 1465 TimeOuts.to_quit = (time_t) 2 MINUTES; 1466 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1467 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1468 return; 1469 } 1470 1471 for (;; val = p) 1472 { 1473 while (isascii(*val) && isspace(*val)) 1474 val++; 1475 if (*val == '\0') 1476 break; 1477 for (p = val; *p != '\0' && *p != ','; p++) 1478 continue; 1479 if (*p != '\0') 1480 *p++ = '\0'; 1481 1482 if (isascii(*val) && isdigit(*val)) 1483 { 1484 /* old syntax -- set everything */ 1485 TimeOuts.to_mail = convtime(val, 'm'); 1486 TimeOuts.to_rcpt = TimeOuts.to_mail; 1487 TimeOuts.to_datainit = TimeOuts.to_mail; 1488 TimeOuts.to_datablock = TimeOuts.to_mail; 1489 TimeOuts.to_datafinal = TimeOuts.to_mail; 1490 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1491 continue; 1492 } 1493 else 1494 { 1495 register char *q = strchr(val, '='); 1496 time_t to; 1497 1498 if (q == NULL) 1499 { 1500 /* syntax error */ 1501 continue; 1502 } 1503 *q++ = '\0'; 1504 to = convtime(q, 'm'); 1505 1506 if (strcasecmp(val, "initial") == 0) 1507 TimeOuts.to_initial = to; 1508 else if (strcasecmp(val, "mail") == 0) 1509 TimeOuts.to_mail = to; 1510 else if (strcasecmp(val, "rcpt") == 0) 1511 TimeOuts.to_rcpt = to; 1512 else if (strcasecmp(val, "datainit") == 0) 1513 TimeOuts.to_datainit = to; 1514 else if (strcasecmp(val, "datablock") == 0) 1515 TimeOuts.to_datablock = to; 1516 else if (strcasecmp(val, "datafinal") == 0) 1517 TimeOuts.to_datafinal = to; 1518 else if (strcasecmp(val, "command") == 0) 1519 TimeOuts.to_nextcommand = to; 1520 else if (strcasecmp(val, "rset") == 0) 1521 TimeOuts.to_rset = to; 1522 else if (strcasecmp(val, "helo") == 0) 1523 TimeOuts.to_helo = to; 1524 else if (strcasecmp(val, "quit") == 0) 1525 TimeOuts.to_quit = to; 1526 else if (strcasecmp(val, "misc") == 0) 1527 TimeOuts.to_miscshort = to; 1528 else 1529 syserr("settimeouts: invalid timeout %s", val); 1530 } 1531 } 1532 } 1533