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