1 /* 2 ** Sendmail 3 ** Copyright (c) 1983 Eric P. Allman 4 ** Berkeley, California 5 ** 6 ** Copyright (c) 1983 Regents of the University of California. 7 ** All rights reserved. The Berkeley software License Agreement 8 ** specifies the terms and conditions for redistribution. 9 */ 10 11 #ifndef lint 12 static char SccsId[] = "@(#)readcf.c 5.3.1.1 (Berkeley) 09/19/85"; 13 #endif not lint 14 15 # include "sendmail.h" 16 17 /* 18 ** READCF -- read control file. 19 ** 20 ** This routine reads the control file and builds the internal 21 ** form. 22 ** 23 ** The file is formatted as a sequence of lines, each taken 24 ** atomically. The first character of each line describes how 25 ** the line is to be interpreted. The lines are: 26 ** Dxval Define macro x to have value val. 27 ** Cxword Put word into class x. 28 ** Fxfile [fmt] Read file for lines to put into 29 ** class x. Use scanf string 'fmt' 30 ** or "%s" if not present. Fmt should 31 ** only produce one string-valued result. 32 ** Hname: value Define header with field-name 'name' 33 ** and value as specified; this will be 34 ** macro expanded immediately before 35 ** use. 36 ** Sn Use rewriting set n. 37 ** Rlhs rhs Rewrite addresses that match lhs to 38 ** be rhs. 39 ** Mn arg=val... Define mailer. n is the internal name. 40 ** Args specify mailer parameters. 41 ** Oxvalue Set option x to value. 42 ** Pname=value Set precedence name to value. 43 ** 44 ** Parameters: 45 ** cfname -- control file name. 46 ** 47 ** Returns: 48 ** none. 49 ** 50 ** Side Effects: 51 ** Builds several internal tables. 52 */ 53 54 readcf(cfname) 55 char *cfname; 56 { 57 FILE *cf; 58 int ruleset = 0; 59 char *q; 60 char **pv; 61 struct rewrite *rwp = NULL; 62 char buf[MAXLINE]; 63 register char *p; 64 extern char **prescan(); 65 extern char **copyplist(); 66 char exbuf[MAXLINE]; 67 char pvpbuf[PSBUFSIZE]; 68 extern char *fgetfolded(); 69 extern char *munchstring(); 70 71 cf = fopen(cfname, "r"); 72 if (cf == NULL) 73 { 74 syserr("cannot open %s", cfname); 75 exit(EX_OSFILE); 76 } 77 78 FileName = cfname; 79 LineNumber = 0; 80 while (fgetfolded(buf, sizeof buf, cf) != NULL) 81 { 82 /* map $ into \001 (ASCII SOH) for macro expansion */ 83 for (p = buf; *p != '\0'; p++) 84 { 85 if (*p != '$') 86 continue; 87 88 if (p[1] == '$') 89 { 90 /* actual dollar sign.... */ 91 (void) strcpy(p, p + 1); 92 continue; 93 } 94 95 /* convert to macro expansion character */ 96 *p = '\001'; 97 } 98 99 /* interpret this line */ 100 switch (buf[0]) 101 { 102 case '\0': 103 case '#': /* comment */ 104 break; 105 106 case 'R': /* rewriting rule */ 107 for (p = &buf[1]; *p != '\0' && *p != '\t'; p++) 108 continue; 109 110 if (*p == '\0') 111 { 112 syserr("invalid rewrite line \"%s\"", buf); 113 break; 114 } 115 116 /* allocate space for the rule header */ 117 if (rwp == NULL) 118 { 119 RewriteRules[ruleset] = rwp = 120 (struct rewrite *) xalloc(sizeof *rwp); 121 } 122 else 123 { 124 rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 125 rwp = rwp->r_next; 126 } 127 rwp->r_next = NULL; 128 129 /* expand and save the LHS */ 130 *p = '\0'; 131 expand(&buf[1], exbuf, &exbuf[sizeof exbuf], CurEnv); 132 rwp->r_lhs = prescan(exbuf, '\t', pvpbuf); 133 if (rwp->r_lhs != NULL) 134 rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 135 136 /* expand and save the RHS */ 137 while (*++p == '\t') 138 continue; 139 q = p; 140 while (*p != '\0' && *p != '\t') 141 p++; 142 *p = '\0'; 143 expand(q, exbuf, &exbuf[sizeof exbuf], CurEnv); 144 rwp->r_rhs = prescan(exbuf, '\t', pvpbuf); 145 if (rwp->r_rhs != NULL) 146 rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 147 break; 148 149 case 'S': /* select rewriting set */ 150 ruleset = atoi(&buf[1]); 151 if (ruleset >= MAXRWSETS || ruleset < 0) 152 { 153 syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 154 ruleset = 0; 155 } 156 rwp = NULL; 157 break; 158 159 case 'D': /* macro definition */ 160 define(buf[1], newstr(munchstring(&buf[2])), CurEnv); 161 break; 162 163 case 'H': /* required header line */ 164 (void) chompheader(&buf[1], TRUE); 165 break; 166 167 case 'C': /* word class */ 168 case 'F': /* word class from file */ 169 /* read list of words from argument or file */ 170 if (buf[0] == 'F') 171 { 172 /* read from file */ 173 for (p = &buf[2]; *p != '\0' && !isspace(*p); p++) 174 continue; 175 if (*p == '\0') 176 p = "%s"; 177 else 178 { 179 *p = '\0'; 180 while (isspace(*++p)) 181 continue; 182 } 183 fileclass(buf[1], &buf[2], p); 184 break; 185 } 186 187 /* scan the list of words and set class for all */ 188 for (p = &buf[2]; *p != '\0'; ) 189 { 190 register char *wd; 191 char delim; 192 193 while (*p != '\0' && isspace(*p)) 194 p++; 195 wd = p; 196 while (*p != '\0' && !isspace(*p)) 197 p++; 198 delim = *p; 199 *p = '\0'; 200 if (wd[0] != '\0') 201 setclass(buf[1], wd); 202 *p = delim; 203 } 204 break; 205 206 case 'M': /* define mailer */ 207 makemailer(&buf[1]); 208 break; 209 210 case 'O': /* set option */ 211 setoption(buf[1], &buf[2], TRUE, FALSE); 212 break; 213 214 case 'P': /* set precedence */ 215 if (NumPriorities >= MAXPRIORITIES) 216 { 217 toomany('P', MAXPRIORITIES); 218 break; 219 } 220 for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 221 continue; 222 if (*p == '\0') 223 goto badline; 224 *p = '\0'; 225 Priorities[NumPriorities].pri_name = newstr(&buf[1]); 226 Priorities[NumPriorities].pri_val = atoi(++p); 227 NumPriorities++; 228 break; 229 230 case 'T': /* trusted user(s) */ 231 p = &buf[1]; 232 while (*p != '\0') 233 { 234 while (isspace(*p)) 235 p++; 236 q = p; 237 while (*p != '\0' && !isspace(*p)) 238 p++; 239 if (*p != '\0') 240 *p++ = '\0'; 241 if (*q == '\0') 242 continue; 243 for (pv = TrustedUsers; *pv != NULL; pv++) 244 continue; 245 if (pv >= &TrustedUsers[MAXTRUST]) 246 { 247 toomany('T', MAXTRUST); 248 break; 249 } 250 *pv = newstr(q); 251 } 252 break; 253 254 default: 255 badline: 256 syserr("unknown control line \"%s\"", buf); 257 } 258 } 259 FileName = NULL; 260 } 261 /* 262 ** TOOMANY -- signal too many of some option 263 ** 264 ** Parameters: 265 ** id -- the id of the error line 266 ** maxcnt -- the maximum possible values 267 ** 268 ** Returns: 269 ** none. 270 ** 271 ** Side Effects: 272 ** gives a syserr. 273 */ 274 275 toomany(id, maxcnt) 276 char id; 277 int maxcnt; 278 { 279 syserr("too many %c lines, %d max", id, maxcnt); 280 } 281 /* 282 ** FILECLASS -- read members of a class from a file 283 ** 284 ** Parameters: 285 ** class -- class to define. 286 ** filename -- name of file to read. 287 ** fmt -- scanf string to use for match. 288 ** 289 ** Returns: 290 ** none 291 ** 292 ** Side Effects: 293 ** 294 ** puts all lines in filename that match a scanf into 295 ** the named class. 296 */ 297 298 fileclass(class, filename, fmt) 299 int class; 300 char *filename; 301 char *fmt; 302 { 303 register FILE *f; 304 char buf[MAXLINE]; 305 306 f = fopen(filename, "r"); 307 if (f == NULL) 308 { 309 syserr("cannot open %s", filename); 310 return; 311 } 312 313 while (fgets(buf, sizeof buf, f) != NULL) 314 { 315 register STAB *s; 316 char wordbuf[MAXNAME+1]; 317 318 if (sscanf(buf, fmt, wordbuf) != 1) 319 continue; 320 s = stab(wordbuf, ST_CLASS, ST_ENTER); 321 setbitn(class, s->s_class); 322 } 323 324 (void) fclose(f); 325 } 326 /* 327 ** MAKEMAILER -- define a new mailer. 328 ** 329 ** Parameters: 330 ** line -- description of mailer. This is in labeled 331 ** fields. The fields are: 332 ** P -- the path to the mailer 333 ** F -- the flags associated with the mailer 334 ** A -- the argv for this mailer 335 ** S -- the sender rewriting set 336 ** R -- the recipient rewriting set 337 ** E -- the eol string 338 ** The first word is the canonical name of the mailer. 339 ** 340 ** Returns: 341 ** none. 342 ** 343 ** Side Effects: 344 ** enters the mailer into the mailer table. 345 */ 346 347 makemailer(line) 348 char *line; 349 { 350 register char *p; 351 register struct mailer *m; 352 register STAB *s; 353 int i; 354 char fcode; 355 extern int NextMailer; 356 extern char **makeargv(); 357 extern char *munchstring(); 358 extern char *DelimChar; 359 extern long atol(); 360 361 /* allocate a mailer and set up defaults */ 362 m = (struct mailer *) xalloc(sizeof *m); 363 bzero((char *) m, sizeof *m); 364 m->m_mno = NextMailer; 365 m->m_eol = "\n"; 366 367 /* collect the mailer name */ 368 for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++) 369 continue; 370 if (*p != '\0') 371 *p++ = '\0'; 372 m->m_name = newstr(line); 373 374 /* now scan through and assign info from the fields */ 375 while (*p != '\0') 376 { 377 while (*p != '\0' && (*p == ',' || isspace(*p))) 378 p++; 379 380 /* p now points to field code */ 381 fcode = *p; 382 while (*p != '\0' && *p != '=' && *p != ',') 383 p++; 384 if (*p++ != '=') 385 { 386 syserr("`=' expected"); 387 return; 388 } 389 while (isspace(*p)) 390 p++; 391 392 /* p now points to the field body */ 393 p = munchstring(p); 394 395 /* install the field into the mailer struct */ 396 switch (fcode) 397 { 398 case 'P': /* pathname */ 399 m->m_mailer = newstr(p); 400 break; 401 402 case 'F': /* flags */ 403 for (; *p != '\0'; p++) 404 setbitn(*p, m->m_flags); 405 break; 406 407 case 'S': /* sender rewriting ruleset */ 408 case 'R': /* recipient rewriting ruleset */ 409 i = atoi(p); 410 if (i < 0 || i >= MAXRWSETS) 411 { 412 syserr("invalid rewrite set, %d max", MAXRWSETS); 413 return; 414 } 415 if (fcode == 'S') 416 m->m_s_rwset = i; 417 else 418 m->m_r_rwset = i; 419 break; 420 421 case 'E': /* end of line string */ 422 m->m_eol = newstr(p); 423 break; 424 425 case 'A': /* argument vector */ 426 m->m_argv = makeargv(p); 427 break; 428 429 case 'M': /* maximum message size */ 430 m->m_maxsize = atol(p); 431 break; 432 } 433 434 p = DelimChar; 435 } 436 437 /* now store the mailer away */ 438 if (NextMailer >= MAXMAILERS) 439 { 440 syserr("too many mailers defined (%d max)", MAXMAILERS); 441 return; 442 } 443 Mailer[NextMailer++] = m; 444 s = stab(m->m_name, ST_MAILER, ST_ENTER); 445 s->s_mailer = m; 446 } 447 /* 448 ** MUNCHSTRING -- translate a string into internal form. 449 ** 450 ** Parameters: 451 ** p -- the string to munch. 452 ** 453 ** Returns: 454 ** the munched string. 455 ** 456 ** Side Effects: 457 ** Sets "DelimChar" to point to the string that caused us 458 ** to stop. 459 */ 460 461 char * 462 munchstring(p) 463 register char *p; 464 { 465 register char *q; 466 bool backslash = FALSE; 467 bool quotemode = FALSE; 468 static char buf[MAXLINE]; 469 extern char *DelimChar; 470 471 for (q = buf; *p != '\0'; p++) 472 { 473 if (backslash) 474 { 475 /* everything is roughly literal */ 476 backslash = FALSE; 477 switch (*p) 478 { 479 case 'r': /* carriage return */ 480 *q++ = '\r'; 481 continue; 482 483 case 'n': /* newline */ 484 *q++ = '\n'; 485 continue; 486 487 case 'f': /* form feed */ 488 *q++ = '\f'; 489 continue; 490 491 case 'b': /* backspace */ 492 *q++ = '\b'; 493 continue; 494 } 495 *q++ = *p; 496 } 497 else 498 { 499 if (*p == '\\') 500 backslash = TRUE; 501 else if (*p == '"') 502 quotemode = !quotemode; 503 else if (quotemode || *p != ',') 504 *q++ = *p; 505 else 506 break; 507 } 508 } 509 510 DelimChar = p; 511 *q++ = '\0'; 512 return (buf); 513 } 514 /* 515 ** MAKEARGV -- break up a string into words 516 ** 517 ** Parameters: 518 ** p -- the string to break up. 519 ** 520 ** Returns: 521 ** a char **argv (dynamically allocated) 522 ** 523 ** Side Effects: 524 ** munges p. 525 */ 526 527 char ** 528 makeargv(p) 529 register char *p; 530 { 531 char *q; 532 int i; 533 char **avp; 534 char *argv[MAXPV + 1]; 535 536 /* take apart the words */ 537 i = 0; 538 while (*p != '\0' && i < MAXPV) 539 { 540 q = p; 541 while (*p != '\0' && !isspace(*p)) 542 p++; 543 while (isspace(*p)) 544 *p++ = '\0'; 545 argv[i++] = newstr(q); 546 } 547 argv[i++] = NULL; 548 549 /* now make a copy of the argv */ 550 avp = (char **) xalloc(sizeof *avp * i); 551 bcopy((char *) argv, (char *) avp, sizeof *avp * i); 552 553 return (avp); 554 } 555 /* 556 ** PRINTRULES -- print rewrite rules (for debugging) 557 ** 558 ** Parameters: 559 ** none. 560 ** 561 ** Returns: 562 ** none. 563 ** 564 ** Side Effects: 565 ** prints rewrite rules. 566 */ 567 568 # ifdef DEBUG 569 570 printrules() 571 { 572 register struct rewrite *rwp; 573 register int ruleset; 574 575 for (ruleset = 0; ruleset < 10; ruleset++) 576 { 577 if (RewriteRules[ruleset] == NULL) 578 continue; 579 printf("\n----Rule Set %d:", ruleset); 580 581 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 582 { 583 printf("\nLHS:"); 584 printav(rwp->r_lhs); 585 printf("RHS:"); 586 printav(rwp->r_rhs); 587 } 588 } 589 } 590 591 # endif DEBUG 592 /* 593 ** SETOPTION -- set global processing option 594 ** 595 ** Parameters: 596 ** opt -- option name. 597 ** val -- option value (as a text string). 598 ** safe -- set if this came from a configuration file. 599 ** Some options (if set from the command line) will 600 ** reset the user id to avoid security problems. 601 ** sticky -- if set, don't let other setoptions override 602 ** this value. 603 ** 604 ** Returns: 605 ** none. 606 ** 607 ** Side Effects: 608 ** Sets options as implied by the arguments. 609 */ 610 611 static BITMAP StickyOpt; /* set if option is stuck */ 612 extern char *WizWord; /* the stored wizard password */ 613 extern char *NetName; /* name of home (local) network */ 614 615 setoption(opt, val, safe, sticky) 616 char opt; 617 char *val; 618 bool safe; 619 bool sticky; 620 { 621 extern bool atobool(); 622 extern time_t convtime(); 623 extern int QueueLA; 624 extern int RefuseLA; 625 extern bool trusteduser(); 626 extern char *username(); 627 628 # ifdef DEBUG 629 if (tTd(37, 1)) 630 printf("setoption %c=%s", opt, val); 631 # endif DEBUG 632 633 /* 634 ** See if this option is preset for us. 635 */ 636 637 if (bitnset(opt, StickyOpt)) 638 { 639 # ifdef DEBUG 640 if (tTd(37, 1)) 641 printf(" (ignored)\n"); 642 # endif DEBUG 643 return; 644 } 645 646 /* 647 ** Check to see if this option can be specified by this user. 648 */ 649 650 if (!safe && getruid()) 651 safe = TRUE; 652 if (!safe && index("deiLmorsv", opt) == NULL) 653 { 654 # ifdef DEBUG 655 if (tTd(37, 1)) 656 printf(" (unsafe)"); 657 # endif DEBUG 658 if (getruid() != geteuid()) 659 { 660 printf("(Resetting uid)\n"); 661 (void) setgid(getgid()); 662 (void) setuid(getuid()); 663 } 664 } 665 #ifdef DEBUG 666 else if (tTd(37, 1)) 667 printf("\n"); 668 #endif DEBUG 669 670 switch (opt) 671 { 672 case 'A': /* set default alias file */ 673 if (val[0] == '\0') 674 AliasFile = "aliases"; 675 else 676 AliasFile = newstr(val); 677 break; 678 679 case 'a': /* look N minutes for "@:@" in alias file */ 680 if (val[0] == '\0') 681 SafeAlias = 5; 682 else 683 SafeAlias = atoi(val); 684 break; 685 686 case 'B': /* substitution for blank character */ 687 SpaceSub = val[0]; 688 if (SpaceSub == '\0') 689 SpaceSub = ' '; 690 break; 691 692 case 'c': /* don't connect to "expensive" mailers */ 693 NoConnect = atobool(val); 694 break; 695 696 case 'C': /* checkpoint after N connections */ 697 CheckPointLimit = atoi(val); 698 break; 699 700 case 'd': /* delivery mode */ 701 switch (*val) 702 { 703 case '\0': 704 SendMode = SM_DELIVER; 705 break; 706 707 case SM_QUEUE: /* queue only */ 708 #ifndef QUEUE 709 syserr("need QUEUE to set -odqueue"); 710 #endif QUEUE 711 /* fall through..... */ 712 713 case SM_DELIVER: /* do everything */ 714 case SM_FORK: /* fork after verification */ 715 SendMode = *val; 716 break; 717 718 default: 719 syserr("Unknown delivery mode %c", *val); 720 exit(EX_USAGE); 721 } 722 break; 723 724 case 'D': /* rebuild alias database as needed */ 725 AutoRebuild = atobool(val); 726 break; 727 728 case 'e': /* set error processing mode */ 729 switch (*val) 730 { 731 case EM_QUIET: /* be silent about it */ 732 case EM_MAIL: /* mail back */ 733 case EM_BERKNET: /* do berknet error processing */ 734 case EM_WRITE: /* write back (or mail) */ 735 HoldErrs = TRUE; 736 /* fall through... */ 737 738 case EM_PRINT: /* print errors normally (default) */ 739 ErrorMode = *val; 740 break; 741 } 742 break; 743 744 case 'F': /* file mode */ 745 FileMode = atooct(val) & 0777; 746 break; 747 748 case 'f': /* save Unix-style From lines on front */ 749 SaveFrom = atobool(val); 750 break; 751 752 case 'g': /* default gid */ 753 DefGid = atoi(val); 754 break; 755 756 case 'H': /* help file */ 757 if (val[0] == '\0') 758 HelpFile = "sendmail.hf"; 759 else 760 HelpFile = newstr(val); 761 break; 762 763 case 'i': /* ignore dot lines in message */ 764 IgnrDot = atobool(val); 765 break; 766 767 case 'L': /* log level */ 768 LogLevel = atoi(val); 769 break; 770 771 case 'M': /* define macro */ 772 define(val[0], newstr(&val[1]), CurEnv); 773 sticky = FALSE; 774 break; 775 776 case 'm': /* send to me too */ 777 MeToo = atobool(val); 778 break; 779 780 # ifdef DAEMON 781 case 'N': /* home (local?) network name */ 782 NetName = newstr(val); 783 break; 784 # endif DAEMON 785 786 case 'o': /* assume old style headers */ 787 if (atobool(val)) 788 CurEnv->e_flags |= EF_OLDSTYLE; 789 else 790 CurEnv->e_flags &= ~EF_OLDSTYLE; 791 break; 792 793 case 'P': /* postmaster copy address for returned mail */ 794 PostMasterCopy = newstr(val); 795 break; 796 797 case 'q': /* slope of queue only function */ 798 QueueFactor = atoi(val); 799 break; 800 801 case 'Q': /* queue directory */ 802 if (val[0] == '\0') 803 QueueDir = "mqueue"; 804 else 805 QueueDir = newstr(val); 806 break; 807 808 case 'r': /* read timeout */ 809 ReadTimeout = convtime(val); 810 break; 811 812 case 'S': /* status file */ 813 if (val[0] == '\0') 814 StatFile = "sendmail.st"; 815 else 816 StatFile = newstr(val); 817 break; 818 819 case 's': /* be super safe, even if expensive */ 820 SuperSafe = atobool(val); 821 break; 822 823 case 'T': /* queue timeout */ 824 TimeOut = convtime(val); 825 break; 826 827 case 't': /* time zone name */ 828 # ifdef V6 829 StdTimezone = newstr(val); 830 DstTimezone = index(StdTimeZone, ','); 831 if (DstTimezone == NULL) 832 syserr("bad time zone spec"); 833 else 834 *DstTimezone++ = '\0'; 835 # endif V6 836 break; 837 838 case 'u': /* set default uid */ 839 DefUid = atoi(val); 840 break; 841 842 case 'v': /* run in verbose mode */ 843 Verbose = atobool(val); 844 break; 845 846 # ifdef DEBUG 847 case 'W': /* set the wizards password */ 848 WizWord = newstr(val); 849 break; 850 # endif DEBUG 851 852 case 'x': /* load avg at which to auto-queue msgs */ 853 QueueLA = atoi(val); 854 break; 855 856 case 'X': /* load avg at which to auto-reject connections */ 857 RefuseLA = atoi(val); 858 break; 859 860 default: 861 break; 862 } 863 if (sticky) 864 setbitn(opt, StickyOpt); 865 return; 866 } 867 /* 868 ** SETCLASS -- set a word into a class 869 ** 870 ** Parameters: 871 ** class -- the class to put the word in. 872 ** word -- the word to enter 873 ** 874 ** Returns: 875 ** none. 876 ** 877 ** Side Effects: 878 ** puts the word into the symbol table. 879 */ 880 881 setclass(class, word) 882 int class; 883 char *word; 884 { 885 register STAB *s; 886 887 s = stab(word, ST_CLASS, ST_ENTER); 888 setbitn(class, s->s_class); 889 } 890