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