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