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.16 (Berkeley) 03/01/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); 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 ** 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) 954 char opt; 955 char *val; 956 bool safe; 957 bool sticky; 958 { 959 register char *p; 960 extern bool atobool(); 961 extern time_t convtime(); 962 extern int QueueLA; 963 extern int RefuseLA; 964 extern bool trusteduser(); 965 extern char *username(); 966 967 if (tTd(37, 1)) 968 printf("setoption %c=%s", opt, val); 969 970 /* 971 ** See if this option is preset for us. 972 */ 973 974 if (bitnset(opt, StickyOpt)) 975 { 976 if (tTd(37, 1)) 977 printf(" (ignored)\n"); 978 return; 979 } 980 981 /* 982 ** Check to see if this option can be specified by this user. 983 */ 984 985 if (!safe && getuid() == 0) 986 safe = TRUE; 987 if (!safe && strchr("bdeEiLmoprsvC8", opt) == NULL) 988 { 989 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 990 { 991 if (tTd(37, 1)) 992 printf(" (unsafe)"); 993 if (getuid() != geteuid()) 994 { 995 if (tTd(37, 1)) 996 printf("(Resetting uid)"); 997 (void) setgid(getgid()); 998 (void) setuid(getuid()); 999 } 1000 } 1001 } 1002 if (tTd(37, 1)) 1003 printf("\n"); 1004 1005 switch (opt) 1006 { 1007 case '8': /* allow eight-bit input */ 1008 EightBit = atobool(val); 1009 break; 1010 1011 case 'A': /* set default alias file */ 1012 if (val[0] == '\0') 1013 AliasFile = "aliases"; 1014 else 1015 AliasFile = newstr(val); 1016 break; 1017 1018 case 'a': /* look N minutes for "@:@" in alias file */ 1019 if (val[0] == '\0') 1020 SafeAlias = 5; 1021 else 1022 SafeAlias = atoi(val); 1023 break; 1024 1025 case 'B': /* substitution for blank character */ 1026 SpaceSub = val[0]; 1027 if (SpaceSub == '\0') 1028 SpaceSub = ' '; 1029 break; 1030 1031 case 'b': /* minimum number of blocks free on queue fs */ 1032 MinBlocksFree = atol(val); 1033 break; 1034 1035 case 'c': /* don't connect to "expensive" mailers */ 1036 NoConnect = atobool(val); 1037 break; 1038 1039 case 'C': /* checkpoint every N addresses */ 1040 CheckpointInterval = atoi(val); 1041 break; 1042 1043 case 'd': /* delivery mode */ 1044 switch (*val) 1045 { 1046 case '\0': 1047 SendMode = SM_DELIVER; 1048 break; 1049 1050 case SM_QUEUE: /* queue only */ 1051 #ifndef QUEUE 1052 syserr("need QUEUE to set -odqueue"); 1053 #endif /* QUEUE */ 1054 /* fall through..... */ 1055 1056 case SM_DELIVER: /* do everything */ 1057 case SM_FORK: /* fork after verification */ 1058 SendMode = *val; 1059 break; 1060 1061 default: 1062 syserr("Unknown delivery mode %c", *val); 1063 exit(EX_USAGE); 1064 } 1065 break; 1066 1067 case 'D': /* rebuild alias database as needed */ 1068 AutoRebuild = atobool(val); 1069 break; 1070 1071 case 'E': /* error message header/header file */ 1072 if (*val != '\0') 1073 ErrMsgFile = newstr(val); 1074 break; 1075 1076 case 'e': /* set error processing mode */ 1077 switch (*val) 1078 { 1079 case EM_QUIET: /* be silent about it */ 1080 case EM_MAIL: /* mail back */ 1081 case EM_BERKNET: /* do berknet error processing */ 1082 case EM_WRITE: /* write back (or mail) */ 1083 HoldErrs = TRUE; 1084 /* fall through... */ 1085 1086 case EM_PRINT: /* print errors normally (default) */ 1087 ErrorMode = *val; 1088 break; 1089 } 1090 break; 1091 1092 case 'F': /* file mode */ 1093 FileMode = atooct(val) & 0777; 1094 break; 1095 1096 case 'f': /* save Unix-style From lines on front */ 1097 SaveFrom = atobool(val); 1098 break; 1099 1100 case 'G': /* match recipients against GECOS field */ 1101 MatchGecos = atobool(val); 1102 break; 1103 1104 case 'g': /* default gid */ 1105 DefGid = atoi(val); 1106 break; 1107 1108 case 'H': /* help file */ 1109 if (val[0] == '\0') 1110 HelpFile = "sendmail.hf"; 1111 else 1112 HelpFile = newstr(val); 1113 break; 1114 1115 case 'h': /* maximum hop count */ 1116 MaxHopCount = atoi(val); 1117 break; 1118 1119 case 'I': /* use internet domain name server */ 1120 #ifdef NAMED_BIND 1121 UseNameServer = TRUE; 1122 for (p = val; *p != 0; ) 1123 { 1124 bool clearmode; 1125 char *q; 1126 struct resolverflags *rfp; 1127 1128 while (*p == ' ') 1129 p++; 1130 if (*p == '\0') 1131 break; 1132 clearmode = FALSE; 1133 if (*p == '-') 1134 clearmode = TRUE; 1135 else if (*p != '+') 1136 p--; 1137 p++; 1138 q = p; 1139 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1140 p++; 1141 if (*p != '\0') 1142 *p++ = '\0'; 1143 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1144 { 1145 if (strcasecmp(q, rfp->rf_name) == 0) 1146 break; 1147 } 1148 if (clearmode) 1149 _res.options &= ~rfp->rf_bits; 1150 else 1151 _res.options |= rfp->rf_bits; 1152 } 1153 if (tTd(8, 2)) 1154 printf("_res.options = %x\n", _res.options); 1155 #else 1156 usrerr("name server (I option) specified but BIND not compiled in"); 1157 #endif 1158 break; 1159 1160 case 'i': /* ignore dot lines in message */ 1161 IgnrDot = atobool(val); 1162 break; 1163 1164 case 'J': /* .forward search path */ 1165 ForwardPath = newstr(val); 1166 break; 1167 1168 case 'k': /* connection cache size */ 1169 MaxMciCache = atoi(val); 1170 if (MaxMciCache < 0) 1171 MaxMciCache = 0; 1172 break; 1173 1174 case 'K': /* connection cache timeout */ 1175 MciCacheTimeout = convtime(val); 1176 break; 1177 1178 case 'L': /* log level */ 1179 LogLevel = atoi(val); 1180 break; 1181 1182 case 'M': /* define macro */ 1183 define(val[0], newstr(&val[1]), CurEnv); 1184 sticky = FALSE; 1185 break; 1186 1187 case 'm': /* send to me too */ 1188 MeToo = atobool(val); 1189 break; 1190 1191 case 'n': /* validate RHS in newaliases */ 1192 CheckAliases = atobool(val); 1193 break; 1194 1195 case 'o': /* assume old style headers */ 1196 if (atobool(val)) 1197 CurEnv->e_flags |= EF_OLDSTYLE; 1198 else 1199 CurEnv->e_flags &= ~EF_OLDSTYLE; 1200 break; 1201 1202 case 'p': /* select privacy level */ 1203 p = val; 1204 for (;;) 1205 { 1206 register struct prival *pv; 1207 extern struct prival PrivacyValues[]; 1208 1209 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1210 p++; 1211 if (*p == '\0') 1212 break; 1213 val = p; 1214 while (isascii(*p) && isalnum(*p)) 1215 p++; 1216 if (*p != '\0') 1217 *p++ = '\0'; 1218 1219 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1220 { 1221 if (strcasecmp(val, pv->pv_name) == 0) 1222 break; 1223 } 1224 PrivacyFlags |= pv->pv_flag; 1225 } 1226 break; 1227 1228 case 'P': /* postmaster copy address for returned mail */ 1229 PostMasterCopy = newstr(val); 1230 break; 1231 1232 case 'q': /* slope of queue only function */ 1233 QueueFactor = atoi(val); 1234 break; 1235 1236 case 'Q': /* queue directory */ 1237 if (val[0] == '\0') 1238 QueueDir = "mqueue"; 1239 else 1240 QueueDir = newstr(val); 1241 break; 1242 1243 case 'R': /* don't prune routes */ 1244 DontPruneRoutes = atobool(val); 1245 break; 1246 1247 case 'r': /* read timeout */ 1248 settimeouts(val); 1249 break; 1250 1251 case 'S': /* status file */ 1252 if (val[0] == '\0') 1253 StatFile = "sendmail.st"; 1254 else 1255 StatFile = newstr(val); 1256 break; 1257 1258 case 's': /* be super safe, even if expensive */ 1259 SuperSafe = atobool(val); 1260 break; 1261 1262 case 'T': /* queue timeout */ 1263 TimeOut = convtime(val); 1264 break; 1265 1266 case 't': /* time zone name */ 1267 TimeZoneSpec = newstr(val); 1268 break; 1269 1270 case 'U': /* location of user database */ 1271 UdbSpec = newstr(val); 1272 break; 1273 1274 case 'u': /* set default uid */ 1275 DefUid = atoi(val); 1276 setdefuser(); 1277 break; 1278 1279 case 'v': /* run in verbose mode */ 1280 Verbose = atobool(val); 1281 break; 1282 1283 case 'x': /* load avg at which to auto-queue msgs */ 1284 QueueLA = atoi(val); 1285 break; 1286 1287 case 'X': /* load avg at which to auto-reject connections */ 1288 RefuseLA = atoi(val); 1289 break; 1290 1291 case 'y': /* work recipient factor */ 1292 WkRecipFact = atoi(val); 1293 break; 1294 1295 case 'Y': /* fork jobs during queue runs */ 1296 ForkQueueRuns = atobool(val); 1297 break; 1298 1299 case 'z': /* work message class factor */ 1300 WkClassFact = atoi(val); 1301 break; 1302 1303 case 'Z': /* work time factor */ 1304 WkTimeFact = atoi(val); 1305 break; 1306 1307 default: 1308 break; 1309 } 1310 if (sticky) 1311 setbitn(opt, StickyOpt); 1312 return; 1313 } 1314 /* 1315 ** SETCLASS -- set a word into a class 1316 ** 1317 ** Parameters: 1318 ** class -- the class to put the word in. 1319 ** word -- the word to enter 1320 ** 1321 ** Returns: 1322 ** none. 1323 ** 1324 ** Side Effects: 1325 ** puts the word into the symbol table. 1326 */ 1327 1328 setclass(class, word) 1329 int class; 1330 char *word; 1331 { 1332 register STAB *s; 1333 1334 if (tTd(37, 8)) 1335 printf("%s added to class %c\n", word, class); 1336 s = stab(word, ST_CLASS, ST_ENTER); 1337 setbitn(class, s->s_class); 1338 } 1339 /* 1340 ** MAKEMAPENTRY -- create a map entry 1341 ** 1342 ** Parameters: 1343 ** line -- the config file line 1344 ** 1345 ** Returns: 1346 ** TRUE if it successfully entered the map entry. 1347 ** FALSE otherwise (usually syntax error). 1348 ** 1349 ** Side Effects: 1350 ** Enters the map into the dictionary. 1351 */ 1352 1353 void 1354 makemapentry(line) 1355 char *line; 1356 { 1357 register char *p; 1358 char *mapname; 1359 char *classname; 1360 register STAB *map; 1361 STAB *class; 1362 1363 for (p = line; isascii(*p) && isspace(*p); p++) 1364 continue; 1365 if (!(isascii(*p) && isalnum(*p))) 1366 { 1367 syserr("readcf: config K line: no map name"); 1368 return; 1369 } 1370 1371 mapname = p; 1372 while (isascii(*++p) && isalnum(*p)) 1373 continue; 1374 if (*p != '\0') 1375 *p++ = '\0'; 1376 while (isascii(*p) && isspace(*p)) 1377 p++; 1378 if (!(isascii(*p) && isalnum(*p))) 1379 { 1380 syserr("readcf: config K line, map %s: no map class", mapname); 1381 return; 1382 } 1383 classname = p; 1384 while (isascii(*++p) && isalnum(*p)) 1385 continue; 1386 if (*p != '\0') 1387 *p++ = '\0'; 1388 while (isascii(*p) && isspace(*p)) 1389 p++; 1390 1391 /* look up the class */ 1392 class = stab(classname, ST_MAPCLASS, ST_FIND); 1393 if (class == NULL) 1394 { 1395 syserr("readcf: map %s: class %s not available", mapname, classname); 1396 return; 1397 } 1398 1399 /* enter the map */ 1400 map = stab(mapname, ST_MAP, ST_ENTER); 1401 map->s_map.map_class = &class->s_mapclass; 1402 1403 if ((*class->s_mapclass.map_init)(&map->s_map, mapname, p)) 1404 map->s_map.map_flags |= MF_VALID; 1405 } 1406 /* 1407 ** SETTIMEOUTS -- parse and set timeout values 1408 ** 1409 ** Parameters: 1410 ** val -- a pointer to the values. If NULL, do initial 1411 ** settings. 1412 ** 1413 ** Returns: 1414 ** none. 1415 ** 1416 ** Side Effects: 1417 ** Initializes the TimeOuts structure 1418 */ 1419 1420 #define MINUTES * 60 1421 #define HOUR * 3600 1422 1423 settimeouts(val) 1424 register char *val; 1425 { 1426 register char *p; 1427 1428 if (val == NULL) 1429 { 1430 TimeOuts.to_initial = (time_t) 5 MINUTES; 1431 TimeOuts.to_helo = (time_t) 5 MINUTES; 1432 TimeOuts.to_mail = (time_t) 10 MINUTES; 1433 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1434 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1435 TimeOuts.to_datablock = (time_t) 1 HOUR; 1436 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1437 TimeOuts.to_rset = (time_t) 5 MINUTES; 1438 TimeOuts.to_quit = (time_t) 2 MINUTES; 1439 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1440 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1441 return; 1442 } 1443 1444 for (;; val = p) 1445 { 1446 while (isascii(*val) && isspace(*val)) 1447 val++; 1448 if (*val == '\0') 1449 break; 1450 for (p = val; *p != '\0' && *p != ','; p++) 1451 continue; 1452 if (*p != '\0') 1453 *p++ = '\0'; 1454 1455 if (isascii(*val) && isdigit(*val)) 1456 { 1457 /* old syntax -- set everything */ 1458 TimeOuts.to_mail = convtime(val); 1459 TimeOuts.to_rcpt = TimeOuts.to_mail; 1460 TimeOuts.to_datainit = TimeOuts.to_mail; 1461 TimeOuts.to_datablock = TimeOuts.to_mail; 1462 TimeOuts.to_datafinal = TimeOuts.to_mail; 1463 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1464 continue; 1465 } 1466 else 1467 { 1468 register char *q = strchr(val, '='); 1469 time_t to; 1470 1471 if (q == NULL) 1472 { 1473 /* syntax error */ 1474 continue; 1475 } 1476 *q++ = '\0'; 1477 to = convtime(q); 1478 1479 if (strcasecmp(val, "initial") == 0) 1480 TimeOuts.to_initial = to; 1481 else if (strcasecmp(val, "mail") == 0) 1482 TimeOuts.to_mail = to; 1483 else if (strcasecmp(val, "rcpt") == 0) 1484 TimeOuts.to_rcpt = to; 1485 else if (strcasecmp(val, "datainit") == 0) 1486 TimeOuts.to_datainit = to; 1487 else if (strcasecmp(val, "datablock") == 0) 1488 TimeOuts.to_datablock = to; 1489 else if (strcasecmp(val, "datafinal") == 0) 1490 TimeOuts.to_datafinal = to; 1491 else if (strcasecmp(val, "command") == 0) 1492 TimeOuts.to_nextcommand = to; 1493 else if (strcasecmp(val, "rset") == 0) 1494 TimeOuts.to_rset = to; 1495 else if (strcasecmp(val, "helo") == 0) 1496 TimeOuts.to_helo = to; 1497 else if (strcasecmp(val, "quit") == 0) 1498 TimeOuts.to_quit = to; 1499 else if (strcasecmp(val, "misc") == 0) 1500 TimeOuts.to_miscshort = to; 1501 else 1502 syserr("settimeouts: invalid timeout %s", val); 1503 } 1504 } 1505 } 1506