|
|
|
/* Copyright (c) 2009 Open Information Security Foundation */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* This file provides a basic configuration system for the IDPS
|
|
|
|
* engine.
|
|
|
|
*
|
|
|
|
* NOTE: Setting values should only be done from one thread during
|
|
|
|
* engine initialization. Multiple threads should be able access read
|
|
|
|
* configuration data. Allowing run time changes to the configuration
|
|
|
|
* will require some locks.
|
|
|
|
*
|
|
|
|
* \author Endace Technology Limited
|
|
|
|
*
|
|
|
|
* \todo Consider using HashListTable to allow easy dumping of all data.
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include "eidps-common.h"
|
|
|
|
#include "conf.h"
|
|
|
|
#include "util-hash.h"
|
|
|
|
#include "util-unittest.h"
|
|
|
|
#include "util-debug.h"
|
|
|
|
|
|
|
|
#define CONF_HASH_TBL_SIZE 1024
|
|
|
|
|
|
|
|
static HashTable *conf_hash = NULL;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Structure of a configuration parameter.
|
|
|
|
*/
|
|
|
|
typedef struct ConfNode_ {
|
|
|
|
char *name;
|
|
|
|
char *val;
|
|
|
|
|
|
|
|
int allow_override;
|
|
|
|
} ConfNode;
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Function to generate the hash of a configuration value.
|
|
|
|
*
|
|
|
|
* This is a callback function provided to HashTable for creating the
|
|
|
|
* hash key. Its a simple wrapper around the generic hash function
|
|
|
|
* the passes on the configuration parameter name.
|
|
|
|
*
|
|
|
|
* \retval The hash ID of the configuration parameters name.
|
|
|
|
*/
|
|
|
|
static uint32_t
|
|
|
|
ConfHashFunc(HashTable *ht, void *data, uint16_t len)
|
|
|
|
{
|
|
|
|
ConfNode *cn = (ConfNode *)data;
|
|
|
|
uint32_t hash;
|
|
|
|
|
|
|
|
hash = HashTableGenericHash(ht, cn->name, strlen(cn->name));
|
|
|
|
SCLogDebug("%s -> %" PRIu32 "", cn->name, hash);
|
|
|
|
return hash;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Function to compare 2 hash nodes.
|
|
|
|
*
|
|
|
|
* This is a callback function provided to the HashTable for comparing
|
|
|
|
* 2 nodes.
|
|
|
|
*
|
|
|
|
* \retval 1 if equivalant otherwise 0.
|
|
|
|
*/
|
|
|
|
static char
|
|
|
|
ConfHashComp(void *a, uint16_t a_len, void *b, uint16_t b_len)
|
|
|
|
{
|
|
|
|
ConfNode *ca = (ConfNode *)a;
|
|
|
|
ConfNode *cb = (ConfNode *)b;
|
|
|
|
|
|
|
|
if (strcmp(ca->name, cb->name) == 0)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Callback function to free a hash node.
|
|
|
|
*/
|
|
|
|
static void ConfHashFree(void *data)
|
|
|
|
{
|
|
|
|
ConfNode *cn = (ConfNode *)data;
|
|
|
|
|
|
|
|
SCLogDebug("freeing configuration parameter '%s'", cn->name);
|
|
|
|
free(cn->name);
|
|
|
|
free(cn->val);
|
|
|
|
free(cn);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Initialize the configuration system.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ConfInit(void)
|
|
|
|
{
|
|
|
|
/* Prevent double initialization. */
|
|
|
|
if (conf_hash != NULL) {
|
|
|
|
SCLogDebug("already initialized");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
conf_hash = HashTableInit(CONF_HASH_TBL_SIZE, ConfHashFunc, ConfHashComp,
|
|
|
|
ConfHashFree);
|
|
|
|
if (conf_hash == NULL) {
|
|
|
|
fprintf(stderr,
|
|
|
|
"ERROR: Failed to allocate memory for configuration, aborting.\n");
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
SCLogDebug("configuration module initialized");
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Set a configuration value.
|
|
|
|
*
|
|
|
|
* \param name The name of the configuration parameter to set.
|
|
|
|
* \param val The value of the configuration parameter.
|
|
|
|
* \param allow_override Can a subsequent set override this value.
|
|
|
|
*
|
|
|
|
* \retval 1 if the value was set otherwise 0.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ConfSet(char *name, char *val, int allow_override)
|
|
|
|
{
|
|
|
|
ConfNode lookup_key, *conf_node;
|
|
|
|
|
|
|
|
lookup_key.name = name;
|
|
|
|
conf_node = HashTableLookup(conf_hash, &lookup_key, sizeof(lookup_key));
|
|
|
|
if (conf_node != NULL) {
|
|
|
|
if (!conf_node->allow_override) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
HashTableRemove(conf_hash, conf_node, sizeof(*conf_node));
|
|
|
|
}
|
|
|
|
|
|
|
|
conf_node = calloc(1, sizeof(*conf_node));
|
|
|
|
if (conf_node == NULL) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
conf_node->name = strdup(name);
|
|
|
|
conf_node->val = strdup(val);
|
|
|
|
conf_node->allow_override = allow_override;
|
|
|
|
|
|
|
|
if (HashTableAdd(conf_hash, conf_node, sizeof(*conf_node)) != 0) {
|
|
|
|
fprintf(stderr, "ERROR: Failed to set configuration parameter %s\n",
|
|
|
|
name);
|
|
|
|
exit(1);
|
|
|
|
}
|
|
|
|
SCLogDebug("configuration parameter '%s' set", name);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Retrieve a configuration value.
|
|
|
|
*
|
|
|
|
* \param name Name of configuration parameter to get.
|
|
|
|
* \param vptr Pointer that will be set to the configuration value parameter.
|
|
|
|
* Note that this is just a reference to the actual value, not a copy.
|
|
|
|
*
|
|
|
|
* \retval 1 will be returned if the name is found, otherwise 0 will
|
|
|
|
* be returned.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ConfGet(char *name, char **vptr)
|
|
|
|
{
|
|
|
|
ConfNode lookup_key;
|
|
|
|
ConfNode *conf_node;
|
|
|
|
|
|
|
|
lookup_key.name = name;
|
|
|
|
|
|
|
|
conf_node = HashTableLookup(conf_hash, &lookup_key, sizeof(lookup_key));
|
|
|
|
if (conf_node == NULL) {
|
|
|
|
SCLogDebug("failed to lookup configuration parameter '%s'", name);
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
*vptr = conf_node->val;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Retrieve a configuration value as an integer.
|
|
|
|
*
|
|
|
|
* \param name Name of configuration parameter to get.
|
|
|
|
* \param val Pointer to an intmax_t that will be set the
|
|
|
|
* configuration value.
|
|
|
|
*
|
|
|
|
* \retval 1 will be returned if the name is found and was properly
|
|
|
|
* converted to an interger, otherwise 0 will be returned.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ConfGetInt(char *name, intmax_t *val)
|
|
|
|
{
|
|
|
|
char *strval;
|
|
|
|
intmax_t tmpint;
|
|
|
|
char *endptr;
|
|
|
|
|
|
|
|
if (ConfGet(name, &strval) == 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
errno = 0;
|
|
|
|
tmpint = strtoimax(strval, &endptr, 0);
|
|
|
|
if (strval[0] == '\0' || *endptr != '\0')
|
|
|
|
return 0;
|
|
|
|
if (errno == ERANGE && (tmpint == INTMAX_MAX || tmpint == INTMAX_MIN))
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
*val = tmpint;
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Retrieve a configuration value as an boolen.
|
|
|
|
*
|
|
|
|
* \param name Name of configuration parameter to get.
|
|
|
|
* \param val Pointer to an int that will be set to 1 for true, or 0
|
|
|
|
* for false.
|
|
|
|
*
|
|
|
|
* \retval 1 will be returned if the name is found and was properly
|
|
|
|
* converted to a boolean, otherwise 0 will be returned.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ConfGetBool(char *name, int *val)
|
|
|
|
{
|
|
|
|
char *strval;
|
|
|
|
char *trues[] = {"1", "yes", "true", "on"};
|
|
|
|
int i;
|
|
|
|
|
|
|
|
*val = 0;
|
|
|
|
if (ConfGet(name, &strval) != 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(trues) / sizeof(trues[0]); i++) {
|
|
|
|
if (strcasecmp(strval, trues[i]) == 0) {
|
|
|
|
*val = 1;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Remove a configuration parameter from the configuration db.
|
|
|
|
*
|
|
|
|
* \param name The name of the configuration parameter to remove.
|
|
|
|
*
|
|
|
|
* \retval Returns 1 if the parameter was removed, otherwise 0 is returned
|
|
|
|
* most likely indicating the parameter was not set.
|
|
|
|
*/
|
|
|
|
int
|
|
|
|
ConfRemove(char *name)
|
|
|
|
{
|
|
|
|
ConfNode cn;
|
|
|
|
|
|
|
|
cn.name = name;
|
|
|
|
if (HashTableRemove(conf_hash, &cn, sizeof(cn)) == 0)
|
|
|
|
return 1;
|
|
|
|
else
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* \brief Dump configuration to stdout.
|
|
|
|
*/
|
|
|
|
void
|
|
|
|
ConfDump(void)
|
|
|
|
{
|
|
|
|
HashTableBucket *b;
|
|
|
|
ConfNode *cn;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < conf_hash->array_size; i++) {
|
|
|
|
if (conf_hash->array[i] != NULL) {
|
|
|
|
b = (HashTableBucket *)conf_hash->array[i];
|
|
|
|
while (b != NULL) {
|
|
|
|
cn = (ConfNode *)b->data;
|
|
|
|
printf("%s=%s\n", cn->name, cn->val);
|
|
|
|
b = b->next;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#ifdef UNITTESTS
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Lookup a non-existant value.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ConfTestGetNonExistant(void)
|
|
|
|
{
|
|
|
|
char name[] = "non-existant-value";
|
|
|
|
char *value;
|
|
|
|
|
|
|
|
return !ConfGet(name, &value);
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Set then lookup a value.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ConfTestSetAndGet(void)
|
|
|
|
{
|
|
|
|
char name[] = "some-name";
|
|
|
|
char value[] = "some-value";
|
|
|
|
char *value0;
|
|
|
|
|
|
|
|
if (ConfSet(name, value, 1) != 1)
|
|
|
|
return 0;
|
|
|
|
if (ConfGet(name, &value0) != 1)
|
|
|
|
return 0;
|
|
|
|
if (strcmp(value, value0) != 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
/* Cleanup. */
|
|
|
|
ConfRemove(name);
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that overriding a value is allowed provided allow_override is
|
|
|
|
* true and that the config parameter gets the new value.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ConfTestOverrideValue1(void)
|
|
|
|
{
|
|
|
|
char name[] = "some-name";
|
|
|
|
char value0[] = "some-value";
|
|
|
|
char value1[] = "new-value";
|
|
|
|
char *val;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (ConfSet(name, value0, 1) != 1)
|
|
|
|
return 0;
|
|
|
|
if (ConfSet(name, value1, 1) != 1)
|
|
|
|
return 0;
|
|
|
|
if (ConfGet(name, &val) != 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rc = !strcmp(val, value1);
|
|
|
|
|
|
|
|
/* Cleanup. */
|
|
|
|
ConfRemove(name);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test that overriding a value is not allowed provided that
|
|
|
|
* allow_override is false and make sure the value was not overrided.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ConfTestOverrideValue2(void)
|
|
|
|
{
|
|
|
|
char name[] = "some-name";
|
|
|
|
char value0[] = "some-value";
|
|
|
|
char value1[] = "new-value";
|
|
|
|
char *val;
|
|
|
|
int rc;
|
|
|
|
|
|
|
|
if (ConfSet(name, value0, 0) != 1)
|
|
|
|
return 0;
|
|
|
|
if (ConfSet(name, value1, 1) != 0)
|
|
|
|
return 0;
|
|
|
|
if (ConfGet(name, &val) != 1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
rc = !strcmp(val, value0);
|
|
|
|
|
|
|
|
/* Cleanup. */
|
|
|
|
ConfRemove(name);
|
|
|
|
|
|
|
|
return rc;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test retrieving an integer value from the configuration db.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ConfTestGetInt(void)
|
|
|
|
{
|
|
|
|
char name[] = "some-int";
|
|
|
|
intmax_t val;
|
|
|
|
|
|
|
|
if (ConfSet(name, "0", 1) != 1)
|
|
|
|
return 0;
|
|
|
|
if (ConfGetInt(name, &val) != 1)
|
|
|
|
return 0;
|
|
|
|
if (val != 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (ConfSet(name, "-1", 1) != 1)
|
|
|
|
return 0;
|
|
|
|
if (ConfGetInt(name, &val) != 1)
|
|
|
|
return 0;
|
|
|
|
if (val != -1)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (ConfSet(name, "0xffff", 1) != 1)
|
|
|
|
return 0;
|
|
|
|
if (ConfGetInt(name, &val) != 1)
|
|
|
|
return 0;
|
|
|
|
if (val != 0xffff)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
if (ConfSet(name, "not-an-int", 1) != 1)
|
|
|
|
return 0;
|
|
|
|
if (ConfGetInt(name, &val) != 0)
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Test retrieving a boolean value from the configuration db.
|
|
|
|
*/
|
|
|
|
static int
|
|
|
|
ConfTestGetBool(void)
|
|
|
|
{
|
|
|
|
char name[] = "some-bool";
|
|
|
|
char *trues[] = {
|
|
|
|
"1",
|
|
|
|
"on", "ON",
|
|
|
|
"yes", "YeS",
|
|
|
|
"true", "TRUE",
|
|
|
|
};
|
|
|
|
char *falses[] = {
|
|
|
|
"0",
|
|
|
|
"something",
|
|
|
|
"off", "OFF",
|
|
|
|
"false", "FalSE",
|
|
|
|
"no", "NO",
|
|
|
|
};
|
|
|
|
int val;
|
|
|
|
int i;
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(trues) / sizeof(trues[0]); i++) {
|
|
|
|
if (ConfSet(name, trues[i], 1) != 1)
|
|
|
|
return 0;
|
|
|
|
if (ConfGetBool(name, &val) != 1)
|
|
|
|
return 0;
|
|
|
|
if (val != 1)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
for (i = 0; i < sizeof(falses) / sizeof(falses[0]); i++) {
|
|
|
|
if (ConfSet(name, falses[i], 1) != 1)
|
|
|
|
return 0;
|
|
|
|
if (ConfGetBool(name, &val) != 1)
|
|
|
|
return 0;
|
|
|
|
if (val != 0)
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
|
|
|
void
|
|
|
|
ConfRegisterTests(void)
|
|
|
|
{
|
|
|
|
UtRegisterTest("ConfTestGetNonExistant", ConfTestGetNonExistant, 1);
|
|
|
|
UtRegisterTest("ConfTestSetAndGet", ConfTestSetAndGet, 1);
|
|
|
|
UtRegisterTest("ConfTestOverrideValue1", ConfTestOverrideValue1, 1);
|
|
|
|
UtRegisterTest("ConfTestOverrideValue2", ConfTestOverrideValue2, 1);
|
|
|
|
UtRegisterTest("ConfTestGetInt", ConfTestGetInt, 1);
|
|
|
|
UtRegisterTest("ConfTestGetBool", ConfTestGetBool, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
#endif /* UNITTESTS */
|