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