122709Sdist /* 234921Sbostic * Copyright (c) 1983 Eric P. Allman 333731Sbostic * Copyright (c) 1988 Regents of the University of California. 433731Sbostic * All rights reserved. 533731Sbostic * 642829Sbostic * %sccs.include.redist.c% 733731Sbostic */ 822709Sdist 922709Sdist #ifndef lint 10*57589Seric static char sccsid[] = "@(#)readcf.c 6.4 (Berkeley) 01/18/93"; 1133731Sbostic #endif /* not lint */ 1222709Sdist 133313Seric # include "sendmail.h" 1452647Seric # include <sys/stat.h> 1554973Seric # include <unistd.h> 1657207Seric #ifdef NAMED_BIND 1757207Seric # include <arpa/nameser.h> 1857207Seric # include <resolv.h> 1957207Seric #endif 203308Seric 213308Seric /* 223308Seric ** READCF -- read control file. 233308Seric ** 243308Seric ** This routine reads the control file and builds the internal 253308Seric ** form. 263308Seric ** 274432Seric ** The file is formatted as a sequence of lines, each taken 284432Seric ** atomically. The first character of each line describes how 294432Seric ** the line is to be interpreted. The lines are: 304432Seric ** Dxval Define macro x to have value val. 314432Seric ** Cxword Put word into class x. 324432Seric ** Fxfile [fmt] Read file for lines to put into 334432Seric ** class x. Use scanf string 'fmt' 344432Seric ** or "%s" if not present. Fmt should 354432Seric ** only produce one string-valued result. 364432Seric ** Hname: value Define header with field-name 'name' 374432Seric ** and value as specified; this will be 384432Seric ** macro expanded immediately before 394432Seric ** use. 404432Seric ** Sn Use rewriting set n. 414432Seric ** Rlhs rhs Rewrite addresses that match lhs to 424432Seric ** be rhs. 4324944Seric ** Mn arg=val... Define mailer. n is the internal name. 4424944Seric ** Args specify mailer parameters. 458252Seric ** Oxvalue Set option x to value. 468252Seric ** Pname=value Set precedence name to value. 4752645Seric ** Vversioncode Version level of configuration syntax. 4853654Seric ** Kmapname mapclass arguments.... 4953654Seric ** Define keyed lookup of a given class. 5053654Seric ** Arguments are class dependent. 514432Seric ** 523308Seric ** Parameters: 533308Seric ** cfname -- control file name. 5454973Seric ** safe -- TRUE if this is the system config file; 5554973Seric ** FALSE otherwise. 5655012Seric ** e -- the main envelope. 573308Seric ** 583308Seric ** Returns: 593308Seric ** none. 603308Seric ** 613308Seric ** Side Effects: 623308Seric ** Builds several internal tables. 633308Seric */ 643308Seric 6555012Seric readcf(cfname, safe, e) 663308Seric char *cfname; 6754973Seric bool safe; 6855012Seric register ENVELOPE *e; 693308Seric { 703308Seric FILE *cf; 718547Seric int ruleset = 0; 728547Seric char *q; 738547Seric char **pv; 749350Seric struct rewrite *rwp = NULL; 7557135Seric char *bp; 76*57589Seric int nfuzzy; 773308Seric char buf[MAXLINE]; 783308Seric register char *p; 793308Seric extern char **prescan(); 803308Seric extern char **copyplist(); 8152647Seric struct stat statb; 825909Seric char exbuf[MAXLINE]; 8316915Seric char pvpbuf[PSBUFSIZE]; 849350Seric extern char *fgetfolded(); 8510709Seric extern char *munchstring(); 8653654Seric extern void makemapentry(); 873308Seric 8852647Seric FileName = cfname; 8952647Seric LineNumber = 0; 9052647Seric 913308Seric cf = fopen(cfname, "r"); 923308Seric if (cf == NULL) 933308Seric { 9452647Seric syserr("cannot open"); 953308Seric exit(EX_OSFILE); 963308Seric } 973308Seric 9852647Seric if (fstat(fileno(cf), &statb) < 0) 9952647Seric { 10052647Seric syserr("cannot fstat"); 10152647Seric exit(EX_OSFILE); 10252647Seric } 10352647Seric 10452647Seric if (!S_ISREG(statb.st_mode)) 10552647Seric { 10652647Seric syserr("not a plain file"); 10752647Seric exit(EX_OSFILE); 10852647Seric } 10952647Seric 11052647Seric if (OpMode != MD_TEST && bitset(S_IWGRP|S_IWOTH, statb.st_mode)) 11152647Seric { 11253037Seric if (OpMode == MD_DAEMON || OpMode == MD_FREEZE) 11353037Seric fprintf(stderr, "%s: WARNING: dangerous write permissions\n", 11453037Seric FileName); 11553037Seric #ifdef LOG 11653037Seric if (LogLevel > 0) 11753037Seric syslog(LOG_CRIT, "%s: WARNING: dangerous write permissions", 11853037Seric FileName); 11953037Seric #endif 12052647Seric } 12152647Seric 12257135Seric while ((bp = fgetfolded(buf, sizeof buf, cf)) != NULL) 1233308Seric { 12457135Seric if (bp[0] == '#') 12557135Seric { 12657135Seric if (bp != buf) 12757135Seric free(bp); 12852637Seric continue; 12957135Seric } 13052637Seric 13116157Seric /* map $ into \001 (ASCII SOH) for macro expansion */ 13257135Seric for (p = bp; *p != '\0'; p++) 13316157Seric { 13457135Seric if (*p == '#' && p > bp && ConfigLevel >= 3) 13552647Seric { 13652647Seric /* this is an on-line comment */ 13752647Seric register char *e; 13852647Seric 13952647Seric switch (*--p) 14052647Seric { 14152647Seric case '\001': 14252647Seric /* it's from $# -- let it go through */ 14352647Seric p++; 14452647Seric break; 14552647Seric 14652647Seric case '\\': 14752647Seric /* it's backslash escaped */ 14852647Seric (void) strcpy(p, p + 1); 14952647Seric break; 15052647Seric 15152647Seric default: 15252647Seric /* delete preceeding white space */ 15357135Seric while (isspace(*p) && p > bp) 15452647Seric p--; 15556795Seric if ((e = strchr(++p, '\n')) != NULL) 15652647Seric (void) strcpy(p, e); 15752647Seric else 15852647Seric p[0] = p[1] = '\0'; 15952647Seric break; 16052647Seric } 16152647Seric continue; 16252647Seric } 16352647Seric 16416157Seric if (*p != '$') 16516157Seric continue; 16616157Seric 16716157Seric if (p[1] == '$') 16816157Seric { 16916157Seric /* actual dollar sign.... */ 17023111Seric (void) strcpy(p, p + 1); 17116157Seric continue; 17216157Seric } 17316157Seric 17416157Seric /* convert to macro expansion character */ 17516157Seric *p = '\001'; 17616157Seric } 17716157Seric 17816157Seric /* interpret this line */ 17957135Seric switch (bp[0]) 1803308Seric { 1813308Seric case '\0': 1823308Seric case '#': /* comment */ 1833308Seric break; 1843308Seric 1853308Seric case 'R': /* rewriting rule */ 18657135Seric for (p = &bp[1]; *p != '\0' && *p != '\t'; p++) 1873308Seric continue; 1883308Seric 1893308Seric if (*p == '\0') 1905909Seric { 19157135Seric syserr("invalid rewrite line \"%s\"", bp); 1925909Seric break; 1935909Seric } 1945909Seric 1955909Seric /* allocate space for the rule header */ 1965909Seric if (rwp == NULL) 1975909Seric { 1985909Seric RewriteRules[ruleset] = rwp = 1995909Seric (struct rewrite *) xalloc(sizeof *rwp); 2005909Seric } 2013308Seric else 2023308Seric { 2035909Seric rwp->r_next = (struct rewrite *) xalloc(sizeof *rwp); 2045909Seric rwp = rwp->r_next; 2055909Seric } 2065909Seric rwp->r_next = NULL; 2073308Seric 2085909Seric /* expand and save the LHS */ 2095909Seric *p = '\0'; 21057135Seric expand(&bp[1], exbuf, &exbuf[sizeof exbuf], e); 21116915Seric rwp->r_lhs = prescan(exbuf, '\t', pvpbuf); 212*57589Seric nfuzzy = 0; 2135909Seric if (rwp->r_lhs != NULL) 214*57589Seric { 215*57589Seric register char **ap; 216*57589Seric 2175909Seric rwp->r_lhs = copyplist(rwp->r_lhs, TRUE); 218*57589Seric 219*57589Seric /* count the number of fuzzy matches in LHS */ 220*57589Seric for (ap = rwp->r_lhs; *ap != NULL; ap++) 221*57589Seric { 222*57589Seric switch (**ap) 223*57589Seric { 224*57589Seric case MATCHZANY: 225*57589Seric case MATCHANY: 226*57589Seric case MATCHONE: 227*57589Seric case MATCHCLASS: 228*57589Seric case MATCHNCLASS: 229*57589Seric nfuzzy++; 230*57589Seric } 231*57589Seric } 232*57589Seric } 23356678Seric else 23456678Seric syserr("R line: null LHS"); 2355909Seric 2365909Seric /* expand and save the RHS */ 2375909Seric while (*++p == '\t') 2385909Seric continue; 2397231Seric q = p; 2407231Seric while (*p != '\0' && *p != '\t') 2417231Seric p++; 2427231Seric *p = '\0'; 24355012Seric expand(q, exbuf, &exbuf[sizeof exbuf], e); 24416915Seric rwp->r_rhs = prescan(exbuf, '\t', pvpbuf); 2455909Seric if (rwp->r_rhs != NULL) 246*57589Seric { 247*57589Seric register char **ap; 248*57589Seric 2495909Seric rwp->r_rhs = copyplist(rwp->r_rhs, TRUE); 250*57589Seric 251*57589Seric /* check no out-of-bounds replacements */ 252*57589Seric nfuzzy += '0'; 253*57589Seric for (ap = rwp->r_rhs; *ap != NULL; ap++) 254*57589Seric { 255*57589Seric if (**ap != MATCHREPL) 256*57589Seric continue; 257*57589Seric if ((*ap)[1] <= '0' || (*ap)[1] > nfuzzy) 258*57589Seric { 259*57589Seric syserr("replacement $%c out of bounds", 260*57589Seric (*ap)[1]); 261*57589Seric } 262*57589Seric } 263*57589Seric } 26456678Seric else 26556678Seric syserr("R line: null RHS"); 2663308Seric break; 2673308Seric 2684072Seric case 'S': /* select rewriting set */ 26957135Seric ruleset = atoi(&bp[1]); 2708056Seric if (ruleset >= MAXRWSETS || ruleset < 0) 2718056Seric { 2729381Seric syserr("bad ruleset %d (%d max)", ruleset, MAXRWSETS); 2738056Seric ruleset = 0; 2748056Seric } 2754072Seric rwp = NULL; 2764072Seric break; 2774072Seric 2783308Seric case 'D': /* macro definition */ 27957135Seric define(bp[1], newstr(munchstring(&bp[2])), e); 2803308Seric break; 2813308Seric 2823387Seric case 'H': /* required header line */ 28357135Seric (void) chompheader(&bp[1], TRUE, e); 2843387Seric break; 2853387Seric 2864061Seric case 'C': /* word class */ 2874432Seric case 'F': /* word class from file */ 2884432Seric /* read list of words from argument or file */ 28957135Seric if (bp[0] == 'F') 2904432Seric { 2914432Seric /* read from file */ 29257135Seric for (p = &bp[2]; *p != '\0' && !isspace(*p); p++) 2934432Seric continue; 2944432Seric if (*p == '\0') 2954432Seric p = "%s"; 2964432Seric else 2974432Seric { 2984432Seric *p = '\0'; 2994432Seric while (isspace(*++p)) 3004432Seric continue; 3014432Seric } 30257135Seric fileclass(bp[1], &bp[2], p, safe); 3034432Seric break; 3044432Seric } 3054061Seric 3064432Seric /* scan the list of words and set class for all */ 30757135Seric for (p = &bp[2]; *p != '\0'; ) 3084061Seric { 3094061Seric register char *wd; 3104061Seric char delim; 3114061Seric 3124061Seric while (*p != '\0' && isspace(*p)) 3134061Seric p++; 3144061Seric wd = p; 3154061Seric while (*p != '\0' && !isspace(*p)) 3164061Seric p++; 3174061Seric delim = *p; 3184061Seric *p = '\0'; 3194061Seric if (wd[0] != '\0') 32057135Seric setclass(bp[1], wd); 3214061Seric *p = delim; 3224061Seric } 3234061Seric break; 3244061Seric 3254096Seric case 'M': /* define mailer */ 32657135Seric makemailer(&bp[1]); 3274096Seric break; 3284096Seric 3298252Seric case 'O': /* set option */ 33057135Seric setoption(bp[1], &bp[2], safe, FALSE); 3318252Seric break; 3328252Seric 3338252Seric case 'P': /* set precedence */ 3348252Seric if (NumPriorities >= MAXPRIORITIES) 3358252Seric { 3368547Seric toomany('P', MAXPRIORITIES); 3378252Seric break; 3388252Seric } 33957135Seric for (p = &bp[1]; *p != '\0' && *p != '=' && *p != '\t'; p++) 3408252Seric continue; 3418252Seric if (*p == '\0') 3428252Seric goto badline; 3438252Seric *p = '\0'; 34457135Seric Priorities[NumPriorities].pri_name = newstr(&bp[1]); 3458252Seric Priorities[NumPriorities].pri_val = atoi(++p); 3468252Seric NumPriorities++; 3478252Seric break; 3488252Seric 3498547Seric case 'T': /* trusted user(s) */ 35057135Seric p = &bp[1]; 3518547Seric while (*p != '\0') 3528547Seric { 3538547Seric while (isspace(*p)) 3548547Seric p++; 3558547Seric q = p; 3568547Seric while (*p != '\0' && !isspace(*p)) 3578547Seric p++; 3588547Seric if (*p != '\0') 3598547Seric *p++ = '\0'; 3608547Seric if (*q == '\0') 3618547Seric continue; 3628547Seric for (pv = TrustedUsers; *pv != NULL; pv++) 3638547Seric continue; 3648547Seric if (pv >= &TrustedUsers[MAXTRUST]) 3658547Seric { 3668547Seric toomany('T', MAXTRUST); 3678547Seric break; 3688547Seric } 3698547Seric *pv = newstr(q); 3708547Seric } 3718547Seric break; 3728547Seric 37352645Seric case 'V': /* configuration syntax version */ 37457135Seric ConfigLevel = atoi(&bp[1]); 37552645Seric break; 37652645Seric 37753654Seric case 'K': 37857135Seric makemapentry(&bp[1]); 37953654Seric break; 38053654Seric 3813308Seric default: 3824061Seric badline: 38357135Seric syserr("unknown control line \"%s\"", bp); 3843308Seric } 38557135Seric if (bp != buf) 38657135Seric free(bp); 3873308Seric } 38852637Seric if (ferror(cf)) 38952637Seric { 39052647Seric syserr("I/O read error", cfname); 39152637Seric exit(EX_OSFILE); 39252637Seric } 39352637Seric fclose(cf); 3949381Seric FileName = NULL; 39556836Seric 39657076Seric if (stab("host", ST_MAP, ST_FIND) == NULL) 39757076Seric { 39857076Seric /* user didn't initialize: set up host map */ 39957076Seric strcpy(buf, "host host"); 40057076Seric if (ConfigLevel >= 2) 40157076Seric strcat(buf, " -a."); 40257076Seric makemapentry(buf); 40357076Seric } 4044096Seric } 4054096Seric /* 4068547Seric ** TOOMANY -- signal too many of some option 4078547Seric ** 4088547Seric ** Parameters: 4098547Seric ** id -- the id of the error line 4108547Seric ** maxcnt -- the maximum possible values 4118547Seric ** 4128547Seric ** Returns: 4138547Seric ** none. 4148547Seric ** 4158547Seric ** Side Effects: 4168547Seric ** gives a syserr. 4178547Seric */ 4188547Seric 4198547Seric toomany(id, maxcnt) 4208547Seric char id; 4218547Seric int maxcnt; 4228547Seric { 4239381Seric syserr("too many %c lines, %d max", id, maxcnt); 4248547Seric } 4258547Seric /* 4264432Seric ** FILECLASS -- read members of a class from a file 4274432Seric ** 4284432Seric ** Parameters: 4294432Seric ** class -- class to define. 4304432Seric ** filename -- name of file to read. 4314432Seric ** fmt -- scanf string to use for match. 4324432Seric ** 4334432Seric ** Returns: 4344432Seric ** none 4354432Seric ** 4364432Seric ** Side Effects: 4374432Seric ** 4384432Seric ** puts all lines in filename that match a scanf into 4394432Seric ** the named class. 4404432Seric */ 4414432Seric 44254973Seric fileclass(class, filename, fmt, safe) 4434432Seric int class; 4444432Seric char *filename; 4454432Seric char *fmt; 44654973Seric bool safe; 4474432Seric { 44825808Seric FILE *f; 44954973Seric struct stat stbuf; 4504432Seric char buf[MAXLINE]; 4514432Seric 45254973Seric if (stat(filename, &stbuf) < 0) 45354973Seric { 45454973Seric syserr("fileclass: cannot stat %s", filename); 45554973Seric return; 45654973Seric } 45754973Seric if (!S_ISREG(stbuf.st_mode)) 45854973Seric { 45954973Seric syserr("fileclass: %s not a regular file", filename); 46054973Seric return; 46154973Seric } 46254973Seric if (!safe && access(filename, R_OK) < 0) 46354973Seric { 46454973Seric syserr("fileclass: access denied on %s", filename); 46554973Seric return; 46654973Seric } 46754973Seric f = fopen(filename, "r"); 4684432Seric if (f == NULL) 4694432Seric { 47054973Seric syserr("fileclass: cannot open %s", filename); 4714432Seric return; 4724432Seric } 4734432Seric 4744432Seric while (fgets(buf, sizeof buf, f) != NULL) 4754432Seric { 4764432Seric register STAB *s; 47725808Seric register char *p; 47825808Seric # ifdef SCANF 4794432Seric char wordbuf[MAXNAME+1]; 4804432Seric 4814432Seric if (sscanf(buf, fmt, wordbuf) != 1) 4824432Seric continue; 48325808Seric p = wordbuf; 48456795Seric # else /* SCANF */ 48525808Seric p = buf; 48656795Seric # endif /* SCANF */ 48725808Seric 48825808Seric /* 48925808Seric ** Break up the match into words. 49025808Seric */ 49125808Seric 49225808Seric while (*p != '\0') 49325808Seric { 49425808Seric register char *q; 49525808Seric 49625808Seric /* strip leading spaces */ 49725808Seric while (isspace(*p)) 49825808Seric p++; 49925808Seric if (*p == '\0') 50025808Seric break; 50125808Seric 50225808Seric /* find the end of the word */ 50325808Seric q = p; 50425808Seric while (*p != '\0' && !isspace(*p)) 50525808Seric p++; 50625808Seric if (*p != '\0') 50725808Seric *p++ = '\0'; 50825808Seric 50925808Seric /* enter the word in the symbol table */ 51025808Seric s = stab(q, ST_CLASS, ST_ENTER); 51125808Seric setbitn(class, s->s_class); 51225808Seric } 5134432Seric } 5144432Seric 51554973Seric (void) fclose(f); 5164432Seric } 5174432Seric /* 5184096Seric ** MAKEMAILER -- define a new mailer. 5194096Seric ** 5204096Seric ** Parameters: 52110327Seric ** line -- description of mailer. This is in labeled 52210327Seric ** fields. The fields are: 52310327Seric ** P -- the path to the mailer 52410327Seric ** F -- the flags associated with the mailer 52510327Seric ** A -- the argv for this mailer 52610327Seric ** S -- the sender rewriting set 52710327Seric ** R -- the recipient rewriting set 52810327Seric ** E -- the eol string 52910327Seric ** The first word is the canonical name of the mailer. 5304096Seric ** 5314096Seric ** Returns: 5324096Seric ** none. 5334096Seric ** 5344096Seric ** Side Effects: 5354096Seric ** enters the mailer into the mailer table. 5364096Seric */ 5373308Seric 53821066Seric makemailer(line) 5394096Seric char *line; 5404096Seric { 5414096Seric register char *p; 5428067Seric register struct mailer *m; 5438067Seric register STAB *s; 5448067Seric int i; 54510327Seric char fcode; 5464096Seric extern int NextMailer; 54710327Seric extern char **makeargv(); 54810327Seric extern char *munchstring(); 54910327Seric extern char *DelimChar; 55010701Seric extern long atol(); 5514096Seric 55210327Seric /* allocate a mailer and set up defaults */ 55310327Seric m = (struct mailer *) xalloc(sizeof *m); 55410327Seric bzero((char *) m, sizeof *m); 55510327Seric m->m_eol = "\n"; 55610327Seric 55710327Seric /* collect the mailer name */ 55810327Seric for (p = line; *p != '\0' && *p != ',' && !isspace(*p); p++) 55910327Seric continue; 56010327Seric if (*p != '\0') 56110327Seric *p++ = '\0'; 56210327Seric m->m_name = newstr(line); 56310327Seric 56410327Seric /* now scan through and assign info from the fields */ 56510327Seric while (*p != '\0') 56610327Seric { 56710327Seric while (*p != '\0' && (*p == ',' || isspace(*p))) 56810327Seric p++; 56910327Seric 57010327Seric /* p now points to field code */ 57110327Seric fcode = *p; 57210327Seric while (*p != '\0' && *p != '=' && *p != ',') 57310327Seric p++; 57410327Seric if (*p++ != '=') 57510327Seric { 57652637Seric syserr("mailer %s: `=' expected", m->m_name); 57710327Seric return; 57810327Seric } 57910327Seric while (isspace(*p)) 58010327Seric p++; 58110327Seric 58210327Seric /* p now points to the field body */ 58310327Seric p = munchstring(p); 58410327Seric 58510327Seric /* install the field into the mailer struct */ 58610327Seric switch (fcode) 58710327Seric { 58810327Seric case 'P': /* pathname */ 58910327Seric m->m_mailer = newstr(p); 59010327Seric break; 59110327Seric 59210327Seric case 'F': /* flags */ 59310687Seric for (; *p != '\0'; p++) 59452637Seric if (!isspace(*p)) 59552637Seric setbitn(*p, m->m_flags); 59610327Seric break; 59710327Seric 59810327Seric case 'S': /* sender rewriting ruleset */ 59910327Seric case 'R': /* recipient rewriting ruleset */ 60010327Seric i = atoi(p); 60110327Seric if (i < 0 || i >= MAXRWSETS) 60210327Seric { 60310327Seric syserr("invalid rewrite set, %d max", MAXRWSETS); 60410327Seric return; 60510327Seric } 60610327Seric if (fcode == 'S') 60710327Seric m->m_s_rwset = i; 60810327Seric else 60910327Seric m->m_r_rwset = i; 61010327Seric break; 61110327Seric 61210327Seric case 'E': /* end of line string */ 61310327Seric m->m_eol = newstr(p); 61410327Seric break; 61510327Seric 61610327Seric case 'A': /* argument vector */ 61710327Seric m->m_argv = makeargv(p); 61810327Seric break; 61910701Seric 62010701Seric case 'M': /* maximum message size */ 62110701Seric m->m_maxsize = atol(p); 62210701Seric break; 62352106Seric 62452106Seric case 'L': /* maximum line length */ 62552106Seric m->m_linelimit = atoi(p); 62652106Seric break; 62710327Seric } 62810327Seric 62910327Seric p = DelimChar; 63010327Seric } 63110327Seric 63252106Seric /* do some heuristic cleanup for back compatibility */ 63352106Seric if (bitnset(M_LIMITS, m->m_flags)) 63452106Seric { 63552106Seric if (m->m_linelimit == 0) 63652106Seric m->m_linelimit = SMTPLINELIM; 63755418Seric if (ConfigLevel < 2) 63852106Seric setbitn(M_7BITS, m->m_flags); 63952106Seric } 64052106Seric 6414096Seric if (NextMailer >= MAXMAILERS) 6424096Seric { 6439381Seric syserr("too many mailers defined (%d max)", MAXMAILERS); 6444096Seric return; 6454096Seric } 64657402Seric 64710327Seric s = stab(m->m_name, ST_MAILER, ST_ENTER); 64857402Seric if (s->s_mailer != NULL) 64957402Seric { 65057402Seric i = s->s_mailer->m_mno; 65157402Seric free(s->s_mailer); 65257402Seric } 65357402Seric else 65457402Seric { 65557402Seric i = NextMailer++; 65657402Seric } 65757402Seric Mailer[i] = s->s_mailer = m; 65857454Seric m->m_mno = i; 65910327Seric } 66010327Seric /* 66110327Seric ** MUNCHSTRING -- translate a string into internal form. 66210327Seric ** 66310327Seric ** Parameters: 66410327Seric ** p -- the string to munch. 66510327Seric ** 66610327Seric ** Returns: 66710327Seric ** the munched string. 66810327Seric ** 66910327Seric ** Side Effects: 67010327Seric ** Sets "DelimChar" to point to the string that caused us 67110327Seric ** to stop. 67210327Seric */ 6734096Seric 67410327Seric char * 67510327Seric munchstring(p) 67610327Seric register char *p; 67710327Seric { 67810327Seric register char *q; 67910327Seric bool backslash = FALSE; 68010327Seric bool quotemode = FALSE; 68110327Seric static char buf[MAXLINE]; 68210327Seric extern char *DelimChar; 6834096Seric 68410327Seric for (q = buf; *p != '\0'; p++) 6854096Seric { 68610327Seric if (backslash) 68710327Seric { 68810327Seric /* everything is roughly literal */ 68910357Seric backslash = FALSE; 69010327Seric switch (*p) 69110327Seric { 69210327Seric case 'r': /* carriage return */ 69310327Seric *q++ = '\r'; 69410327Seric continue; 69510327Seric 69610327Seric case 'n': /* newline */ 69710327Seric *q++ = '\n'; 69810327Seric continue; 69910327Seric 70010327Seric case 'f': /* form feed */ 70110327Seric *q++ = '\f'; 70210327Seric continue; 70310327Seric 70410327Seric case 'b': /* backspace */ 70510327Seric *q++ = '\b'; 70610327Seric continue; 70710327Seric } 70810327Seric *q++ = *p; 70910327Seric } 71010327Seric else 71110327Seric { 71210327Seric if (*p == '\\') 71310327Seric backslash = TRUE; 71410327Seric else if (*p == '"') 71510327Seric quotemode = !quotemode; 71610327Seric else if (quotemode || *p != ',') 71710327Seric *q++ = *p; 71810327Seric else 71910327Seric break; 72010327Seric } 7214096Seric } 7224096Seric 72310327Seric DelimChar = p; 72410327Seric *q++ = '\0'; 72510327Seric return (buf); 72610327Seric } 72710327Seric /* 72810327Seric ** MAKEARGV -- break up a string into words 72910327Seric ** 73010327Seric ** Parameters: 73110327Seric ** p -- the string to break up. 73210327Seric ** 73310327Seric ** Returns: 73410327Seric ** a char **argv (dynamically allocated) 73510327Seric ** 73610327Seric ** Side Effects: 73710327Seric ** munges p. 73810327Seric */ 7394096Seric 74010327Seric char ** 74110327Seric makeargv(p) 74210327Seric register char *p; 74310327Seric { 74410327Seric char *q; 74510327Seric int i; 74610327Seric char **avp; 74710327Seric char *argv[MAXPV + 1]; 74810327Seric 74910327Seric /* take apart the words */ 75010327Seric i = 0; 75110327Seric while (*p != '\0' && i < MAXPV) 7524096Seric { 75310327Seric q = p; 75410327Seric while (*p != '\0' && !isspace(*p)) 75510327Seric p++; 75610327Seric while (isspace(*p)) 75710327Seric *p++ = '\0'; 75810327Seric argv[i++] = newstr(q); 7594096Seric } 76010327Seric argv[i++] = NULL; 7614096Seric 76210327Seric /* now make a copy of the argv */ 76310327Seric avp = (char **) xalloc(sizeof *avp * i); 76416893Seric bcopy((char *) argv, (char *) avp, sizeof *avp * i); 76510327Seric 76610327Seric return (avp); 7673308Seric } 7683308Seric /* 7693308Seric ** PRINTRULES -- print rewrite rules (for debugging) 7703308Seric ** 7713308Seric ** Parameters: 7723308Seric ** none. 7733308Seric ** 7743308Seric ** Returns: 7753308Seric ** none. 7763308Seric ** 7773308Seric ** Side Effects: 7783308Seric ** prints rewrite rules. 7793308Seric */ 7803308Seric 7813308Seric printrules() 7823308Seric { 7833308Seric register struct rewrite *rwp; 7844072Seric register int ruleset; 7853308Seric 7864072Seric for (ruleset = 0; ruleset < 10; ruleset++) 7873308Seric { 7884072Seric if (RewriteRules[ruleset] == NULL) 7894072Seric continue; 7908067Seric printf("\n----Rule Set %d:", ruleset); 7913308Seric 7924072Seric for (rwp = RewriteRules[ruleset]; rwp != NULL; rwp = rwp->r_next) 7933308Seric { 7948067Seric printf("\nLHS:"); 7958067Seric printav(rwp->r_lhs); 7968067Seric printf("RHS:"); 7978067Seric printav(rwp->r_rhs); 7983308Seric } 7993308Seric } 8003308Seric } 8014319Seric 8024096Seric /* 8038256Seric ** SETOPTION -- set global processing option 8048256Seric ** 8058256Seric ** Parameters: 8068256Seric ** opt -- option name. 8078256Seric ** val -- option value (as a text string). 80821755Seric ** safe -- set if this came from a configuration file. 80921755Seric ** Some options (if set from the command line) will 81021755Seric ** reset the user id to avoid security problems. 8118269Seric ** sticky -- if set, don't let other setoptions override 8128269Seric ** this value. 8138256Seric ** 8148256Seric ** Returns: 8158256Seric ** none. 8168256Seric ** 8178256Seric ** Side Effects: 8188256Seric ** Sets options as implied by the arguments. 8198256Seric */ 8208256Seric 82110687Seric static BITMAP StickyOpt; /* set if option is stuck */ 8228269Seric 82357207Seric 82457207Seric #ifdef NAMED_BIND 82557207Seric 82657207Seric struct resolverflags 82757207Seric { 82857207Seric char *rf_name; /* name of the flag */ 82957207Seric long rf_bits; /* bits to set/clear */ 83057207Seric } ResolverFlags[] = 83157207Seric { 83257207Seric "debug", RES_DEBUG, 83357207Seric "aaonly", RES_AAONLY, 83457207Seric "usevc", RES_USEVC, 83557207Seric "primary", RES_PRIMARY, 83657207Seric "igntc", RES_IGNTC, 83757207Seric "recurse", RES_RECURSE, 83857207Seric "defnames", RES_DEFNAMES, 83957207Seric "stayopen", RES_STAYOPEN, 84057207Seric "dnsrch", RES_DNSRCH, 84157207Seric NULL, 0 84257207Seric }; 84357207Seric 84457207Seric #endif 84557207Seric 84621755Seric setoption(opt, val, safe, sticky) 8478256Seric char opt; 8488256Seric char *val; 84921755Seric bool safe; 8508269Seric bool sticky; 8518256Seric { 85257207Seric register char *p; 8538265Seric extern bool atobool(); 85412633Seric extern time_t convtime(); 85514879Seric extern int QueueLA; 85614879Seric extern int RefuseLA; 85717474Seric extern bool trusteduser(); 85817474Seric extern char *username(); 8598256Seric 8608256Seric if (tTd(37, 1)) 8619341Seric printf("setoption %c=%s", opt, val); 8628256Seric 8638256Seric /* 8648269Seric ** See if this option is preset for us. 8658256Seric */ 8668256Seric 86710687Seric if (bitnset(opt, StickyOpt)) 8688269Seric { 8699341Seric if (tTd(37, 1)) 8709341Seric printf(" (ignored)\n"); 8718269Seric return; 8728269Seric } 8738269Seric 87421755Seric /* 87521755Seric ** Check to see if this option can be specified by this user. 87621755Seric */ 87721755Seric 87836238Skarels if (!safe && getuid() == 0) 87921755Seric safe = TRUE; 88057136Seric if (!safe && strchr("deEiLmorsvC8", opt) == NULL) 88121755Seric { 88239111Srick if (opt != 'M' || (val[0] != 'r' && val[0] != 's')) 88321755Seric { 88436582Sbostic if (tTd(37, 1)) 88536582Sbostic printf(" (unsafe)"); 88636582Sbostic if (getuid() != geteuid()) 88736582Sbostic { 88851210Seric if (tTd(37, 1)) 88951210Seric printf("(Resetting uid)"); 89036582Sbostic (void) setgid(getgid()); 89136582Sbostic (void) setuid(getuid()); 89236582Sbostic } 89321755Seric } 89421755Seric } 89551210Seric if (tTd(37, 1)) 89617985Seric printf("\n"); 8978269Seric 8988256Seric switch (opt) 8998256Seric { 90052106Seric case '8': /* allow eight-bit input */ 90152106Seric EightBit = atobool(val); 90252106Seric break; 90352106Seric 9048256Seric case 'A': /* set default alias file */ 9059381Seric if (val[0] == '\0') 9068269Seric AliasFile = "aliases"; 9079381Seric else 9089381Seric AliasFile = newstr(val); 9098256Seric break; 9108256Seric 91117474Seric case 'a': /* look N minutes for "@:@" in alias file */ 91217474Seric if (val[0] == '\0') 91317474Seric SafeAlias = 5; 91417474Seric else 91517474Seric SafeAlias = atoi(val); 91617474Seric break; 91717474Seric 91816843Seric case 'B': /* substitution for blank character */ 91916843Seric SpaceSub = val[0]; 92016843Seric if (SpaceSub == '\0') 92116843Seric SpaceSub = ' '; 92216843Seric break; 92316843Seric 9249284Seric case 'c': /* don't connect to "expensive" mailers */ 9259381Seric NoConnect = atobool(val); 9269284Seric break; 9279284Seric 92851305Seric case 'C': /* checkpoint every N addresses */ 92951305Seric CheckpointInterval = atoi(val); 93024944Seric break; 93124944Seric 9329284Seric case 'd': /* delivery mode */ 9339284Seric switch (*val) 9348269Seric { 9359284Seric case '\0': 9369284Seric SendMode = SM_DELIVER; 9378269Seric break; 9388269Seric 93910755Seric case SM_QUEUE: /* queue only */ 94010755Seric #ifndef QUEUE 94110755Seric syserr("need QUEUE to set -odqueue"); 94256795Seric #endif /* QUEUE */ 94310755Seric /* fall through..... */ 94410755Seric 9459284Seric case SM_DELIVER: /* do everything */ 9469284Seric case SM_FORK: /* fork after verification */ 9479284Seric SendMode = *val; 9488269Seric break; 9498269Seric 9508269Seric default: 9519284Seric syserr("Unknown delivery mode %c", *val); 9528269Seric exit(EX_USAGE); 9538269Seric } 9548269Seric break; 9558269Seric 9569146Seric case 'D': /* rebuild alias database as needed */ 9579381Seric AutoRebuild = atobool(val); 9589146Seric break; 9599146Seric 96055372Seric case 'E': /* error message header/header file */ 96155379Seric if (*val != '\0') 96255379Seric ErrMsgFile = newstr(val); 96355372Seric break; 96455372Seric 9658269Seric case 'e': /* set error processing mode */ 9668269Seric switch (*val) 9678269Seric { 9689381Seric case EM_QUIET: /* be silent about it */ 9699381Seric case EM_MAIL: /* mail back */ 9709381Seric case EM_BERKNET: /* do berknet error processing */ 9719381Seric case EM_WRITE: /* write back (or mail) */ 9728269Seric HoldErrs = TRUE; 9739381Seric /* fall through... */ 9748269Seric 9759381Seric case EM_PRINT: /* print errors normally (default) */ 9769381Seric ErrorMode = *val; 9778269Seric break; 9788269Seric } 9798269Seric break; 9808269Seric 9819049Seric case 'F': /* file mode */ 98217975Seric FileMode = atooct(val) & 0777; 9839049Seric break; 9849049Seric 9858269Seric case 'f': /* save Unix-style From lines on front */ 9869381Seric SaveFrom = atobool(val); 9878269Seric break; 9888269Seric 98953735Seric case 'G': /* match recipients against GECOS field */ 99053735Seric MatchGecos = atobool(val); 99153735Seric break; 99253735Seric 9938256Seric case 'g': /* default gid */ 99417474Seric DefGid = atoi(val); 9958256Seric break; 9968256Seric 9978256Seric case 'H': /* help file */ 9989381Seric if (val[0] == '\0') 9998269Seric HelpFile = "sendmail.hf"; 10009381Seric else 10019381Seric HelpFile = newstr(val); 10028256Seric break; 10038256Seric 100451305Seric case 'h': /* maximum hop count */ 100551305Seric MaxHopCount = atoi(val); 100651305Seric break; 100751305Seric 100835651Seric case 'I': /* use internet domain name server */ 100957207Seric #ifdef NAMED_BIND 101057207Seric UseNameServer = TRUE; 101157207Seric for (p = val; *p != 0; ) 101257207Seric { 101357207Seric bool clearmode; 101457207Seric char *q; 101557207Seric struct resolverflags *rfp; 101657207Seric 101757207Seric while (*p == ' ') 101857207Seric p++; 101957207Seric if (*p == '\0') 102057207Seric break; 102157207Seric clearmode = FALSE; 102257207Seric if (*p == '-') 102357207Seric clearmode = TRUE; 102457207Seric else if (*p != '+') 102557207Seric p--; 102657207Seric p++; 102757207Seric q = p; 102857207Seric while (*p != '\0' && !isspace(*p)) 102957207Seric p++; 103057207Seric if (*p != '\0') 103157207Seric *p++ = '\0'; 103257207Seric for (rfp = ResolverFlags; rfp->rf_name != NULL; rfp++) 103357207Seric { 103457207Seric if (strcasecmp(q, rfp->rf_name) == 0) 103557207Seric break; 103657207Seric } 103757207Seric if (clearmode) 103857207Seric _res.options &= ~rfp->rf_bits; 103957207Seric else 104057207Seric _res.options |= rfp->rf_bits; 104157207Seric } 104257207Seric if (tTd(8, 2)) 104357207Seric printf("_res.options = %x\n", _res.options); 104457207Seric #else 104557207Seric usrerr("name server (I option) specified but BIND not compiled in"); 104657207Seric #endif 104735651Seric break; 104835651Seric 10498269Seric case 'i': /* ignore dot lines in message */ 10509381Seric IgnrDot = atobool(val); 10518269Seric break; 10528269Seric 105357136Seric case 'J': /* .forward search path */ 105457136Seric ForwardPath = newstr(val); 105557136Seric break; 105657136Seric 105754967Seric case 'k': /* connection cache size */ 105854967Seric MaxMciCache = atoi(val); 105956215Seric if (MaxMciCache < 0) 106056215Seric MaxMciCache = 0; 106154967Seric break; 106254967Seric 106354967Seric case 'K': /* connection cache timeout */ 106454967Seric MciCacheTimeout = convtime(val); 106554967Seric break; 106654967Seric 10678256Seric case 'L': /* log level */ 10689381Seric LogLevel = atoi(val); 10698256Seric break; 10708256Seric 10718269Seric case 'M': /* define macro */ 10729381Seric define(val[0], newstr(&val[1]), CurEnv); 107316878Seric sticky = FALSE; 10748269Seric break; 10758269Seric 10768269Seric case 'm': /* send to me too */ 10779381Seric MeToo = atobool(val); 10788269Seric break; 10798269Seric 108025820Seric case 'n': /* validate RHS in newaliases */ 108125820Seric CheckAliases = atobool(val); 108225820Seric break; 108325820Seric 10848269Seric case 'o': /* assume old style headers */ 10859381Seric if (atobool(val)) 10869341Seric CurEnv->e_flags |= EF_OLDSTYLE; 10879341Seric else 10889341Seric CurEnv->e_flags &= ~EF_OLDSTYLE; 10898269Seric break; 10908269Seric 109124944Seric case 'P': /* postmaster copy address for returned mail */ 109224944Seric PostMasterCopy = newstr(val); 109324944Seric break; 109424944Seric 109524944Seric case 'q': /* slope of queue only function */ 109624944Seric QueueFactor = atoi(val); 109724944Seric break; 109824944Seric 10998256Seric case 'Q': /* queue directory */ 11009381Seric if (val[0] == '\0') 11018269Seric QueueDir = "mqueue"; 11029381Seric else 11039381Seric QueueDir = newstr(val); 11048256Seric break; 11058256Seric 11068256Seric case 'r': /* read timeout */ 11079381Seric ReadTimeout = convtime(val); 11088256Seric break; 11098256Seric 11108256Seric case 'S': /* status file */ 11119381Seric if (val[0] == '\0') 11128269Seric StatFile = "sendmail.st"; 11139381Seric else 11149381Seric StatFile = newstr(val); 11158256Seric break; 11168256Seric 11178265Seric case 's': /* be super safe, even if expensive */ 11189381Seric SuperSafe = atobool(val); 11198256Seric break; 11208256Seric 11218256Seric case 'T': /* queue timeout */ 11229381Seric TimeOut = convtime(val); 112354967Seric break; 11248256Seric 11258265Seric case 't': /* time zone name */ 112652106Seric TimeZoneSpec = newstr(val); 11278265Seric break; 11288265Seric 112950556Seric case 'U': /* location of user database */ 113051360Seric UdbSpec = newstr(val); 113150556Seric break; 113250556Seric 11338256Seric case 'u': /* set default uid */ 113417474Seric DefUid = atoi(val); 113540973Sbostic setdefuser(); 11368256Seric break; 11378256Seric 11388269Seric case 'v': /* run in verbose mode */ 11399381Seric Verbose = atobool(val); 11408256Seric break; 11418256Seric 114251216Seric case 'w': /* we don't have wildcard MX records */ 114351216Seric NoWildcardMX = atobool(val); 114450537Seric break; 114550537Seric 114614879Seric case 'x': /* load avg at which to auto-queue msgs */ 114714879Seric QueueLA = atoi(val); 114814879Seric break; 114914879Seric 115014879Seric case 'X': /* load avg at which to auto-reject connections */ 115114879Seric RefuseLA = atoi(val); 115214879Seric break; 115314879Seric 115424981Seric case 'y': /* work recipient factor */ 115524981Seric WkRecipFact = atoi(val); 115624981Seric break; 115724981Seric 115824981Seric case 'Y': /* fork jobs during queue runs */ 115924952Seric ForkQueueRuns = atobool(val); 116024952Seric break; 116124952Seric 116224981Seric case 'z': /* work message class factor */ 116324981Seric WkClassFact = atoi(val); 116424981Seric break; 116524981Seric 116624981Seric case 'Z': /* work time factor */ 116724981Seric WkTimeFact = atoi(val); 116824981Seric break; 116924981Seric 11708256Seric default: 11718256Seric break; 11728256Seric } 117316878Seric if (sticky) 117416878Seric setbitn(opt, StickyOpt); 11759188Seric return; 11768256Seric } 117710687Seric /* 117810687Seric ** SETCLASS -- set a word into a class 117910687Seric ** 118010687Seric ** Parameters: 118110687Seric ** class -- the class to put the word in. 118210687Seric ** word -- the word to enter 118310687Seric ** 118410687Seric ** Returns: 118510687Seric ** none. 118610687Seric ** 118710687Seric ** Side Effects: 118810687Seric ** puts the word into the symbol table. 118910687Seric */ 119010687Seric 119110687Seric setclass(class, word) 119210687Seric int class; 119310687Seric char *word; 119410687Seric { 119510687Seric register STAB *s; 119610687Seric 119710687Seric s = stab(word, ST_CLASS, ST_ENTER); 119810687Seric setbitn(class, s->s_class); 119910687Seric } 120053654Seric /* 120153654Seric ** MAKEMAPENTRY -- create a map entry 120253654Seric ** 120353654Seric ** Parameters: 120453654Seric ** line -- the config file line 120553654Seric ** 120653654Seric ** Returns: 120753654Seric ** TRUE if it successfully entered the map entry. 120853654Seric ** FALSE otherwise (usually syntax error). 120953654Seric ** 121053654Seric ** Side Effects: 121153654Seric ** Enters the map into the dictionary. 121253654Seric */ 121353654Seric 121453654Seric void 121553654Seric makemapentry(line) 121653654Seric char *line; 121753654Seric { 121853654Seric register char *p; 121953654Seric char *mapname; 122053654Seric char *classname; 122153654Seric register STAB *map; 122253654Seric STAB *class; 122353654Seric 122453654Seric for (p = line; isspace(*p); p++) 122553654Seric continue; 122653654Seric if (!isalnum(*p)) 122753654Seric { 122853654Seric syserr("readcf: config K line: no map name"); 122953654Seric return; 123053654Seric } 123153654Seric 123253654Seric mapname = p; 123353654Seric while (isalnum(*++p)) 123453654Seric continue; 123553654Seric if (*p != '\0') 123653654Seric *p++ = '\0'; 123753654Seric while (isspace(*p)) 123853654Seric p++; 123953654Seric if (!isalnum(*p)) 124053654Seric { 124153654Seric syserr("readcf: config K line, map %s: no map class", mapname); 124253654Seric return; 124353654Seric } 124453654Seric classname = p; 124553654Seric while (isalnum(*++p)) 124653654Seric continue; 124753654Seric if (*p != '\0') 124853654Seric *p++ = '\0'; 124953654Seric while (isspace(*p)) 125053654Seric p++; 125153654Seric 125253654Seric /* look up the class */ 125353654Seric class = stab(classname, ST_MAPCLASS, ST_FIND); 125453654Seric if (class == NULL) 125553654Seric { 125653654Seric syserr("readcf: map %s: class %s not available", mapname, classname); 125753654Seric return; 125853654Seric } 125953654Seric 126053654Seric /* enter the map */ 126153654Seric map = stab(mapname, ST_MAP, ST_ENTER); 126253654Seric map->s_map.map_class = &class->s_mapclass; 126353654Seric 126456823Seric if ((*class->s_mapclass.map_init)(&map->s_map, mapname, p)) 126553654Seric map->s_map.map_flags |= MF_VALID; 126653654Seric } 1267