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