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