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