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