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