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