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