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