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.24 (Berkeley) 03/30/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 nfuzzy++; 243 break; 244 245 case MATCHREPL: 246 botch = "$0-$9"; 247 break; 248 249 case CANONNET: 250 botch = "$#"; 251 break; 252 253 case CANONUSER: 254 botch = "$:"; 255 break; 256 257 case CALLSUBR: 258 botch = "$>"; 259 break; 260 261 case CONDIF: 262 botch = "$?"; 263 break; 264 265 case CONDELSE: 266 botch = "$|"; 267 break; 268 269 case CONDFI: 270 botch = "$."; 271 break; 272 273 case HOSTBEGIN: 274 botch = "$["; 275 break; 276 277 case HOSTEND: 278 botch = "$]"; 279 break; 280 281 case LOOKUPBEGIN: 282 botch = "$("; 283 break; 284 285 case LOOKUPEND: 286 botch = "$)"; 287 break; 288 } 289 if (botch != NULL) 290 syserr("Inappropriate use of %s on LHS", 291 botch); 292 } 293 } 294 else 295 syserr("R line: null LHS"); 296 297 /* expand and save the RHS */ 298 while (*++p == '\t') 299 continue; 300 q = p; 301 while (*p != '\0' && *p != '\t') 302 p++; 303 *p = '\0'; 304 expand(q, exbuf, &exbuf[sizeof exbuf], e); 305 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf, NULL); 306 if (rwp->r_rhs != NULL) 307 { 308 register char **ap; 309 310 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 311 312 /* check no out-of-bounds replacements */ 313 nfuzzy += '0'; 314 for (ap = rwp->r_rhs; *ap != NULL; ap++) 315 { 316 char *botch; 317 318 botch = NULL; 319 switch (**ap & 0377) 320 { 321 case MATCHREPL: 322 if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 323 { 324 syserr("replacement $%c out of bounds", 325 (*ap)[1]); 326 } 327 break; 328 329 case MATCHZANY: 330 botch = "$*"; 331 break; 332 333 case MATCHANY: 334 botch = "$+"; 335 break; 336 337 case MATCHONE: 338 botch = "$-"; 339 break; 340 341 case MATCHCLASS: 342 botch = "$="; 343 break; 344 345 case MATCHNCLASS: 346 botch = "$~"; 347 break; 348 } 349 if (botch != NULL) 350 syserr("Inappropriate use of %s on RHS", 351 botch); 352 } 353 } 354 else 355 syserr("R line: null RHS"); 356 break; 357 358 case 'S': /* select rewriting set */ 359 ruleset = atoi(&bp[1]); 360 if (ruleset >= MAXRWSETS || ruleset < 0) 361 { 362 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 363 ruleset = 0; 364 } 365 rwp = NULL; 366 break; 367 368 case 'D': /* macro definition */ 369 define(bp[1], newstr(munchstring(&bp[2], NULL)), e); 370 break; 371 372 case 'H': /* required header line */ 373 (void) chompheader(&bp[1], TRUE, e); 374 break; 375 376 case 'C': /* word class */ 377 case 'F': /* word class from file */ 378 /* read list of words from argument or file */ 379 if (bp[0] == 'F') 380 { 381 /* read from file */ 382 for (p = &bp[2]; 383 *p != '\0' && !(isascii(*p) && isspace(*p)); 384 p++) 385 continue; 386 if (*p == '\0') 387 p = "%s"; 388 else 389 { 390 *p = '\0'; 391 while (isascii(*++p) && isspace(*p)) 392 continue; 393 } 394 fileclass(bp[1], &bp[2], p, safe); 395 break; 396 } 397 398 /* scan the list of words and set class for all */ 399 for (p = &bp[2]; *p != '\0'; ) 400 { 401 register char *wd; 402 char delim; 403 404 while (*p != '\0' && isascii(*p) && isspace(*p)) 405 p++; 406 wd = p; 407 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 408 p++; 409 delim = *p; 410 *p = '\0'; 411 if (wd[0] != '\0') 412 { 413 if (tTd(37, 2)) 414 printf("setclass(%c, %s)\n", 415 bp[1], wd); 416 setclass(bp[1], wd); 417 } 418 *p = delim; 419 } 420 break; 421 422 case 'M': /* define mailer */ 423 makemailer(&bp[1]); 424 break; 425 426 case 'O': /* set option */ 427 setoption(bp[1], &bp[2], safe, FALSE, e); 428 break; 429 430 case 'P': /* set precedence */ 431 if (NumPriorities >= MAXPRIORITIES) 432 { 433 toomany('P', MAXPRIORITIES); 434 break; 435 } 436 for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 437 continue; 438 if (*p == '\0') 439 goto badline; 440 *p = '\0'; 441 Priorities[NumPriorities].pri_name = newstr(&bp[1]); 442 Priorities[NumPriorities].pri_val = atoi(++p); 443 NumPriorities++; 444 break; 445 446 case 'T': /* trusted user(s) */ 447 /* this option is obsolete, but will be ignored */ 448 break; 449 450 case 'V': /* configuration syntax version */ 451 ConfigLevel = atoi(&bp[1]); 452 break; 453 454 case 'K': 455 makemapentry(&bp[1]); 456 break; 457 458 default: 459 badline: 460 syserr("unknown control line \"%s\"", bp); 461 } 462 if (bp != buf) 463 free(bp); 464 } 465 if (ferror(cf)) 466 { 467 syserr("I/O read error", cfname); 468 exit(EX_OSFILE); 469 } 470 fclose(cf); 471 FileName = NULL; 472 473 if (stab("host", ST_MAP, ST_FIND) == NULL) 474 { 475 /* user didn't initialize: set up host map */ 476 strcpy(buf, "host host"); 477 if (ConfigLevel >= 2) 478 strcat(buf, " -a."); 479 makemapentry(buf); 480 } 481 } 482 /* 483 ** TOOMANY -- signal too many of some option 484 ** 485 ** Parameters: 486 ** id -- the id of the error line 487 ** maxcnt -- the maximum possible values 488 ** 489 ** Returns: 490 ** none. 491 ** 492 ** Side Effects: 493 ** gives a syserr. 494 */ 495 496 toomany(id, maxcnt) 497 char id; 498 int maxcnt; 499 { 500 syserr("too many %c lines, %d max", id, maxcnt); 501 } 502 /* 503 ** FILECLASS -- read members of a class from a file 504 ** 505 ** Parameters: 506 ** class -- class to define. 507 ** filename -- name of file to read. 508 ** fmt -- scanf string to use for match. 509 ** 510 ** Returns: 511 ** none 512 ** 513 ** Side Effects: 514 ** 515 ** puts all lines in filename that match a scanf into 516 ** the named class. 517 */ 518 519 fileclass(class, filename, fmt, safe) 520 int class; 521 char *filename; 522 char *fmt; 523 bool safe; 524 { 525 FILE *f; 526 struct stat stbuf; 527 char buf[MAXLINE]; 528 529 if (stat(filename, &stbuf) < 0) 530 { 531 syserr("fileclass: cannot stat %s", filename); 532 return; 533 } 534 if (!S_ISREG(stbuf.st_mode)) 535 { 536 syserr("fileclass: %s not a regular file", filename); 537 return; 538 } 539 if (!safe && access(filename, R_OK) < 0) 540 { 541 syserr("fileclass: access denied on %s", filename); 542 return; 543 } 544 f = fopen(filename, "r"); 545 if (f == NULL) 546 { 547 syserr("fileclass: cannot open %s", filename); 548 return; 549 } 550 551 while (fgets(buf, sizeof buf, f) != NULL) 552 { 553 register STAB *s; 554 register char *p; 555 # ifdef SCANF 556 char wordbuf[MAXNAME+1]; 557 558 if (sscanf(buf, fmt, wordbuf) != 1) 559 continue; 560 p = wordbuf; 561 # else /* SCANF */ 562 p = buf; 563 # endif /* SCANF */ 564 565 /* 566 ** Break up the match into words. 567 */ 568 569 while (*p != '\0') 570 { 571 register char *q; 572 573 /* strip leading spaces */ 574 while (isascii(*p) && isspace(*p)) 575 p++; 576 if (*p == '\0') 577 break; 578 579 /* find the end of the word */ 580 q = p; 581 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 582 p++; 583 if (*p != '\0') 584 *p++ = '\0'; 585 586 /* enter the word in the symbol table */ 587 s = stab(q, ST_CLASS, ST_ENTER); 588 setbitn(class, s->s_class); 589 } 590 } 591 592 (void) fclose(f); 593 } 594 /* 595 ** MAKEMAILER -- define a new mailer. 596 ** 597 ** Parameters: 598 ** line -- description of mailer. This is in labeled 599 ** fields. The fields are: 600 ** P -- the path to the mailer 601 ** F -- the flags associated with the mailer 602 ** A -- the argv for this mailer 603 ** S -- the sender rewriting set 604 ** R -- the recipient rewriting set 605 ** E -- the eol string 606 ** The first word is the canonical name of the mailer. 607 ** 608 ** Returns: 609 ** none. 610 ** 611 ** Side Effects: 612 ** enters the mailer into the mailer table. 613 */ 614 615 makemailer(line) 616 char *line; 617 { 618 register char *p; 619 register struct mailer *m; 620 register STAB *s; 621 int i; 622 char fcode; 623 auto char *endp; 624 extern int NextMailer; 625 extern char **makeargv(); 626 extern char *munchstring(); 627 extern long atol(); 628 629 /* allocate a mailer and set up defaults */ 630 m = (struct mailer *) xalloc(sizeof *m); 631 bzero((char *) m, sizeof *m); 632 m->m_eol = "\n"; 633 634 /* collect the mailer name */ 635 for (p = line; *p != '\0' && *p != ',' && !(isascii(*p) && isspace(*p)); p++) 636 continue; 637 if (*p != '\0') 638 *p++ = '\0'; 639 m->m_name = newstr(line); 640 641 /* now scan through and assign info from the fields */ 642 while (*p != '\0') 643 { 644 auto char *delimptr; 645 646 while (*p != '\0' && (*p == ',' || (isascii(*p) && isspace(*p)))) 647 p++; 648 649 /* p now points to field code */ 650 fcode = *p; 651 while (*p != '\0' && *p != '=' && *p != ',') 652 p++; 653 if (*p++ != '=') 654 { 655 syserr("mailer %s: `=' expected", m->m_name); 656 return; 657 } 658 while (isascii(*p) && isspace(*p)) 659 p++; 660 661 /* p now points to the field body */ 662 p = munchstring(p, &delimptr); 663 664 /* install the field into the mailer struct */ 665 switch (fcode) 666 { 667 case 'P': /* pathname */ 668 m->m_mailer = newstr(p); 669 break; 670 671 case 'F': /* flags */ 672 for (; *p != '\0'; p++) 673 if (!(isascii(*p) && isspace(*p))) 674 setbitn(*p, m->m_flags); 675 break; 676 677 case 'S': /* sender rewriting ruleset */ 678 case 'R': /* recipient rewriting ruleset */ 679 i = strtol(p, &endp, 10); 680 if (i < 0 || i >= MAXRWSETS) 681 { 682 syserr("invalid rewrite set, %d max", MAXRWSETS); 683 return; 684 } 685 if (fcode == 'S') 686 m->m_sh_rwset = m->m_se_rwset = i; 687 else 688 m->m_rh_rwset = m->m_re_rwset = i; 689 690 p = endp; 691 if (*p == '/') 692 { 693 i = strtol(p, NULL, 10); 694 if (i < 0 || i >= MAXRWSETS) 695 { 696 syserr("invalid rewrite set, %d max", 697 MAXRWSETS); 698 return; 699 } 700 if (fcode == 'S') 701 m->m_sh_rwset = i; 702 else 703 m->m_rh_rwset = i; 704 } 705 break; 706 707 case 'E': /* end of line string */ 708 m->m_eol = newstr(p); 709 break; 710 711 case 'A': /* argument vector */ 712 m->m_argv = makeargv(p); 713 break; 714 715 case 'M': /* maximum message size */ 716 m->m_maxsize = atol(p); 717 break; 718 719 case 'L': /* maximum line length */ 720 m->m_linelimit = atoi(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': /* minimum number of blocks free on queue fs */ 1033 MinBlocksFree = atol(val); 1034 break; 1035 1036 case 'c': /* don't connect to "expensive" mailers */ 1037 NoConnect = atobool(val); 1038 break; 1039 1040 case 'C': /* checkpoint every N addresses */ 1041 CheckpointInterval = atoi(val); 1042 break; 1043 1044 case 'd': /* delivery mode */ 1045 switch (*val) 1046 { 1047 case '\0': 1048 e->e_sendmode = SM_DELIVER; 1049 break; 1050 1051 case SM_QUEUE: /* queue only */ 1052 #ifndef QUEUE 1053 syserr("need QUEUE to set -odqueue"); 1054 #endif /* QUEUE */ 1055 /* fall through..... */ 1056 1057 case SM_DELIVER: /* do everything */ 1058 case SM_FORK: /* fork after verification */ 1059 e->e_sendmode = *val; 1060 break; 1061 1062 default: 1063 syserr("Unknown delivery mode %c", *val); 1064 exit(EX_USAGE); 1065 } 1066 break; 1067 1068 case 'D': /* rebuild alias database as needed */ 1069 AutoRebuild = atobool(val); 1070 break; 1071 1072 case 'E': /* error message header/header file */ 1073 if (*val != '\0') 1074 ErrMsgFile = newstr(val); 1075 break; 1076 1077 case 'e': /* set error processing mode */ 1078 switch (*val) 1079 { 1080 case EM_QUIET: /* be silent about it */ 1081 case EM_MAIL: /* mail back */ 1082 case EM_BERKNET: /* do berknet error processing */ 1083 case EM_WRITE: /* write back (or mail) */ 1084 HoldErrs = TRUE; 1085 /* fall through... */ 1086 1087 case EM_PRINT: /* print errors normally (default) */ 1088 e->e_errormode = *val; 1089 break; 1090 } 1091 break; 1092 1093 case 'F': /* file mode */ 1094 FileMode = atooct(val) & 0777; 1095 break; 1096 1097 case 'f': /* save Unix-style From lines on front */ 1098 SaveFrom = atobool(val); 1099 break; 1100 1101 case 'G': /* match recipients against GECOS field */ 1102 MatchGecos = atobool(val); 1103 break; 1104 1105 case 'g': /* default gid */ 1106 DefGid = atoi(val); 1107 break; 1108 1109 case 'H': /* help file */ 1110 if (val[0] == '\0') 1111 HelpFile = "sendmail.hf"; 1112 else 1113 HelpFile = newstr(val); 1114 break; 1115 1116 case 'h': /* maximum hop count */ 1117 MaxHopCount = atoi(val); 1118 break; 1119 1120 case 'I': /* use internet domain name server */ 1121 #ifdef NAMED_BIND 1122 UseNameServer = TRUE; 1123 for (p = val; *p != 0; ) 1124 { 1125 bool clearmode; 1126 char *q; 1127 struct resolverflags *rfp; 1128 1129 while (*p == ' ') 1130 p++; 1131 if (*p == '\0') 1132 break; 1133 clearmode = FALSE; 1134 if (*p == '-') 1135 clearmode = TRUE; 1136 else if (*p != '+') 1137 p--; 1138 p++; 1139 q = p; 1140 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1141 p++; 1142 if (*p != '\0') 1143 *p++ = '\0'; 1144 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1145 { 1146 if (strcasecmp(q, rfp->rf_name) == 0) 1147 break; 1148 } 1149 if (clearmode) 1150 _res.options &= ~rfp->rf_bits; 1151 else 1152 _res.options |= rfp->rf_bits; 1153 } 1154 if (tTd(8, 2)) 1155 printf("_res.options = %x\n", _res.options); 1156 #else 1157 usrerr("name server (I option) specified but BIND not compiled in"); 1158 #endif 1159 break; 1160 1161 case 'i': /* ignore dot lines in message */ 1162 IgnrDot = atobool(val); 1163 break; 1164 1165 case 'J': /* .forward search path */ 1166 ForwardPath = newstr(val); 1167 break; 1168 1169 case 'k': /* connection cache size */ 1170 MaxMciCache = atoi(val); 1171 if (MaxMciCache < 0) 1172 MaxMciCache = 0; 1173 break; 1174 1175 case 'K': /* connection cache timeout */ 1176 MciCacheTimeout = convtime(val, 'm'); 1177 break; 1178 1179 case 'L': /* log level */ 1180 LogLevel = atoi(val); 1181 break; 1182 1183 case 'M': /* define macro */ 1184 define(val[0], newstr(&val[1]), CurEnv); 1185 sticky = FALSE; 1186 break; 1187 1188 case 'm': /* send to me too */ 1189 MeToo = atobool(val); 1190 break; 1191 1192 case 'n': /* validate RHS in newaliases */ 1193 CheckAliases = atobool(val); 1194 break; 1195 1196 case 'O': /* daemon options */ 1197 setdaemonoptions(val); 1198 break; 1199 1200 case 'o': /* assume old style headers */ 1201 if (atobool(val)) 1202 CurEnv->e_flags |= EF_OLDSTYLE; 1203 else 1204 CurEnv->e_flags &= ~EF_OLDSTYLE; 1205 break; 1206 1207 case 'p': /* select privacy level */ 1208 p = val; 1209 for (;;) 1210 { 1211 register struct prival *pv; 1212 extern struct prival PrivacyValues[]; 1213 1214 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1215 p++; 1216 if (*p == '\0') 1217 break; 1218 val = p; 1219 while (isascii(*p) && isalnum(*p)) 1220 p++; 1221 if (*p != '\0') 1222 *p++ = '\0'; 1223 1224 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1225 { 1226 if (strcasecmp(val, pv->pv_name) == 0) 1227 break; 1228 } 1229 if (pv->pv_name == NULL) 1230 syserr("readcf: Op line: %s unrecognized", val); 1231 PrivacyFlags |= pv->pv_flag; 1232 } 1233 break; 1234 1235 case 'P': /* postmaster copy address for returned mail */ 1236 PostMasterCopy = newstr(val); 1237 break; 1238 1239 case 'q': /* slope of queue only function */ 1240 QueueFactor = atoi(val); 1241 break; 1242 1243 case 'Q': /* queue directory */ 1244 if (val[0] == '\0') 1245 QueueDir = "mqueue"; 1246 else 1247 QueueDir = newstr(val); 1248 if (RealUid != 0 && !safe) 1249 auth_warning(e, "Processed from queue %s", QueueDir); 1250 break; 1251 1252 case 'R': /* don't prune routes */ 1253 DontPruneRoutes = atobool(val); 1254 break; 1255 1256 case 'r': /* read timeout */ 1257 settimeouts(val); 1258 break; 1259 1260 case 'S': /* status file */ 1261 if (val[0] == '\0') 1262 StatFile = "sendmail.st"; 1263 else 1264 StatFile = newstr(val); 1265 break; 1266 1267 case 's': /* be super safe, even if expensive */ 1268 SuperSafe = atobool(val); 1269 break; 1270 1271 case 'T': /* queue timeout */ 1272 p = strchr(val, '/'); 1273 if (p != NULL) 1274 { 1275 *p++ = '\0'; 1276 TimeOuts.to_q_warning = convtime(p, 'd'); 1277 } 1278 TimeOuts.to_q_return = convtime(val, 'h'); 1279 break; 1280 1281 case 't': /* time zone name */ 1282 TimeZoneSpec = newstr(val); 1283 break; 1284 1285 case 'U': /* location of user database */ 1286 UdbSpec = newstr(val); 1287 break; 1288 1289 case 'u': /* set default uid */ 1290 DefUid = atoi(val); 1291 setdefuser(); 1292 break; 1293 1294 case 'V': /* fallback MX host */ 1295 FallBackMX = newstr(val); 1296 break; 1297 1298 case 'v': /* run in verbose mode */ 1299 Verbose = atobool(val); 1300 break; 1301 1302 case 'x': /* load avg at which to auto-queue msgs */ 1303 QueueLA = atoi(val); 1304 break; 1305 1306 case 'X': /* load avg at which to auto-reject connections */ 1307 RefuseLA = atoi(val); 1308 break; 1309 1310 case 'y': /* work recipient factor */ 1311 WkRecipFact = atoi(val); 1312 break; 1313 1314 case 'Y': /* fork jobs during queue runs */ 1315 ForkQueueRuns = atobool(val); 1316 break; 1317 1318 case 'z': /* work message class factor */ 1319 WkClassFact = atoi(val); 1320 break; 1321 1322 case 'Z': /* work time factor */ 1323 WkTimeFact = atoi(val); 1324 break; 1325 1326 default: 1327 break; 1328 } 1329 if (sticky) 1330 setbitn(opt, StickyOpt); 1331 return; 1332 } 1333 /* 1334 ** SETCLASS -- set a word into a class 1335 ** 1336 ** Parameters: 1337 ** class -- the class to put the word in. 1338 ** word -- the word to enter 1339 ** 1340 ** Returns: 1341 ** none. 1342 ** 1343 ** Side Effects: 1344 ** puts the word into the symbol table. 1345 */ 1346 1347 setclass(class, word) 1348 int class; 1349 char *word; 1350 { 1351 register STAB *s; 1352 1353 if (tTd(37, 8)) 1354 printf("%s added to class %c\n", word, class); 1355 s = stab(word, ST_CLASS, ST_ENTER); 1356 setbitn(class, s->s_class); 1357 } 1358 /* 1359 ** MAKEMAPENTRY -- create a map entry 1360 ** 1361 ** Parameters: 1362 ** line -- the config file line 1363 ** 1364 ** Returns: 1365 ** TRUE if it successfully entered the map entry. 1366 ** FALSE otherwise (usually syntax error). 1367 ** 1368 ** Side Effects: 1369 ** Enters the map into the dictionary. 1370 */ 1371 1372 void 1373 makemapentry(line) 1374 char *line; 1375 { 1376 register char *p; 1377 char *mapname; 1378 char *classname; 1379 register STAB *map; 1380 STAB *class; 1381 1382 for (p = line; isascii(*p) && isspace(*p); p++) 1383 continue; 1384 if (!(isascii(*p) && isalnum(*p))) 1385 { 1386 syserr("readcf: config K line: no map name"); 1387 return; 1388 } 1389 1390 mapname = p; 1391 while (isascii(*++p) && isalnum(*p)) 1392 continue; 1393 if (*p != '\0') 1394 *p++ = '\0'; 1395 while (isascii(*p) && isspace(*p)) 1396 p++; 1397 if (!(isascii(*p) && isalnum(*p))) 1398 { 1399 syserr("readcf: config K line, map %s: no map class", mapname); 1400 return; 1401 } 1402 classname = p; 1403 while (isascii(*++p) && isalnum(*p)) 1404 continue; 1405 if (*p != '\0') 1406 *p++ = '\0'; 1407 while (isascii(*p) && isspace(*p)) 1408 p++; 1409 1410 /* look up the class */ 1411 class = stab(classname, ST_MAPCLASS, ST_FIND); 1412 if (class == NULL) 1413 { 1414 syserr("readcf: map %s: class %s not available", mapname, classname); 1415 return; 1416 } 1417 1418 /* enter the map */ 1419 map = stab(mapname, ST_MAP, ST_ENTER); 1420 map->s_map.map_class = &class->s_mapclass; 1421 1422 if ((*class->s_mapclass.map_init)(&map->s_map, mapname, p)) 1423 map->s_map.map_flags |= MF_VALID; 1424 } 1425 /* 1426 ** SETTIMEOUTS -- parse and set timeout values 1427 ** 1428 ** Parameters: 1429 ** val -- a pointer to the values. If NULL, do initial 1430 ** settings. 1431 ** 1432 ** Returns: 1433 ** none. 1434 ** 1435 ** Side Effects: 1436 ** Initializes the TimeOuts structure 1437 */ 1438 1439 #define MINUTES * 60 1440 #define HOUR * 3600 1441 1442 settimeouts(val) 1443 register char *val; 1444 { 1445 register char *p; 1446 extern time_t convtime(); 1447 1448 if (val == NULL) 1449 { 1450 TimeOuts.to_initial = (time_t) 5 MINUTES; 1451 TimeOuts.to_helo = (time_t) 5 MINUTES; 1452 TimeOuts.to_mail = (time_t) 10 MINUTES; 1453 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1454 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1455 TimeOuts.to_datablock = (time_t) 1 HOUR; 1456 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1457 TimeOuts.to_rset = (time_t) 5 MINUTES; 1458 TimeOuts.to_quit = (time_t) 2 MINUTES; 1459 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1460 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1461 return; 1462 } 1463 1464 for (;; val = p) 1465 { 1466 while (isascii(*val) && isspace(*val)) 1467 val++; 1468 if (*val == '\0') 1469 break; 1470 for (p = val; *p != '\0' && *p != ','; p++) 1471 continue; 1472 if (*p != '\0') 1473 *p++ = '\0'; 1474 1475 if (isascii(*val) && isdigit(*val)) 1476 { 1477 /* old syntax -- set everything */ 1478 TimeOuts.to_mail = convtime(val, 'm'); 1479 TimeOuts.to_rcpt = TimeOuts.to_mail; 1480 TimeOuts.to_datainit = TimeOuts.to_mail; 1481 TimeOuts.to_datablock = TimeOuts.to_mail; 1482 TimeOuts.to_datafinal = TimeOuts.to_mail; 1483 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1484 continue; 1485 } 1486 else 1487 { 1488 register char *q = strchr(val, '='); 1489 time_t to; 1490 1491 if (q == NULL) 1492 { 1493 /* syntax error */ 1494 continue; 1495 } 1496 *q++ = '\0'; 1497 to = convtime(q, 'm'); 1498 1499 if (strcasecmp(val, "initial") == 0) 1500 TimeOuts.to_initial = to; 1501 else if (strcasecmp(val, "mail") == 0) 1502 TimeOuts.to_mail = to; 1503 else if (strcasecmp(val, "rcpt") == 0) 1504 TimeOuts.to_rcpt = to; 1505 else if (strcasecmp(val, "datainit") == 0) 1506 TimeOuts.to_datainit = to; 1507 else if (strcasecmp(val, "datablock") == 0) 1508 TimeOuts.to_datablock = to; 1509 else if (strcasecmp(val, "datafinal") == 0) 1510 TimeOuts.to_datafinal = to; 1511 else if (strcasecmp(val, "command") == 0) 1512 TimeOuts.to_nextcommand = to; 1513 else if (strcasecmp(val, "rset") == 0) 1514 TimeOuts.to_rset = to; 1515 else if (strcasecmp(val, "helo") == 0) 1516 TimeOuts.to_helo = to; 1517 else if (strcasecmp(val, "quit") == 0) 1518 TimeOuts.to_quit = to; 1519 else if (strcasecmp(val, "misc") == 0) 1520 TimeOuts.to_miscshort = to; 1521 else 1522 syserr("settimeouts: invalid timeout %s", val); 1523 } 1524 } 1525 } 1526