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.25 (Berkeley) 04/03/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 case 'D': /* working directory */ 724 m->m_execdir = newstr(p); 725 break; 726 } 727 728 p = delimptr; 729 } 730 731 /* do some heuristic cleanup for back compatibility */ 732 if (bitnset(M_LIMITS, m->m_flags)) 733 { 734 if (m->m_linelimit == 0) 735 m->m_linelimit = SMTPLINELIM; 736 if (ConfigLevel < 2) 737 setbitn(M_7BITS, m->m_flags); 738 } 739 740 /* do some rationality checking */ 741 if (m->m_argv == NULL) 742 { 743 syserr("M%s: A= argument required", m->m_name); 744 return; 745 } 746 if (m->m_mailer == NULL) 747 { 748 syserr("M%s: P= argument required", m->m_name); 749 return; 750 } 751 752 if (NextMailer >= MAXMAILERS) 753 { 754 syserr("too many mailers defined (%d max)", MAXMAILERS); 755 return; 756 } 757 758 s = stab(m->m_name, ST_MAILER, ST_ENTER); 759 if (s->s_mailer != NULL) 760 { 761 i = s->s_mailer->m_mno; 762 free(s->s_mailer); 763 } 764 else 765 { 766 i = NextMailer++; 767 } 768 Mailer[i] = s->s_mailer = m; 769 m->m_mno = i; 770 } 771 /* 772 ** MUNCHSTRING -- translate a string into internal form. 773 ** 774 ** Parameters: 775 ** p -- the string to munch. 776 ** delimptr -- if non-NULL, set to the pointer of the 777 ** field delimiter character. 778 ** 779 ** Returns: 780 ** the munched string. 781 */ 782 783 char * 784 munchstring(p, delimptr) 785 register char *p; 786 char **delimptr; 787 { 788 register char *q; 789 bool backslash = FALSE; 790 bool quotemode = FALSE; 791 static char buf[MAXLINE]; 792 793 for (q = buf; *p != '\0'; p++) 794 { 795 if (backslash) 796 { 797 /* everything is roughly literal */ 798 backslash = FALSE; 799 switch (*p) 800 { 801 case 'r': /* carriage return */ 802 *q++ = '\r'; 803 continue; 804 805 case 'n': /* newline */ 806 *q++ = '\n'; 807 continue; 808 809 case 'f': /* form feed */ 810 *q++ = '\f'; 811 continue; 812 813 case 'b': /* backspace */ 814 *q++ = '\b'; 815 continue; 816 } 817 *q++ = *p; 818 } 819 else 820 { 821 if (*p == '\\') 822 backslash = TRUE; 823 else if (*p == '"') 824 quotemode = !quotemode; 825 else if (quotemode || *p != ',') 826 *q++ = *p; 827 else 828 break; 829 } 830 } 831 832 if (delimptr != NULL) 833 *delimptr = p; 834 *q++ = '\0'; 835 return (buf); 836 } 837 /* 838 ** MAKEARGV -- break up a string into words 839 ** 840 ** Parameters: 841 ** p -- the string to break up. 842 ** 843 ** Returns: 844 ** a char **argv (dynamically allocated) 845 ** 846 ** Side Effects: 847 ** munges p. 848 */ 849 850 char ** 851 makeargv(p) 852 register char *p; 853 { 854 char *q; 855 int i; 856 char **avp; 857 char *argv[MAXPV + 1]; 858 859 /* take apart the words */ 860 i = 0; 861 while (*p != '\0' && i < MAXPV) 862 { 863 q = p; 864 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 865 p++; 866 while (isascii(*p) && isspace(*p)) 867 *p++ = '\0'; 868 argv[i++] = newstr(q); 869 } 870 argv[i++] = NULL; 871 872 /* now make a copy of the argv */ 873 avp = (char **) xalloc(sizeof *avp * i); 874 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 875 876 return (avp); 877 } 878 /* 879 ** PRINTRULES -- print rewrite rules (for debugging) 880 ** 881 ** Parameters: 882 ** none. 883 ** 884 ** Returns: 885 ** none. 886 ** 887 ** Side Effects: 888 ** prints rewrite rules. 889 */ 890 891 printrules() 892 { 893 register struct rewrite *rwp; 894 register int ruleset; 895 896 for (ruleset = 0; ruleset < 10; ruleset++) 897 { 898 if (RewriteRules[ruleset] == NULL) 899 continue; 900 printf("\n----Rule Set %d:", ruleset); 901 902 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 903 { 904 printf("\nLHS:"); 905 printav(rwp->r_lhs); 906 printf("RHS:"); 907 printav(rwp->r_rhs); 908 } 909 } 910 } 911 912 /* 913 ** SETOPTION -- set global processing option 914 ** 915 ** Parameters: 916 ** opt -- option name. 917 ** val -- option value (as a text string). 918 ** safe -- set if this came from a configuration file. 919 ** Some options (if set from the command line) will 920 ** reset the user id to avoid security problems. 921 ** sticky -- if set, don't let other setoptions override 922 ** this value. 923 ** e -- the main envelope. 924 ** 925 ** Returns: 926 ** none. 927 ** 928 ** Side Effects: 929 ** Sets options as implied by the arguments. 930 */ 931 932 static BITMAP StickyOpt; /* set if option is stuck */ 933 934 935 #ifdef NAMED_BIND 936 937 struct resolverflags 938 { 939 char *rf_name; /* name of the flag */ 940 long rf_bits; /* bits to set/clear */ 941 } ResolverFlags[] = 942 { 943 "debug", RES_DEBUG, 944 "aaonly", RES_AAONLY, 945 "usevc", RES_USEVC, 946 "primary", RES_PRIMARY, 947 "igntc", RES_IGNTC, 948 "recurse", RES_RECURSE, 949 "defnames", RES_DEFNAMES, 950 "stayopen", RES_STAYOPEN, 951 "dnsrch", RES_DNSRCH, 952 NULL, 0 953 }; 954 955 #endif 956 957 setoption(opt, val, safe, sticky, e) 958 char opt; 959 char *val; 960 bool safe; 961 bool sticky; 962 register ENVELOPE *e; 963 { 964 register char *p; 965 extern bool atobool(); 966 extern time_t convtime(); 967 extern int QueueLA; 968 extern int RefuseLA; 969 extern bool trusteduser(); 970 extern char *username(); 971 972 if (tTd(37, 1)) 973 printf("setoption %c=%s", opt, val); 974 975 /* 976 ** See if this option is preset for us. 977 */ 978 979 if (bitnset(opt, StickyOpt)) 980 { 981 if (tTd(37, 1)) 982 printf(" (ignored)\n"); 983 return; 984 } 985 986 /* 987 ** Check to see if this option can be specified by this user. 988 */ 989 990 if (!safe && getuid() == 0) 991 safe = TRUE; 992 if (!safe && strchr("bdeEiLmoprsvC8", opt) == NULL) 993 { 994 if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 995 { 996 if (tTd(37, 1)) 997 printf(" (unsafe)"); 998 if (getuid() != geteuid()) 999 { 1000 if (tTd(37, 1)) 1001 printf("(Resetting uid)"); 1002 (void) setgid(getgid()); 1003 (void) setuid(getuid()); 1004 } 1005 } 1006 } 1007 if (tTd(37, 1)) 1008 printf("\n"); 1009 1010 switch (opt) 1011 { 1012 case '8': /* allow eight-bit input */ 1013 EightBit = atobool(val); 1014 break; 1015 1016 case 'A': /* set default alias file */ 1017 if (val[0] == '\0') 1018 AliasFile = "aliases"; 1019 else 1020 AliasFile = newstr(val); 1021 break; 1022 1023 case 'a': /* look N minutes for "@:@" in alias file */ 1024 if (val[0] == '\0') 1025 SafeAlias = 5; 1026 else 1027 SafeAlias = atoi(val); 1028 break; 1029 1030 case 'B': /* substitution for blank character */ 1031 SpaceSub = val[0]; 1032 if (SpaceSub == '\0') 1033 SpaceSub = ' '; 1034 break; 1035 1036 case 'b': /* minimum number of blocks free on queue fs */ 1037 MinBlocksFree = atol(val); 1038 break; 1039 1040 case 'c': /* don't connect to "expensive" mailers */ 1041 NoConnect = atobool(val); 1042 break; 1043 1044 case 'C': /* checkpoint every N addresses */ 1045 CheckpointInterval = atoi(val); 1046 break; 1047 1048 case 'd': /* delivery mode */ 1049 switch (*val) 1050 { 1051 case '\0': 1052 e->e_sendmode = SM_DELIVER; 1053 break; 1054 1055 case SM_QUEUE: /* queue only */ 1056 #ifndef QUEUE 1057 syserr("need QUEUE to set -odqueue"); 1058 #endif /* QUEUE */ 1059 /* fall through..... */ 1060 1061 case SM_DELIVER: /* do everything */ 1062 case SM_FORK: /* fork after verification */ 1063 e->e_sendmode = *val; 1064 break; 1065 1066 default: 1067 syserr("Unknown delivery mode %c", *val); 1068 exit(EX_USAGE); 1069 } 1070 break; 1071 1072 case 'D': /* rebuild alias database as needed */ 1073 AutoRebuild = atobool(val); 1074 break; 1075 1076 case 'E': /* error message header/header file */ 1077 if (*val != '\0') 1078 ErrMsgFile = newstr(val); 1079 break; 1080 1081 case 'e': /* set error processing mode */ 1082 switch (*val) 1083 { 1084 case EM_QUIET: /* be silent about it */ 1085 case EM_MAIL: /* mail back */ 1086 case EM_BERKNET: /* do berknet error processing */ 1087 case EM_WRITE: /* write back (or mail) */ 1088 HoldErrs = TRUE; 1089 /* fall through... */ 1090 1091 case EM_PRINT: /* print errors normally (default) */ 1092 e->e_errormode = *val; 1093 break; 1094 } 1095 break; 1096 1097 case 'F': /* file mode */ 1098 FileMode = atooct(val) & 0777; 1099 break; 1100 1101 case 'f': /* save Unix-style From lines on front */ 1102 SaveFrom = atobool(val); 1103 break; 1104 1105 case 'G': /* match recipients against GECOS field */ 1106 MatchGecos = atobool(val); 1107 break; 1108 1109 case 'g': /* default gid */ 1110 DefGid = atoi(val); 1111 break; 1112 1113 case 'H': /* help file */ 1114 if (val[0] == '\0') 1115 HelpFile = "sendmail.hf"; 1116 else 1117 HelpFile = newstr(val); 1118 break; 1119 1120 case 'h': /* maximum hop count */ 1121 MaxHopCount = atoi(val); 1122 break; 1123 1124 case 'I': /* use internet domain name server */ 1125 #ifdef NAMED_BIND 1126 UseNameServer = TRUE; 1127 for (p = val; *p != 0; ) 1128 { 1129 bool clearmode; 1130 char *q; 1131 struct resolverflags *rfp; 1132 1133 while (*p == ' ') 1134 p++; 1135 if (*p == '\0') 1136 break; 1137 clearmode = FALSE; 1138 if (*p == '-') 1139 clearmode = TRUE; 1140 else if (*p != '+') 1141 p--; 1142 p++; 1143 q = p; 1144 while (*p != '\0' && !(isascii(*p) && isspace(*p))) 1145 p++; 1146 if (*p != '\0') 1147 *p++ = '\0'; 1148 for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 1149 { 1150 if (strcasecmp(q, rfp->rf_name) == 0) 1151 break; 1152 } 1153 if (clearmode) 1154 _res.options &= ~rfp->rf_bits; 1155 else 1156 _res.options |= rfp->rf_bits; 1157 } 1158 if (tTd(8, 2)) 1159 printf("_res.options = %x\n", _res.options); 1160 #else 1161 usrerr("name server (I option) specified but BIND not compiled in"); 1162 #endif 1163 break; 1164 1165 case 'i': /* ignore dot lines in message */ 1166 IgnrDot = atobool(val); 1167 break; 1168 1169 case 'J': /* .forward search path */ 1170 ForwardPath = newstr(val); 1171 break; 1172 1173 case 'k': /* connection cache size */ 1174 MaxMciCache = atoi(val); 1175 if (MaxMciCache < 0) 1176 MaxMciCache = 0; 1177 break; 1178 1179 case 'K': /* connection cache timeout */ 1180 MciCacheTimeout = convtime(val, 'm'); 1181 break; 1182 1183 case 'L': /* log level */ 1184 LogLevel = atoi(val); 1185 break; 1186 1187 case 'M': /* define macro */ 1188 define(val[0], newstr(&val[1]), CurEnv); 1189 sticky = FALSE; 1190 break; 1191 1192 case 'm': /* send to me too */ 1193 MeToo = atobool(val); 1194 break; 1195 1196 case 'n': /* validate RHS in newaliases */ 1197 CheckAliases = atobool(val); 1198 break; 1199 1200 case 'O': /* daemon options */ 1201 setdaemonoptions(val); 1202 break; 1203 1204 case 'o': /* assume old style headers */ 1205 if (atobool(val)) 1206 CurEnv->e_flags |= EF_OLDSTYLE; 1207 else 1208 CurEnv->e_flags &= ~EF_OLDSTYLE; 1209 break; 1210 1211 case 'p': /* select privacy level */ 1212 p = val; 1213 for (;;) 1214 { 1215 register struct prival *pv; 1216 extern struct prival PrivacyValues[]; 1217 1218 while (isascii(*p) && (isspace(*p) || ispunct(*p))) 1219 p++; 1220 if (*p == '\0') 1221 break; 1222 val = p; 1223 while (isascii(*p) && isalnum(*p)) 1224 p++; 1225 if (*p != '\0') 1226 *p++ = '\0'; 1227 1228 for (pv = PrivacyValues; pv->pv_name != NULL; pv++) 1229 { 1230 if (strcasecmp(val, pv->pv_name) == 0) 1231 break; 1232 } 1233 if (pv->pv_name == NULL) 1234 syserr("readcf: Op line: %s unrecognized", val); 1235 PrivacyFlags |= pv->pv_flag; 1236 } 1237 break; 1238 1239 case 'P': /* postmaster copy address for returned mail */ 1240 PostMasterCopy = newstr(val); 1241 break; 1242 1243 case 'q': /* slope of queue only function */ 1244 QueueFactor = atoi(val); 1245 break; 1246 1247 case 'Q': /* queue directory */ 1248 if (val[0] == '\0') 1249 QueueDir = "mqueue"; 1250 else 1251 QueueDir = newstr(val); 1252 if (RealUid != 0 && !safe) 1253 auth_warning(e, "Processed from queue %s", QueueDir); 1254 break; 1255 1256 case 'R': /* don't prune routes */ 1257 DontPruneRoutes = atobool(val); 1258 break; 1259 1260 case 'r': /* read timeout */ 1261 settimeouts(val); 1262 break; 1263 1264 case 'S': /* status file */ 1265 if (val[0] == '\0') 1266 StatFile = "sendmail.st"; 1267 else 1268 StatFile = newstr(val); 1269 break; 1270 1271 case 's': /* be super safe, even if expensive */ 1272 SuperSafe = atobool(val); 1273 break; 1274 1275 case 'T': /* queue timeout */ 1276 p = strchr(val, '/'); 1277 if (p != NULL) 1278 { 1279 *p++ = '\0'; 1280 TimeOuts.to_q_warning = convtime(p, 'd'); 1281 } 1282 TimeOuts.to_q_return = convtime(val, 'h'); 1283 break; 1284 1285 case 't': /* time zone name */ 1286 TimeZoneSpec = newstr(val); 1287 break; 1288 1289 case 'U': /* location of user database */ 1290 UdbSpec = newstr(val); 1291 break; 1292 1293 case 'u': /* set default uid */ 1294 DefUid = atoi(val); 1295 setdefuser(); 1296 break; 1297 1298 case 'V': /* fallback MX host */ 1299 FallBackMX = newstr(val); 1300 break; 1301 1302 case 'v': /* run in verbose mode */ 1303 Verbose = atobool(val); 1304 break; 1305 1306 case 'x': /* load avg at which to auto-queue msgs */ 1307 QueueLA = atoi(val); 1308 break; 1309 1310 case 'X': /* load avg at which to auto-reject connections */ 1311 RefuseLA = atoi(val); 1312 break; 1313 1314 case 'y': /* work recipient factor */ 1315 WkRecipFact = atoi(val); 1316 break; 1317 1318 case 'Y': /* fork jobs during queue runs */ 1319 ForkQueueRuns = atobool(val); 1320 break; 1321 1322 case 'z': /* work message class factor */ 1323 WkClassFact = atoi(val); 1324 break; 1325 1326 case 'Z': /* work time factor */ 1327 WkTimeFact = atoi(val); 1328 break; 1329 1330 default: 1331 break; 1332 } 1333 if (sticky) 1334 setbitn(opt, StickyOpt); 1335 return; 1336 } 1337 /* 1338 ** SETCLASS -- set a word into a class 1339 ** 1340 ** Parameters: 1341 ** class -- the class to put the word in. 1342 ** word -- the word to enter 1343 ** 1344 ** Returns: 1345 ** none. 1346 ** 1347 ** Side Effects: 1348 ** puts the word into the symbol table. 1349 */ 1350 1351 setclass(class, word) 1352 int class; 1353 char *word; 1354 { 1355 register STAB *s; 1356 1357 if (tTd(37, 8)) 1358 printf("%s added to class %c\n", word, class); 1359 s = stab(word, ST_CLASS, ST_ENTER); 1360 setbitn(class, s->s_class); 1361 } 1362 /* 1363 ** MAKEMAPENTRY -- create a map entry 1364 ** 1365 ** Parameters: 1366 ** line -- the config file line 1367 ** 1368 ** Returns: 1369 ** TRUE if it successfully entered the map entry. 1370 ** FALSE otherwise (usually syntax error). 1371 ** 1372 ** Side Effects: 1373 ** Enters the map into the dictionary. 1374 */ 1375 1376 void 1377 makemapentry(line) 1378 char *line; 1379 { 1380 register char *p; 1381 char *mapname; 1382 char *classname; 1383 register STAB *map; 1384 STAB *class; 1385 1386 for (p = line; isascii(*p) && isspace(*p); p++) 1387 continue; 1388 if (!(isascii(*p) && isalnum(*p))) 1389 { 1390 syserr("readcf: config K line: no map name"); 1391 return; 1392 } 1393 1394 mapname = p; 1395 while (isascii(*++p) && isalnum(*p)) 1396 continue; 1397 if (*p != '\0') 1398 *p++ = '\0'; 1399 while (isascii(*p) && isspace(*p)) 1400 p++; 1401 if (!(isascii(*p) && isalnum(*p))) 1402 { 1403 syserr("readcf: config K line, map %s: no map class", mapname); 1404 return; 1405 } 1406 classname = p; 1407 while (isascii(*++p) && isalnum(*p)) 1408 continue; 1409 if (*p != '\0') 1410 *p++ = '\0'; 1411 while (isascii(*p) && isspace(*p)) 1412 p++; 1413 1414 /* look up the class */ 1415 class = stab(classname, ST_MAPCLASS, ST_FIND); 1416 if (class == NULL) 1417 { 1418 syserr("readcf: map %s: class %s not available", mapname, classname); 1419 return; 1420 } 1421 1422 /* enter the map */ 1423 map = stab(mapname, ST_MAP, ST_ENTER); 1424 map->s_map.map_class = &class->s_mapclass; 1425 1426 if ((*class->s_mapclass.map_init)(&map->s_map, mapname, p)) 1427 map->s_map.map_flags |= MF_VALID; 1428 } 1429 /* 1430 ** SETTIMEOUTS -- parse and set timeout values 1431 ** 1432 ** Parameters: 1433 ** val -- a pointer to the values. If NULL, do initial 1434 ** settings. 1435 ** 1436 ** Returns: 1437 ** none. 1438 ** 1439 ** Side Effects: 1440 ** Initializes the TimeOuts structure 1441 */ 1442 1443 #define MINUTES * 60 1444 #define HOUR * 3600 1445 1446 settimeouts(val) 1447 register char *val; 1448 { 1449 register char *p; 1450 extern time_t convtime(); 1451 1452 if (val == NULL) 1453 { 1454 TimeOuts.to_initial = (time_t) 5 MINUTES; 1455 TimeOuts.to_helo = (time_t) 5 MINUTES; 1456 TimeOuts.to_mail = (time_t) 10 MINUTES; 1457 TimeOuts.to_rcpt = (time_t) 1 HOUR; 1458 TimeOuts.to_datainit = (time_t) 5 MINUTES; 1459 TimeOuts.to_datablock = (time_t) 1 HOUR; 1460 TimeOuts.to_datafinal = (time_t) 1 HOUR; 1461 TimeOuts.to_rset = (time_t) 5 MINUTES; 1462 TimeOuts.to_quit = (time_t) 2 MINUTES; 1463 TimeOuts.to_nextcommand = (time_t) 1 HOUR; 1464 TimeOuts.to_miscshort = (time_t) 2 MINUTES; 1465 return; 1466 } 1467 1468 for (;; val = p) 1469 { 1470 while (isascii(*val) && isspace(*val)) 1471 val++; 1472 if (*val == '\0') 1473 break; 1474 for (p = val; *p != '\0' && *p != ','; p++) 1475 continue; 1476 if (*p != '\0') 1477 *p++ = '\0'; 1478 1479 if (isascii(*val) && isdigit(*val)) 1480 { 1481 /* old syntax -- set everything */ 1482 TimeOuts.to_mail = convtime(val, 'm'); 1483 TimeOuts.to_rcpt = TimeOuts.to_mail; 1484 TimeOuts.to_datainit = TimeOuts.to_mail; 1485 TimeOuts.to_datablock = TimeOuts.to_mail; 1486 TimeOuts.to_datafinal = TimeOuts.to_mail; 1487 TimeOuts.to_nextcommand = TimeOuts.to_mail; 1488 continue; 1489 } 1490 else 1491 { 1492 register char *q = strchr(val, '='); 1493 time_t to; 1494 1495 if (q == NULL) 1496 { 1497 /* syntax error */ 1498 continue; 1499 } 1500 *q++ = '\0'; 1501 to = convtime(q, 'm'); 1502 1503 if (strcasecmp(val, "initial") == 0) 1504 TimeOuts.to_initial = to; 1505 else if (strcasecmp(val, "mail") == 0) 1506 TimeOuts.to_mail = to; 1507 else if (strcasecmp(val, "rcpt") == 0) 1508 TimeOuts.to_rcpt = to; 1509 else if (strcasecmp(val, "datainit") == 0) 1510 TimeOuts.to_datainit = to; 1511 else if (strcasecmp(val, "datablock") == 0) 1512 TimeOuts.to_datablock = to; 1513 else if (strcasecmp(val, "datafinal") == 0) 1514 TimeOuts.to_datafinal = to; 1515 else if (strcasecmp(val, "command") == 0) 1516 TimeOuts.to_nextcommand = to; 1517 else if (strcasecmp(val, "rset") == 0) 1518 TimeOuts.to_rset = to; 1519 else if (strcasecmp(val, "helo") == 0) 1520 TimeOuts.to_helo = to; 1521 else if (strcasecmp(val, "quit") == 0) 1522 TimeOuts.to_quit = to; 1523 else if (strcasecmp(val, "misc") == 0) 1524 TimeOuts.to_miscshort = to; 1525 else 1526 syserr("settimeouts: invalid timeout %s", val); 1527 } 1528 } 1529 } 1530