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