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