readconf.c #7

  • //
  • guest/
  • bryan_costales/
  • readconf.c
  • View
  • Commits
  • Open Download .zip Download (8 KB)
/************************************************************
** $Id: readconf.c,v 0.8 2003/12/29 16:45:38 bcx Exp bcx $
**
** Copyright Bryan Costales and Perforce Software, Inc. 2003
**
** This code is "open source" as defined by version 1.9 of
** the Open Source Definition from:
**
**	http://www.opensource.org/docs/definition.php.
**
** Entry points defined in this file:
**
** setdefaults():
** 	Set the defaults for all global variables.
** readconf():
** 	Opens, reads, and closes the configuration file.
*************************************************************/

# define EXTERN
# include "slow.h"

void
setdefaults(void)
{
	/*
	 * Global variables, not resetable by the config file.
	 */
	LogPrefix		 = "";

	/*
	 * Global variables, can be reset by the config file.
	 */
	ListenPort		 = LISTENPORT;
	DatabaseLoc		 = DBFILE;
	MaxHeaderRcpts		 = MAX_HEADER_RCPTS;
	MaxRcptsPerEnvelope	 = MAX_RCPTS_PER_ENV;
	CheckMessageId		 = TRUE;
	CheckFromHeader		 = TRUE;
	CheckSenderHost		 = FALSE;
	LookBackOne		 = FALSE;
	AllowWhiteListIPs	 = TRUE;
	RBLList			 = DEFAULT_RBL_LIST;
	MilterAbort		 = TRUE;
	AdvanceWrite		 = TRUE;
	CheckForged		 = TRUE;
	HoneyFile		 = NULL;
	HoneyHosts		 = NULL;
	MxHosts			 = NULL;
	LocalHostName		 = NULL;
	XTrackHeader		 = X_TRACK_HEADER;
	DoLog			 = TRUE;
	LogEvents		 = TRUE;
	AddIPAliases		 = FALSE;
	RunAsUser		 = NULL;
	RunAsGroup		 = NULL;
}

typedef struct {
	char *token; /* the config file keyword */
	int   index; /* for switch() statement */
	bool  set;   /* already set from config file if TRUE */
} CFLINES;

CFLINES Cflines[] = {
# define I_PSOC		1
	{"ListenPort", 			I_PSOC, 0 },

# define I_PRIDBFILE	2
	{"PrimaryDatabaseLoc", 		I_PRIDBFILE, 0 },
# define I_SECDBFILE	3
	{"SecondaryDatabaseLoc", 	I_SECDBFILE, 0 },
# define I_MAXRPERHDR	4
	{"MaxHeaderRcpts", 		I_MAXRPERHDR, 0 },
# define I_MAXRPERE	5
	{"MaxRcptsPerEnvelope", 	I_MAXRPERE, 0 },
# define I_CHK_MID	6
	{"CheckMessageId", 		I_CHK_MID, 0 },
# define I_CHKFROMHDR	7
	{"CheckFromHeader", 		I_CHKFROMHDR, 0 },
# define I_CHKSENDHOST	8
	{"CheckSenderHost", 		I_CHKSENDHOST, 0 },
# define I_ALLOWWHITE	9
	{"AllowWhiteListIPs", 		I_ALLOWWHITE, 0 },
# define I_LOOKBACK	10
	{"LookBackOne", 		I_LOOKBACK, 0 },
# define I_MILABORT	11
	{"MilterAbort", 		I_MILABORT, 0 },
# define I_ADVWRITE	12
	{"AdvanceWrite", 		I_ADVWRITE, 0 },
# define I_FORGED	13
	{"CheckForged", 		I_FORGED, 0 },
# define I_HONEY	14
	{"HoneyFile",	 		I_HONEY, 0 },
# define I_MYNAME	15
	{"LocalHostName", 		I_MYNAME, 0 },
# define I_XHEADER	16
	{"XTrackHeader", 		I_XHEADER, 0 },
# define I_DOLOG	17
	{"DoLog",	 		I_DOLOG, 0 },
# define I_LOGEVENTS	18
	{"LogEvents",	 		I_LOGEVENTS, 0 },
# define I_RBLLIST	19
	{"RBLList",	 		I_RBLLIST, 0 },
# define I_ADDMX	20
	{"AddMXhosts",	 		I_ADDMX, 0 },
# define I_ADDIPALIAS	21
	{"AddIPAliases", 		I_ADDIPALIAS, 0 },
# define I_RUNASUSER	22
	{"RunAsUser", 			I_RUNASUSER, 0 },
# define I_RUNASGROUP	23
	{"RunAsGroup", 			I_RUNASGROUP, 0 },
# define I_DBFILE	24
	{"DatabaseLoc",			I_DBFILE, 0 },
	{ NULL, 			0, 0 },
};

static bool
truefalse(char *tf)
{
	if (tf == NULL)
		return TRUE;
	switch((int)*tf)
	{
	    case 'Y':
	    case 'y':
	    case 'T':
	    case 't':
		return TRUE;
		break;
	}
	return FALSE;
}

void
readconf(char *prog, char *cffile)
{
	FILE *fp;
	char buf[BUFSIZ];
	CFLINES *cl;
	char **t, **p;
	int line = 0;
	extern int errno;

	fp = fopen(cffile, "r");
	if (fp == NULL)
		milterr(Prog, __FILE__, __LINE__, FALSE,
			errno, "Open config file", cffile);

	/* Use file(line) to show errors in the config file */
	set_bit(BUG_SOURCE, Debugbits);

	while (fgets(buf, BUFSIZ, fp) != NULL)
	{
		char *cp, *ep, *xp;
		int ch;

		++line;
		cp = strrchr(buf, '\n');
		if (cp != NULL)
			*cp = '\0';

		for (cp = buf; *cp != '\0'; ++cp)
		{
			if (! isspace((int)*cp))
				break;
		}
		if (*cp == '\0' || *cp == '#')
			continue; /* empty line */
		for (ep = cp; *ep != '\0'; ++ep)
		{
			if (isspace((int)*ep) || *ep == '=')
				break;
		}
		if (*ep == '\0')
			milterr(Prog, cffile, line, FALSE,
				EINVAL, "Missing \"=\" from", buf);
		ch = *ep;
		*ep = '\0';

		for (cl = Cflines; cl->token != NULL; ++cl)
		{
			if (strcasecmp(cl->token, cp) == 0)
				break;
		}
		*ep = ch;
		if (cl->token == NULL)
			milterr(Prog, cffile, line, FALSE,
				EINVAL, "Unrecognized token", cp);

		if ((cl->set)++ != 0)
			milterr(Prog, cffile, line, FALSE,
				EINVAL, "Repeated definition", buf);

		cp = strchr(buf, '=');
		if (cp == NULL)
			milterr(Prog, cffile, line, FALSE,
				EINVAL, "Missing \"=\" from", buf);

		for (++cp; *cp != '\0'; ++cp)
		{
			if (! isspace((int)*cp))
				break;
		}
		
		if (*cp == '\0')
			milterr(Prog, cffile, line, FALSE,
				EINVAL, "Nothing after \"=\" in", buf);
		switch(cl->index)
		{
		    /*
		     * Should be in the form inet:port@host
		     */
		    case I_PSOC:
			ListenPort = try_strdup(__FILE__, __LINE__, cp, FALSE);
			if (ListenPort == NULL)
				milterr(Prog, cffile, line, FALSE,
					errno, "Memory allocation", cp);
			if (strncasecmp(ListenPort, "inet:", 5) != 0)
				milterr(Prog, cffile, line, FALSE,
					EINVAL, cp,
					"Must start with \"inet:\"");
			break;

		    /*
		     * For security these should be full path names.
		     * Should we enforce that?
		     */
		    case I_DBFILE:
			DatabaseLoc = try_strdup(__FILE__, __LINE__, cp, FALSE);
			if (DatabaseLoc == NULL)
				milterr(Prog, cffile, line, FALSE,
					errno, "Memory allocation", buf);
			break;

		    case I_MAXRPERHDR:
			MaxHeaderRcpts = atoi(cp);
			if (MaxHeaderRcpts <= 0)
				milterr(Prog, cffile, line, FALSE,
					EINVAL, "Must be positive", buf);
			break;

		    case I_MAXRPERE:
			MaxRcptsPerEnvelope = atoi(cp);
			if (MaxRcptsPerEnvelope <= 0)
				milterr(Prog, cffile, line, FALSE,
					EINVAL, "Must be positive", buf);
			break;

		    case I_CHK_MID:
			CheckMessageId = truefalse(cp);
			break;

		    case I_CHKFROMHDR:
			CheckFromHeader = truefalse(cp);
			break;

		    case I_CHKSENDHOST:
			CheckSenderHost = truefalse(cp);
			break;

		    case I_LOOKBACK:
			LookBackOne = truefalse(cp);
			break;

		    case I_ALLOWWHITE:
			AllowWhiteListIPs = truefalse(cp);
			break;

		    case I_MILABORT:
			MilterAbort = truefalse(cp);
			break;

		    case I_ADVWRITE:
			AdvanceWrite = truefalse(cp);
			break;

		    case I_FORGED:
			CheckForged = truefalse(cp);
			break;

		    /*
		     * The list of honeypot addresses.
		     */
		    case I_HONEY:
			HoneyFile = try_strdup(__FILE__, __LINE__, cp, FALSE);
			if (HoneyFile == NULL)
				milterr(Prog, cffile, line, FALSE,
					errno, "Memory allocation", buf);
			break;

		    case I_MYNAME:
			LocalHostName = try_strdup(__FILE__, __LINE__, cp, FALSE);
			if (LocalHostName == NULL)
				milterr(Prog, cffile, line, FALSE,
					errno, "Memory allocation", buf);
			break;

		    case I_XHEADER:
			XTrackHeader = try_strdup(__FILE__, __LINE__, cp, FALSE);
			if (XTrackHeader == NULL)
				milterr(Prog, cffile, line, FALSE,
					errno, "Memory allocation", buf);
			if (strchr(XTrackHeader, ':') != NULL)
				milterr(Prog, cffile, line, FALSE,
					EINVAL, "Must not contain a colon", cp);
			break;

		    case I_DOLOG:
			DoLog = truefalse(cp);
			break;

		    case I_LOGEVENTS:
			LogEvents = truefalse(cp);
			break;

		    case I_ADDIPALIAS:
			AddIPAliases = truefalse(cp);
			break;

		    case I_RBLLIST:
			RBLList = mkargv(cp, ',', FALSE);
			if (RBLList == NULL)
				milterr(Prog, cffile, line, FALSE,
					EINVAL, "Could not parse", cp);
			xp = try_strdup(__FILE__, __LINE__, "127.0.0.2", FALSE);
			for (p = RBLList; *p != NULL; ++p)
			{
				if (rbl_lookup(*p, xp, FALSE) == 0)
					continue;
				(void) printf("Warning: lookup of %s at %s should have failed, but did not.\n", xp, *p);
			}
			(void) free(xp);
			break;

		    case I_ADDMX:
			t = mkargv(cp, ',', FALSE);
			if (t == NULL)
				milterr(Prog, cffile, line, FALSE,
					EINVAL, "Could not parse", cp);
			for (p = t; *p != NULL; ++p)
			{
				MxHosts = push_list(*p, MxHosts, FALSE);
			}
			break;

		    case I_RUNASUSER:
			RunAsUser = try_strdup(__FILE__, __LINE__, cp, FALSE);
			if (RunAsUser == NULL)
				milterr(Prog, cffile, line, FALSE,
					errno, "Memory allocation", buf);
			break;

		    case I_RUNASGROUP:
			RunAsGroup = try_strdup(__FILE__, __LINE__, cp, FALSE);
			if (RunAsGroup == NULL)
				milterr(Prog, cffile, line, FALSE,
					errno, "Memory allocation", buf);
			break;
		}
	}
	if (ferror(fp))
		milterr(Prog, __FILE__, __LINE__, FALSE,
			errno, "Read config file", cffile);

	(void) fclose(fp);
	Debugbits ^= BUG_SOURCE;
	return;
}

# Change User Description Committed
#7 4222 bryan_costales Massive rewrite to speed up the database writes.
Using a single database now with duplicate keys
where the keys are the IP numbers. Added a purge
command and removed the garbage command. Fixed
some leaking memory bugs and properly closed the
database in a few places were it was not
properly closed. Updated the docs to reflect
this and bumped both the database version and
release number. Running on a FreeBSD 3.x machine
and a Solaris 9 machine.
#6 4052 bryan_costales Implimented:
    whitelisting
    AddMXHost for MX servers that lie
    Converted to thread safe DNS routines
    garbage collection
    RunAsUser and RunAsGroup for root startups
    rebuild the database
    summarize by IP number
Finished all documentation.
Moved release from alpha to beta
#5 4030 bryan_costales Finished documenting the configuration file
Fixed a race condition and a core dump bug.
Added hooks for whitelisting and IP aliasing
Added support for Berkeley DB 4.2
Converted to htonl() and ntohl()

Known Bugs:
    ip.db cannot be shared over NFS
    IP tracking from MX hosts can fail
    A RunAsUser config option is needed.
#4 3998 bryan_costales Brought the whole distribution up to V0.9
Added a huge abount of documentation.
Added slowedit find
Created startup scripts to launch for testing
Fixed numerous bugs.
Fixed a few portablity issues.
Installed hooks for whitelisting and IP aliases.
#3 3957 bryan_costales Added rbl lookup support and testing for same.
Folded in support for smfi_stop().
Added lots of slowedit commands
Fixed a serious bug in MX lookups.
Added to documentation.
#2 3890 bryan_costales This is the 0.6 release. The following have been
added with the uses indicated:

    Source files: edit.c -- the slowedit functions
              compat.c -- missing system files
    Autoconf:     configure.ac, makefile.am config.h
              aclocal.m4 acinclude.m4 build/
    Documents:    doc/ -- html and man(1) documents
    Testing:      tests/ -- regressive testing
    TODO:          -- revised to show actual progress
#1 3838 bryan_costales This is pre-release 0.5 (rcs numbering) which includes:
    The milter source files and Makefile
    A regressive testing subdirectory with Makefile and /bin/sh scripts.
    A patches subdirectory with a patch file for V8.13 sendmail
All have been compiled and tested on a 64bit Sun Solaris 9 machine.