1 # include "sendmail.h" 2 3 SCCSID(@(#)readcf.c 3.47 11/24/82); 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 class; 54 int ruleset = 0; 55 char *q; 56 char **pv; 57 struct rewrite *rwp = NULL; 58 char buf[MAXLINE]; 59 register char *p; 60 extern char **prescan(); 61 extern char **copyplist(); 62 char exbuf[MAXLINE]; 63 extern char *fgetfolded(); 64 65 cf = fopen(cfname, "r"); 66 if (cf == NULL) 67 { 68 syserr("cannot open %s", cfname); 69 exit(EX_OSFILE); 70 } 71 72 LineNumber = 0; 73 while (fgetfolded(buf, sizeof buf, cf) != NULL) 74 { 75 switch (buf[0]) 76 { 77 case '\0': 78 case '#': /* comment */ 79 break; 80 81 case 'R': /* rewriting rule */ 82 for (p = &buf[1]; *p != '\0' && *p != '\t'; p++) 83 continue; 84 85 if (*p == '\0') 86 { 87 syserr("line %d: invalid rewrite line \"%s\"", 88 LineNumber, 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("readcf: line %d: bad ruleset %d (%d max)", 130 LineNumber, ruleset, MAXRWSETS); 131 ruleset = 0; 132 } 133 rwp = NULL; 134 break; 135 136 case 'D': /* macro definition */ 137 define(buf[1], newstr(&buf[2])); 138 break; 139 140 case 'H': /* required header line */ 141 (void) chompheader(&buf[1], TRUE); 142 break; 143 144 case 'C': /* word class */ 145 case 'F': /* word class from file */ 146 class = buf[1]; 147 if (!isalpha(class)) 148 goto badline; 149 if (isupper(class)) 150 class -= 'A'; 151 else 152 class -= 'a'; 153 154 /* read list of words from argument or file */ 155 if (buf[0] == 'F') 156 { 157 /* read from file */ 158 for (p = &buf[2]; *p != '\0' && !isspace(*p); p++) 159 continue; 160 if (*p == '\0') 161 p = "%s"; 162 else 163 { 164 *p = '\0'; 165 while (isspace(*++p)) 166 continue; 167 } 168 fileclass(class, &buf[2], p); 169 break; 170 } 171 172 /* scan the list of words and set class for all */ 173 for (p = &buf[2]; *p != '\0'; ) 174 { 175 register char *wd; 176 char delim; 177 register STAB *s; 178 179 while (*p != '\0' && isspace(*p)) 180 p++; 181 wd = p; 182 while (*p != '\0' && !isspace(*p)) 183 p++; 184 delim = *p; 185 *p = '\0'; 186 if (wd[0] != '\0') 187 { 188 s = stab(wd, ST_CLASS, ST_ENTER); 189 s->s_class |= 1L << class; 190 } 191 *p = delim; 192 } 193 break; 194 195 case 'M': /* define mailer */ 196 makemailer(&buf[1], safe); 197 break; 198 199 case 'O': /* set option */ 200 setoption(buf[1], &buf[2], safe, FALSE); 201 break; 202 203 case 'P': /* set precedence */ 204 if (NumPriorities >= MAXPRIORITIES) 205 { 206 toomany('P', MAXPRIORITIES); 207 break; 208 } 209 for (p = &buf[1]; *p != '\0' && *p != '='; p++) 210 continue; 211 if (*p == '\0') 212 goto badline; 213 *p = '\0'; 214 Priorities[NumPriorities].pri_name = newstr(&buf[1]); 215 Priorities[NumPriorities].pri_val = atoi(++p); 216 NumPriorities++; 217 break; 218 219 case 'T': /* trusted user(s) */ 220 p = &buf[1]; 221 while (*p != '\0') 222 { 223 while (isspace(*p)) 224 p++; 225 q = p; 226 while (*p != '\0' && !isspace(*p)) 227 p++; 228 if (*p != '\0') 229 *p++ = '\0'; 230 if (*q == '\0') 231 continue; 232 for (pv = TrustedUsers; *pv != NULL; pv++) 233 continue; 234 if (pv >= &TrustedUsers[MAXTRUST]) 235 { 236 toomany('T', MAXTRUST); 237 break; 238 } 239 *pv = newstr(q); 240 } 241 break; 242 243 default: 244 badline: 245 syserr("readcf: line %d: unknown control line \"%s\"", 246 LineNumber, buf); 247 } 248 } 249 } 250 /* 251 ** TOOMANY -- signal too many of some option 252 ** 253 ** Parameters: 254 ** id -- the id of the error line 255 ** maxcnt -- the maximum possible values 256 ** 257 ** Returns: 258 ** none. 259 ** 260 ** Side Effects: 261 ** gives a syserr. 262 */ 263 264 toomany(id, maxcnt) 265 char id; 266 int maxcnt; 267 { 268 syserr("readcf: line %d: too many %c lines, %d max", 269 LineNumber, 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 s->s_class |= 1L << 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 tokens 321 ** separated by white space. The fields are: 322 ** * the name of the mailer, as refered to 323 ** in the rewriting rules. 324 ** * the pathname of the program to fork to 325 ** execute it. 326 ** * the options needed by this program. 327 ** * the macro string needed to translate 328 ** a local "from" name to one that can be 329 ** returned to this machine. 330 ** * the argument vector (a series of parameters). 331 ** safe -- set if this is a safe configuration file. 332 ** 333 ** Returns: 334 ** none. 335 ** 336 ** Side Effects: 337 ** enters the mailer into the mailer table. 338 */ 339 340 # define SETWORD \ 341 { \ 342 while (*p != '\0' && isspace(*p)) \ 343 p++; \ 344 q = p; \ 345 while (*p != '\0' && !isspace(*p)) \ 346 p++; \ 347 if (*p != '\0') \ 348 *p++ = '\0'; \ 349 } 350 351 makemailer(line, safe) 352 char *line; 353 bool safe; 354 { 355 register char *p; 356 register char *q; 357 register struct mailer *m; 358 register STAB *s; 359 int i; 360 char *mname; 361 char *mpath; 362 u_long mopts; 363 short mrset, msset; 364 char *margv[MAXPV + 1]; 365 extern u_long mfencode(); 366 extern int NextMailer; 367 368 if (NextMailer >= MAXMAILERS) 369 { 370 syserr("readcf: line %d: too many mailers defined (%d max)", 371 LineNumber, MAXMAILERS); 372 return; 373 } 374 375 /* collect initial information */ 376 p = line; 377 SETWORD; 378 mname = q; 379 SETWORD; 380 mpath = q; 381 SETWORD; 382 mopts = mfencode(q); 383 if (!safe) 384 mopts &= ~M_RESTR; 385 SETWORD; 386 msset = atoi(q); 387 SETWORD; 388 mrset = atoi(q); 389 390 if (*p == '\0') 391 { 392 syserr("readcf: line %d: invalid M line in configuration file", 393 LineNumber); 394 return; 395 } 396 if (msset >= MAXRWSETS || mrset >= MAXRWSETS) 397 { 398 syserr("readcf: line %d: invalid rewrite set, %d max", 399 LineNumber, MAXRWSETS); 400 return; 401 } 402 403 /* allocate a mailer */ 404 m = (struct mailer *) xalloc(sizeof *m); 405 m->m_name = newstr(mname); 406 m->m_mailer = newstr(mpath); 407 m->m_flags = mopts; 408 m->m_r_rwset = mrset; 409 m->m_s_rwset = msset; 410 m->m_badstat = EX_UNAVAILABLE; 411 m->m_mno = NextMailer; 412 Mailer[NextMailer++] = m; 413 414 /* collect the argument vector */ 415 for (i = 0; i < MAXPV - 1 && *p != '\0'; i++) 416 { 417 SETWORD; 418 margv[i] = newstr(q); 419 } 420 margv[i++] = NULL; 421 422 /* save the argv */ 423 m->m_argv = (char **) xalloc(sizeof margv[0] * i); 424 bmove((char *) margv, (char *) m->m_argv, sizeof margv[0] * i); 425 s = stab(m->m_name, ST_MAILER, ST_ENTER); 426 s->s_mailer = m; 427 } 428 /* 429 ** PRINTRULES -- print rewrite rules (for debugging) 430 ** 431 ** Parameters: 432 ** none. 433 ** 434 ** Returns: 435 ** none. 436 ** 437 ** Side Effects: 438 ** prints rewrite rules. 439 */ 440 441 # ifdef DEBUG 442 443 printrules() 444 { 445 register struct rewrite *rwp; 446 register int ruleset; 447 448 for (ruleset = 0; ruleset < 10; ruleset++) 449 { 450 if (RewriteRules[ruleset] == NULL) 451 continue; 452 printf("\n----Rule Set %d:", ruleset); 453 454 for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 455 { 456 printf("\nLHS:"); 457 printav(rwp->r_lhs); 458 printf("RHS:"); 459 printav(rwp->r_rhs); 460 } 461 } 462 } 463 464 # endif DEBUG 465 /* 466 ** MFENCODE -- crack mailer options 467 ** 468 ** These options modify the functioning of the mailer 469 ** from the configuration table. 470 ** 471 ** Parameters: 472 ** p -- pointer to vector of options. 473 ** 474 ** Returns: 475 ** option list in binary. 476 ** 477 ** Side Effects: 478 ** none. 479 */ 480 481 struct optlist 482 { 483 char opt_name; /* external name of option */ 484 u_long opt_value; /* internal name of option */ 485 }; 486 struct optlist OptList[] = 487 { 488 'f', M_FOPT, 489 'r', M_ROPT, 490 'q', M_QUIET, 491 'S', M_RESTR, 492 'n', M_NHDR, 493 'l', M_LOCAL, 494 's', M_STRIPQ, 495 'm', M_MUSER, 496 'F', M_NEEDFROM, 497 'D', M_NEEDDATE, 498 'M', M_MSGID, 499 'u', M_USR_UPPER, 500 'h', M_HST_UPPER, 501 'x', M_FULLNAME, 502 'A', M_ARPAFMT, 503 'U', M_UGLYUUCP, 504 'e', M_EXPENSIVE, 505 'X', M_FULLSMTP, 506 'C', M_CANONICAL, 507 'I', M_INTERNAL, 508 '\0', 0 509 }; 510 511 u_long 512 mfencode(p) 513 register char *p; 514 { 515 register struct optlist *o; 516 register u_long opts = 0; 517 518 while (*p != '\0') 519 { 520 for (o = OptList; o->opt_name != '\0' && o->opt_name != *p; o++) 521 continue; 522 if (o->opt_name == '\0') 523 syserr("bad mailer option %c", *p); 524 opts |= o->opt_value; 525 p++; 526 } 527 return (opts); 528 } 529 /* 530 ** MFDECODE -- decode mailer flags into external form. 531 ** 532 ** Parameters: 533 ** flags -- value of flags to decode. 534 ** f -- file to write them onto. 535 ** 536 ** Returns: 537 ** none. 538 ** 539 ** Side Effects: 540 ** none. 541 */ 542 543 mfdecode(flags, f) 544 u_long flags; 545 FILE *f; 546 { 547 register struct optlist *o; 548 549 putc('?', f); 550 for (o = OptList; o->opt_name != '\0'; o++) 551 { 552 if ((o->opt_value & flags) == o->opt_value) 553 { 554 flags &= ~o->opt_value; 555 putc(o->opt_name, f); 556 } 557 } 558 putc('?', f); 559 } 560 /* 561 ** SETOPTION -- set global processing option 562 ** 563 ** Parameters: 564 ** opt -- option name. 565 ** val -- option value (as a text string). 566 ** safe -- if set, this came from a system configuration file. 567 ** sticky -- if set, don't let other setoptions override 568 ** this value. 569 ** 570 ** Returns: 571 ** none. 572 ** 573 ** Side Effects: 574 ** Sets options as implied by the arguments. 575 */ 576 577 static int StickyOpt[128 / sizeof (int)]; /* set if option is stuck */ 578 extern char *WizWord; /* the stored wizard password */ 579 580 setoption(opt, val, safe, sticky) 581 char opt; 582 char *val; 583 bool safe; 584 bool sticky; 585 { 586 time_t tval; 587 int ival; 588 bool bval; 589 int smask; 590 int sindex; 591 extern bool atobool(); 592 593 # ifdef DEBUG 594 if (tTd(37, 1)) 595 printf("setoption %c=%s", opt, val); 596 # endif DEBUG 597 598 /* 599 ** See if this option is preset for us. 600 */ 601 602 sindex = opt; 603 smask = 1 << (sindex % sizeof (int)); 604 sindex /= sizeof (int); 605 if (bitset(smask, StickyOpt[sindex])) 606 { 607 # ifdef DEBUG 608 if (tTd(37, 1)) 609 printf(" (ignored)\n"); 610 # endif DEBUG 611 return; 612 } 613 #ifdef DEBUG 614 else if (tTd(37, 1)) 615 printf("\n"); 616 #endif DEBUG 617 if (sticky) 618 StickyOpt[sindex] |= smask; 619 620 if (getruid() == 0) 621 safe = TRUE; 622 623 /* 624 ** Encode this option as appropriate. 625 */ 626 627 if (index("rT", opt) != NULL) 628 tval = convtime(val); 629 else if (index("gLu", opt) != NULL) 630 ival = atoi(val); 631 else if (index("F", opt) != NULL) 632 ival = atooct(val); 633 else if (index("acDfimosv", opt) != NULL) 634 bval = atobool(val); 635 else if (index("be", opt) != NULL) 636 /* do nothing */ ; 637 else if (val[0] == '\0') 638 val = ""; 639 else 640 val = newstr(val); 641 642 /* 643 ** Now do the actual assignment. 644 */ 645 646 switch (opt) 647 { 648 case 'A': /* set default alias file */ 649 AliasFile = val; 650 if (AliasFile[0] == '\0') 651 AliasFile = "aliases"; 652 break; 653 654 case 'a': /* look for "@:@" in alias file */ 655 SafeAlias = bval; 656 break; 657 658 case 'c': /* don't connect to "expensive" mailers */ 659 NoConnect = bval; 660 break; 661 662 case 'd': /* delivery mode */ 663 switch (*val) 664 { 665 case '\0': 666 SendMode = SM_DELIVER; 667 break; 668 669 case SM_DELIVER: /* do everything */ 670 case SM_FORK: /* fork after verification */ 671 case SM_QUEUE: /* queue only */ 672 SendMode = *val; 673 break; 674 675 default: 676 syserr("Unknown delivery mode %c", *val); 677 exit(EX_USAGE); 678 } 679 break; 680 681 case 'D': /* rebuild alias database as needed */ 682 AutoRebuild = bval; 683 break; 684 685 case 'e': /* set error processing mode */ 686 switch (*val) 687 { 688 case 'p': /* print errors normally */ 689 break; /* (default) */ 690 691 case 'q': /* be silent about it */ 692 (void) freopen("/dev/null", "w", stdout); 693 break; 694 695 case 'm': /* mail back */ 696 MailBack = TRUE; 697 HoldErrs = TRUE; 698 break; 699 700 case 'e': /* do berknet error processing */ 701 BerkNet = TRUE; 702 HoldErrs = TRUE; 703 break; 704 705 case 'w': /* write back (or mail) */ 706 WriteBack = TRUE; 707 HoldErrs = TRUE; 708 break; 709 } 710 break; 711 712 case 'F': /* file mode */ 713 FileMode = ival; 714 break; 715 716 case 'f': /* save Unix-style From lines on front */ 717 SaveFrom = bval; 718 break; 719 720 case 'g': /* default gid */ 721 if (safe) 722 DefGid = ival; 723 break; 724 725 case 'H': /* help file */ 726 HelpFile = val; 727 if (HelpFile[0] == '\0') 728 HelpFile = "sendmail.hf"; 729 break; 730 731 case 'i': /* ignore dot lines in message */ 732 IgnrDot = bval; 733 break; 734 735 case 'L': /* log level */ 736 LogLevel = ival; 737 break; 738 739 case 'M': /* define macro */ 740 define(val[0], &val[1]); 741 break; 742 743 case 'm': /* send to me too */ 744 MeToo = bval; 745 break; 746 747 case 'o': /* assume old style headers */ 748 if (bval) 749 CurEnv->e_flags |= EF_OLDSTYLE; 750 else 751 CurEnv->e_flags &= ~EF_OLDSTYLE; 752 break; 753 754 case 'Q': /* queue directory */ 755 QueueDir = val; 756 if (QueueDir[0] == '\0') 757 QueueDir = "mqueue"; 758 break; 759 760 case 'r': /* read timeout */ 761 ReadTimeout = tval; 762 break; 763 764 case 'S': /* status file */ 765 StatFile = val; 766 if (StatFile[0] == '\0') 767 StatFile = "sendmail.st"; 768 break; 769 770 case 's': /* be super safe, even if expensive */ 771 SuperSafe = bval; 772 break; 773 774 case 'T': /* queue timeout */ 775 TimeOut = tval; 776 break; 777 778 case 't': /* time zone name */ 779 # ifdef V6 780 StdTimezone = val; 781 DstTimezone = index(val, ','); 782 if (DstTimezone == NULL) 783 goto syntax; 784 *DstTimezone++ = '\0'; 785 # endif V6 786 break; 787 788 case 'u': /* set default uid */ 789 if (safe) 790 DefUid = ival; 791 break; 792 793 case 'v': /* run in verbose mode */ 794 Verbose = bval; 795 break; 796 797 # ifdef DEBUG 798 case 'W': /* set the wizards password */ 799 if (safe) 800 WizWord = val; 801 break; 802 # endif DEBUG 803 804 default: 805 break; 806 } 807 return; 808 809 syntax: 810 syserr("setoption: line %d: syntax error on \"%c%s\"", 811 LineNumber, opt, val); 812 } 813