1: # include "sendmail.h" 2: 3: SCCSID(@(#)readcf.c 4.2 8/28/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 ruleset = 0; 54: char *q; 55: char **pv; 56: struct rewrite *rwp = NULL; 57: char buf[MAXLINE]; 58: register char *p; 59: extern char **prescan(); 60: extern char **copyplist(); 61: char exbuf[MAXLINE]; 62: extern char *fgetfolded(); 63: extern char *munchstring(); 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(munchstring(&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: /* read list of words from argument or file */ 146: if (buf[0] == 'F') 147: { 148: /* read from file */ 149: for (p = &buf[2]; *p != '\0' && !isspace(*p); p++) 150: continue; 151: if (*p == '\0') 152: p = "%s"; 153: else 154: { 155: *p = '\0'; 156: while (isspace(*++p)) 157: continue; 158: } 159: fileclass(buf[1], &buf[2], p); 160: break; 161: } 162: 163: /* scan the list of words and set class for all */ 164: for (p = &buf[2]; *p != '\0'; ) 165: { 166: register char *wd; 167: char delim; 168: 169: while (*p != '\0' && isspace(*p)) 170: p++; 171: wd = p; 172: while (*p != '\0' && !isspace(*p)) 173: p++; 174: delim = *p; 175: *p = '\0'; 176: if (wd[0] != '\0') 177: setclass(buf[1], wd); 178: *p = delim; 179: } 180: break; 181: 182: case 'M': /* define mailer */ 183: makemailer(&buf[1], safe); 184: break; 185: 186: case 'O': /* set option */ 187: setoption(buf[1], &buf[2], safe, FALSE); 188: break; 189: 190: case 'P': /* set precedence */ 191: if (NumPriorities >= MAXPRIORITIES) 192: { 193: toomany('P', MAXPRIORITIES); 194: break; 195: } 196: for (p = &buf[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 197: continue; 198: if (*p == '\0') 199: goto badline; 200: *p = '\0'; 201: Priorities[NumPriorities].pri_name = newstr(&buf[1]); 202: Priorities[NumPriorities].pri_val = atoi(++p); 203: NumPriorities++; 204: break; 205: 206: case 'T': /* trusted user(s) */ 207: p = &buf[1]; 208: while (*p != '\0') 209: { 210: while (isspace(*p)) 211: p++; 212: q = p; 213: while (*p != '\0' && !isspace(*p)) 214: p++; 215: if (*p != '\0') 216: *p++ = '\0'; 217: if (*q == '\0') 218: continue; 219: for (pv = TrustedUsers; *pv != NULL; pv++) 220: continue; 221: if (pv >= &TrustedUsers[MAXTRUST]) 222: { 223: toomany('T', MAXTRUST); 224: break; 225: } 226: *pv = newstr(q); 227: } 228: break; 229: 230: default: 231: badline: 232: syserr("unknown control line \"%s\"", buf); 233: } 234: } 235: FileName = NULL; 236: } 237: /* 238: ** TOOMANY -- signal too many of some option 239: ** 240: ** Parameters: 241: ** id -- the id of the error line 242: ** maxcnt -- the maximum possible values 243: ** 244: ** Returns: 245: ** none. 246: ** 247: ** Side Effects: 248: ** gives a syserr. 249: */ 250: 251: toomany(id, maxcnt) 252: char id; 253: int maxcnt; 254: { 255: syserr("too many %c lines, %d max", id, maxcnt); 256: } 257: /* 258: ** FILECLASS -- read members of a class from a file 259: ** 260: ** Parameters: 261: ** class -- class to define. 262: ** filename -- name of file to read. 263: ** fmt -- scanf string to use for match. 264: ** 265: ** Returns: 266: ** none 267: ** 268: ** Side Effects: 269: ** 270: ** puts all lines in filename that match a scanf into 271: ** the named class. 272: */ 273: 274: fileclass(class, filename, fmt) 275: int class; 276: char *filename; 277: char *fmt; 278: { 279: register FILE *f; 280: char buf[MAXLINE]; 281: 282: f = fopen(filename, "r"); 283: if (f == NULL) 284: { 285: syserr("cannot open %s", filename); 286: return; 287: } 288: 289: while (fgets(buf, sizeof buf, f) != NULL) 290: { 291: register STAB *s; 292: char wordbuf[MAXNAME+1]; 293: 294: if (sscanf(buf, fmt, wordbuf) != 1) 295: continue; 296: s = stab(wordbuf, ST_CLASS, ST_ENTER); 297: setbitn(class, s->s_class); 298: } 299: 300: (void) fclose(f); 301: } 302: /* 303: ** MAKEMAILER -- define a new mailer. 304: ** 305: ** Parameters: 306: ** line -- description of mailer. This is in labeled 307: ** fields. The fields are: 308: ** P -- the path to the mailer 309: ** F -- the flags associated with the mailer 310: ** A -- the argv for this mailer 311: ** S -- the sender rewriting set 312: ** R -- the recipient rewriting set 313: ** E -- the eol string 314: ** The first word is the canonical name of the mailer. 315: ** safe -- set if this is a safe configuration file. 316: ** 317: ** Returns: 318: ** none. 319: ** 320: ** Side Effects: 321: ** enters the mailer into the mailer table. 322: */ 323: 324: makemailer(line, safe) 325: char *line; 326: bool safe; 327: { 328: register char *p; 329: register struct mailer *m; 330: register STAB *s; 331: int i; 332: char fcode; 333: extern int NextMailer; 334: extern char **makeargv(); 335: extern char *munchstring(); 336: extern char *DelimChar; 337: extern long atol(); 338: 339: /* allocate a mailer and set up defaults */ 340: m = (struct mailer *) xalloc(sizeof *m); 341: bzero((char *) m, sizeof *m); 342: m->m_mno = NextMailer; 343: m->m_eol = "\n"; 344: 345: /* collect the mailer name */ 346: for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++) 347: continue; 348: if (*p != '\0') 349: *p++ = '\0'; 350: m->m_name = newstr(line); 351: 352: /* now scan through and assign info from the fields */ 353: while (*p != '\0') 354: { 355: while (*p != '\0' && (*p == ',' || isspace(*p))) 356: p++; 357: 358: /* p now points to field code */ 359: fcode = *p; 360: while (*p != '\0' && *p != '=' && *p != ',') 361: p++; 362: if (*p++ != '=') 363: { 364: syserr("`=' expected"); 365: return; 366: } 367: while (isspace(*p)) 368: p++; 369: 370: /* p now points to the field body */ 371: p = munchstring(p); 372: 373: /* install the field into the mailer struct */ 374: switch (fcode) 375: { 376: case 'P': /* pathname */ 377: m->m_mailer = newstr(p); 378: break; 379: 380: case 'F': /* flags */ 381: for (; *p != '\0'; p++) 382: setbitn(*p, m->m_flags); 383: if (!safe) 384: clrbitn(M_RESTR, m->m_flags); 385: break; 386: 387: case 'S': /* sender rewriting ruleset */ 388: case 'R': /* recipient rewriting ruleset */ 389: i = atoi(p); 390: if (i < 0 || i >= MAXRWSETS) 391: { 392: syserr("invalid rewrite set, %d max", MAXRWSETS); 393: return; 394: } 395: if (fcode == 'S') 396: m->m_s_rwset = i; 397: else 398: m->m_r_rwset = i; 399: break; 400: 401: case 'E': /* end of line string */ 402: m->m_eol = newstr(p); 403: break; 404: 405: case 'A': /* argument vector */ 406: m->m_argv = makeargv(p); 407: break; 408: 409: case 'M': /* maximum message size */ 410: m->m_maxsize = atol(p); 411: break; 412: } 413: 414: p = DelimChar; 415: } 416: 417: /* now store the mailer away */ 418: if (NextMailer >= MAXMAILERS) 419: { 420: syserr("too many mailers defined (%d max)", MAXMAILERS); 421: return; 422: } 423: Mailer[NextMailer++] = m; 424: s = stab(m->m_name, ST_MAILER, ST_ENTER); 425: s->s_mailer = m; 426: } 427: /* 428: ** MUNCHSTRING -- translate a string into internal form. 429: ** 430: ** Parameters: 431: ** p -- the string to munch. 432: ** 433: ** Returns: 434: ** the munched string. 435: ** 436: ** Side Effects: 437: ** Sets "DelimChar" to point to the string that caused us 438: ** to stop. 439: */ 440: 441: char * 442: munchstring(p) 443: register char *p; 444: { 445: register char *q; 446: bool backslash = FALSE; 447: bool quotemode = FALSE; 448: static char buf[MAXLINE]; 449: extern char *DelimChar; 450: 451: for (q = buf; *p != '\0'; p++) 452: { 453: if (backslash) 454: { 455: /* everything is roughly literal */ 456: backslash = FALSE; 457: switch (*p) 458: { 459: case 'r': /* carriage return */ 460: *q++ = '\r'; 461: continue; 462: 463: case 'n': /* newline */ 464: *q++ = '\n'; 465: continue; 466: 467: case 'f': /* form feed */ 468: *q++ = '\f'; 469: continue; 470: 471: case 'b': /* backspace */ 472: *q++ = '\b'; 473: continue; 474: } 475: *q++ = *p; 476: } 477: else 478: { 479: if (*p == '\\') 480: backslash = TRUE; 481: else if (*p == '"') 482: quotemode = !quotemode; 483: else if (quotemode || *p != ',') 484: *q++ = *p; 485: else 486: break; 487: } 488: } 489: 490: DelimChar = p; 491: *q++ = '\0'; 492: return (buf); 493: } 494: /* 495: ** MAKEARGV -- break up a string into words 496: ** 497: ** Parameters: 498: ** p -- the string to break up. 499: ** 500: ** Returns: 501: ** a char **argv (dynamically allocated) 502: ** 503: ** Side Effects: 504: ** munges p. 505: */ 506: 507: char ** 508: makeargv(p) 509: register char *p; 510: { 511: char *q; 512: int i; 513: char **avp; 514: char *argv[MAXPV + 1]; 515: 516: /* take apart the words */ 517: i = 0; 518: while (*p != '\0' && i < MAXPV) 519: { 520: q = p; 521: while (*p != '\0' && !isspace(*p)) 522: p++; 523: while (isspace(*p)) 524: *p++ = '\0'; 525: argv[i++] = newstr(q); 526: } 527: argv[i++] = NULL; 528: 529: /* now make a copy of the argv */ 530: avp = (char **) xalloc(sizeof *avp * i); 531: bmove((char *) argv, (char *) avp, sizeof *avp * i); 532: 533: return (avp); 534: } 535: /* 536: ** PRINTRULES -- print rewrite rules (for debugging) 537: ** 538: ** Parameters: 539: ** none. 540: ** 541: ** Returns: 542: ** none. 543: ** 544: ** Side Effects: 545: ** prints rewrite rules. 546: */ 547: 548: # ifdef DEBUG 549: 550: printrules() 551: { 552: register struct rewrite *rwp; 553: register int ruleset; 554: 555: for (ruleset = 0; ruleset < 10; ruleset++) 556: { 557: if (RewriteRules[ruleset] == NULL) 558: continue; 559: printf("\n----Rule Set %d:", ruleset); 560: 561: for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 562: { 563: printf("\nLHS:"); 564: printav(rwp->r_lhs); 565: printf("RHS:"); 566: printav(rwp->r_rhs); 567: } 568: } 569: } 570: 571: # endif DEBUG 572: /* 573: ** SETOPTION -- set global processing option 574: ** 575: ** Parameters: 576: ** opt -- option name. 577: ** val -- option value (as a text string). 578: ** safe -- if set, this came from a system configuration file. 579: ** sticky -- if set, don't let other setoptions override 580: ** this value. 581: ** 582: ** Returns: 583: ** none. 584: ** 585: ** Side Effects: 586: ** Sets options as implied by the arguments. 587: */ 588: 589: static BITMAP StickyOpt; /* set if option is stuck */ 590: extern char *WizWord; /* the stored wizard password */ 591: 592: setoption(opt, val, safe, sticky) 593: char opt; 594: char *val; 595: bool safe; 596: bool sticky; 597: { 598: extern bool atobool(); 599: extern time_t convtime(); 600: extern int QueueLA; 601: extern int RefuseLA; 602: 603: # ifdef DEBUG 604: if (tTd(37, 1)) 605: printf("setoption %c=%s", opt, val); 606: # endif DEBUG 607: 608: /* 609: ** See if this option is preset for us. 610: */ 611: 612: if (bitnset(opt, StickyOpt)) 613: { 614: # ifdef DEBUG 615: if (tTd(37, 1)) 616: printf(" (ignored)\n"); 617: # endif DEBUG 618: return; 619: } 620: #ifdef DEBUG 621: else if (tTd(37, 1)) 622: printf("\n"); 623: #endif DEBUG 624: if (sticky) 625: setbitn(opt, StickyOpt); 626: 627: if (getruid() == 0) 628: safe = TRUE; 629: 630: switch (opt) 631: { 632: case 'A': /* set default alias file */ 633: if (val[0] == '\0') 634: AliasFile = "aliases"; 635: else 636: AliasFile = newstr(val); 637: break; 638: 639: case 'a': /* look for "@:@" in alias file */ 640: SafeAlias = atobool(val); 641: break; 642: 643: case 'c': /* don't connect to "expensive" mailers */ 644: NoConnect = atobool(val); 645: break; 646: 647: case 'd': /* delivery mode */ 648: switch (*val) 649: { 650: case '\0': 651: SendMode = SM_DELIVER; 652: break; 653: 654: case SM_QUEUE: /* queue only */ 655: #ifndef QUEUE 656: syserr("need QUEUE to set -odqueue"); 657: #endif QUEUE 658: /* fall through..... */ 659: 660: case SM_DELIVER: /* do everything */ 661: case SM_FORK: /* fork after verification */ 662: SendMode = *val; 663: break; 664: 665: default: 666: syserr("Unknown delivery mode %c", *val); 667: exit(EX_USAGE); 668: } 669: break; 670: 671: case 'D': /* rebuild alias database as needed */ 672: AutoRebuild = atobool(val); 673: break; 674: 675: case 'e': /* set error processing mode */ 676: switch (*val) 677: { 678: case EM_QUIET: /* be silent about it */ 679: case EM_MAIL: /* mail back */ 680: case EM_BERKNET: /* do berknet error processing */ 681: case EM_WRITE: /* write back (or mail) */ 682: HoldErrs = TRUE; 683: /* fall through... */ 684: 685: case EM_PRINT: /* print errors normally (default) */ 686: ErrorMode = *val; 687: break; 688: } 689: break; 690: 691: case 'F': /* file mode */ 692: FileMode = atooct(val); 693: break; 694: 695: case 'f': /* save Unix-style From lines on front */ 696: SaveFrom = atobool(val); 697: break; 698: 699: case 'g': /* default gid */ 700: if (safe) 701: DefGid = atoi(val); 702: break; 703: 704: case 'H': /* help file */ 705: if (val[0] == '\0') 706: HelpFile = "sendmail.hf"; 707: else 708: HelpFile = newstr(val); 709: break; 710: 711: case 'i': /* ignore dot lines in message */ 712: IgnrDot = atobool(val); 713: break; 714: 715: case 'L': /* log level */ 716: LogLevel = atoi(val); 717: break; 718: 719: case 'M': /* define macro */ 720: define(val[0], newstr(&val[1]), CurEnv); 721: break; 722: 723: case 'm': /* send to me too */ 724: MeToo = atobool(val); 725: break; 726: 727: case 'o': /* assume old style headers */ 728: if (atobool(val)) 729: CurEnv->e_flags |= EF_OLDSTYLE; 730: else 731: CurEnv->e_flags &= ~EF_OLDSTYLE; 732: break; 733: 734: case 'Q': /* queue directory */ 735: if (val[0] == '\0') 736: QueueDir = "mqueue"; 737: else 738: QueueDir = newstr(val); 739: break; 740: 741: case 'r': /* read timeout */ 742: ReadTimeout = convtime(val); 743: break; 744: 745: case 'S': /* status file */ 746: if (val[0] == '\0') 747: StatFile = "sendmail.st"; 748: else 749: StatFile = newstr(val); 750: break; 751: 752: case 's': /* be super safe, even if expensive */ 753: SuperSafe = atobool(val); 754: break; 755: 756: case 'T': /* queue timeout */ 757: TimeOut = convtime(val); 758: break; 759: 760: case 't': /* time zone name */ 761: # ifdef V6 762: StdTimezone = newstr(val); 763: DstTimezone = index(StdTimeZone, ','); 764: if (DstTimezone == NULL) 765: syserr("bad time zone spec"); 766: else 767: *DstTimezone++ = '\0'; 768: # endif V6 769: break; 770: 771: case 'u': /* set default uid */ 772: if (safe) 773: DefUid = atoi(val); 774: break; 775: 776: case 'v': /* run in verbose mode */ 777: Verbose = atobool(val); 778: break; 779: 780: # ifdef DEBUG 781: case 'W': /* set the wizards password */ 782: if (safe) 783: WizWord = newstr(val); 784: break; 785: # endif DEBUG 786: 787: case 'x': /* load avg at which to auto-queue msgs */ 788: QueueLA = atoi(val); 789: break; 790: 791: case 'X': /* load avg at which to auto-reject connections */ 792: RefuseLA = atoi(val); 793: break; 794: 795: default: 796: break; 797: } 798: return; 799: } 800: /* 801: ** SETCLASS -- set a word into a class 802: ** 803: ** Parameters: 804: ** class -- the class to put the word in. 805: ** word -- the word to enter 806: ** 807: ** Returns: 808: ** none. 809: ** 810: ** Side Effects: 811: ** puts the word into the symbol table. 812: */ 813: 814: setclass(class, word) 815: int class; 816: char *word; 817: { 818: register STAB *s; 819: 820: s = stab(word, ST_CLASS, ST_ENTER); 821: setbitn(class, s->s_class); 822: }