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