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