You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/util-classification-config.c

840 lines
25 KiB
C

/* Copyright (C) 2007-2010 Open Information Security Foundation
*
* You can copy, redistribute or modify this Program under the terms of
* the GNU General Public License version 2 as published by the Free
* Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* version 2 along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
* 02110-1301, USA.
*/
/**
* \file
*
* \author Anoop Saldanha <anoopsaldanha@gmail.com>
*
* Used for parsing a classification.config file
*/
#include "suricata-common.h"
#include "detect.h"
#include "detect-engine.h"
#include "util-hash.h"
#include "conf.h"
#include "util-classification-config.h"
#include "util-unittest.h"
#include "util-error.h"
#include "util-debug.h"
#include "util-fmemopen.h"
/* Regex to parse the classtype argument from a Signature. The first substring
* holds the classtype name, the second substring holds the classtype the
* classtype description, and the third argument holds the priority */
#define DETECT_CLASSCONFIG_REGEX "^\\s*config\\s*classification\\s*:\\s*([a-zA-Z][a-zA-Z0-9-_]*)\\s*,\\s*(.+)\\s*,\\s*(\\d+)\\s*$"
/* Default path for the classification.config file */
#if defined OS_WIN32 || defined __CYGWIN__
#define SC_CLASS_CONF_DEF_CONF_FILEPATH CONFIG_DIR "\\\\classification.config"
#else
#define SC_CLASS_CONF_DEF_CONF_FILEPATH CONFIG_DIR "/classification.config"
#endif
static pcre *regex = NULL;
static pcre_extra *regex_study = NULL;
uint32_t SCClassConfClasstypeHashFunc(HashTable *ht, void *data, uint16_t datalen);
char SCClassConfClasstypeHashCompareFunc(void *data1, uint16_t datalen1,
void *data2, uint16_t datalen2);
void SCClassConfClasstypeHashFree(void *ch);
static const char *SCClassConfGetConfFilename(const DetectEngineCtx *de_ctx);
void SCClassConfInit(void)
{
const char *eb = NULL;
int eo;
int opts = 0;
regex = pcre_compile(DETECT_CLASSCONFIG_REGEX, opts, &eb, &eo, NULL);
if (regex == NULL) {
SCLogDebug("Compile of \"%s\" failed at offset %" PRId32 ": %s",
DETECT_CLASSCONFIG_REGEX, eo, eb);
return;
}
regex_study = pcre_study(regex, 0, &eb);
if (eb != NULL) {
pcre_free(regex);
regex = NULL;
SCLogDebug("pcre study failed: %s", eb);
return;
}
return;
}
void SCClassConfDeinit(void)
{
if (regex != NULL) {
pcre_free(regex);
regex = NULL;
}
if (regex_study != NULL) {
pcre_free(regex_study);
regex_study = NULL;
}
}
/**
* \brief Inits the context to be used by the Classification Config parsing API.
*
* This function initializes the hash table to be used by the Detection
* Engine Context to hold the data from the classification.config file,
* obtains the file desc to parse the classification.config file, and
* inits the regex used to parse the lines from classification.config
* file.
*
* \param de_ctx Pointer to the Detection Engine Context.
*
* \retval fp NULL on error
*/
static FILE *SCClassConfInitContextAndLocalResources(DetectEngineCtx *de_ctx, FILE *fd)
{
const char *filename = NULL;
/* init the hash table to be used by the classification config Classtypes */
de_ctx->class_conf_ht = HashTableInit(128, SCClassConfClasstypeHashFunc,
SCClassConfClasstypeHashCompareFunc,
SCClassConfClasstypeHashFree);
if (de_ctx->class_conf_ht == NULL) {
SCLogError(SC_ERR_HASH_TABLE_INIT, "Error initializing the hash "
"table");
goto error;
}
/* if it is not NULL, use the file descriptor. The hack so that we can
* avoid using a dummy classification file for testing purposes and
* instead use an input stream against a buffer containing the
* classification strings */
if (fd == NULL) {
filename = SCClassConfGetConfFilename(de_ctx);
if ( (fd = fopen(filename, "r")) == NULL) {
#ifdef UNITTESTS
if (RunmodeIsUnittests())
goto error; // silently fail
#endif
SCLogError(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, strerror(errno));
goto error;
}
}
return fd;
error:
if (de_ctx->class_conf_ht != NULL) {
HashTableFree(de_ctx->class_conf_ht);
de_ctx->class_conf_ht = NULL;
}
if (fd != NULL) {
fclose(fd);
fd = NULL;
}
return NULL;
}
/**
* \brief Returns the path for the Classification Config file. We check if we
* can retrieve the path from the yaml conf file. If it is not present,
* return the default path for the classification file which is
* "./classification.config".
*
* \retval log_filename Pointer to a string containing the path for the
* Classification Config file.
*/
static const char *SCClassConfGetConfFilename(const DetectEngineCtx *de_ctx)
{
const char *log_filename = NULL;
if (de_ctx != NULL && strlen(de_ctx->config_prefix) > 0) {
char config_value[256];
snprintf(config_value, sizeof(config_value),
"%s.classification-file", de_ctx->config_prefix);
/* try loading prefix setting, fall back to global if that
* fails. */
if (ConfGet(config_value, &log_filename) != 1) {
if (ConfGet("classification-file", &log_filename) != 1) {
log_filename = (char *)SC_CLASS_CONF_DEF_CONF_FILEPATH;
}
}
} else {
if (ConfGet("classification-file", &log_filename) != 1) {
log_filename = (char *)SC_CLASS_CONF_DEF_CONF_FILEPATH;
}
}
return log_filename;
}
/**
* \brief Releases resources used by the Classification Config API.
*/
static void SCClassConfDeInitLocalResources(DetectEngineCtx *de_ctx, FILE *fd)
{
if (fd != NULL) {
fclose(fd);
fd = NULL;
}
}
/**
* \brief Releases resources used by the Classification Config API.
*/
void SCClassConfDeInitContext(DetectEngineCtx *de_ctx)
{
if (de_ctx->class_conf_ht != NULL)
HashTableFree(de_ctx->class_conf_ht);
de_ctx->class_conf_ht = NULL;
return;
}
/**
* \brief Converts a string to lowercase.
*
* \param str Pointer to the string to be converted.
*/
static char *SCClassConfStringToLowercase(const char *str)
{
char *new_str = NULL;
char *temp_str = NULL;
if ( (new_str = SCStrdup(str)) == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
return NULL;
}
temp_str = new_str;
while (*temp_str != '\0') {
*temp_str = tolower((unsigned char)*temp_str);
temp_str++;
}
return new_str;
}
/**
* \brief Parses a line from the classification file and adds it to Classtype
* hash table in DetectEngineCtx, i.e. DetectEngineCtx->class_conf_ht.
*
* \param rawstr Pointer to the string to be parsed.
* \param index Relative index of the string to be parsed.
* \param de_ctx Pointer to the Detection Engine Context.
*
* \retval 0 On success.
* \retval -1 On failure.
*/
static int SCClassConfAddClasstype(char *rawstr, uint8_t index, DetectEngineCtx *de_ctx)
{
char ct_name[64];
char ct_desc[512];
char ct_priority_str[16];
int ct_priority = 0;
uint8_t ct_id = index;
SCClassConfClasstype *ct_new = NULL;
SCClassConfClasstype *ct_lookup = NULL;
#define MAX_SUBSTRINGS 30
int ret = 0;
int ov[MAX_SUBSTRINGS];
ret = pcre_exec(regex, regex_study, rawstr, strlen(rawstr), 0, 0, ov, 30);
if (ret < 0) {
SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid Classtype in "
"classification.config file");
goto error;
}
/* retrieve the classtype name */
ret = pcre_copy_substring((char *)rawstr, ov, 30, 1, ct_name, sizeof(ct_name));
if (ret < 0) {
SCLogInfo("pcre_copy_substring() failed");
goto error;
}
/* retrieve the classtype description */
ret = pcre_copy_substring((char *)rawstr, ov, 30, 2, ct_desc, sizeof(ct_desc));
if (ret < 0) {
SCLogInfo("pcre_copy_substring() failed");
goto error;
}
/* retrieve the classtype priority */
ret = pcre_copy_substring((char *)rawstr, ov, 30, 3, ct_priority_str, sizeof(ct_priority_str));
if (ret < 0) {
SCLogInfo("pcre_copy_substring() failed");
goto error;
}
if (strlen(ct_priority_str) == 0) {
goto error;
}
ct_priority = atoi(ct_priority_str);
/* Create a new instance of the parsed Classtype string */
ct_new = SCClassConfAllocClasstype(ct_id, ct_name, ct_desc, ct_priority);
if (ct_new == NULL)
goto error;
/* Check if the Classtype is present in the HashTable. In case it's present
* ignore it, as it is a duplicate. If not present, add it to the table */
ct_lookup = HashTableLookup(de_ctx->class_conf_ht, ct_new, 0);
if (ct_lookup == NULL) {
if (HashTableAdd(de_ctx->class_conf_ht, ct_new, 0) < 0)
SCLogDebug("HashTable Add failed");
} else {
SCLogDebug("Duplicate classtype found inside classification.config");
if (ct_new->classtype_desc) SCFree(ct_new->classtype_desc);
if (ct_new->classtype) SCFree(ct_new->classtype);
SCFree(ct_new);
}
return 0;
error:
return -1;
}
/**
* \brief Checks if a string is a comment or a blank line.
*
* Comments lines are lines of the following format -
* "# This is a comment string" or
* " # This is a comment string".
*
* \param line String that has to be checked
*
* \retval 1 On the argument string being a comment or blank line
* \retval 0 Otherwise
*/
static int SCClassConfIsLineBlankOrComment(char *line)
{
while (*line != '\0') {
/* we have a comment */
if (*line == '#')
return 1;
/* this line is neither a comment line, nor a blank line */
if (!isspace((unsigned char)*line))
return 0;
line++;
}
/* we have a blank line */
return 1;
}
/**
* \brief Parses the Classification Config file and updates the
* DetectionEngineCtx->class_conf_ht with the Classtype information.
*
* \param de_ctx Pointer to the Detection Engine Context.
*/
static void SCClassConfParseFile(DetectEngineCtx *de_ctx, FILE *fd)
{
char line[1024];
uint8_t i = 1;
while (fgets(line, sizeof(line), fd) != NULL) {
if (SCClassConfIsLineBlankOrComment(line))
continue;
SCClassConfAddClasstype(line, i, de_ctx);
i++;
}
#ifdef UNITTESTS
SCLogInfo("Added \"%d\" classification types from the classification file",
de_ctx->class_conf_ht->count);
#endif
return;
}
/**
* \brief Returns a new SCClassConfClasstype instance. The classtype string
* is converted into lowercase, before being assigned to the instance.
*
* \param classtype Pointer to the classification type.
* \param classtype_desc Pointer to the classification type description.
* \param priority Holds the priority for the classification type.
*
* \retval ct Pointer to the new instance of SCClassConfClasstype on success;
* NULL on failure.
*/
SCClassConfClasstype *SCClassConfAllocClasstype(uint8_t classtype_id,
const char *classtype,
const char *classtype_desc,
int priority)
{
SCClassConfClasstype *ct = NULL;
if (classtype == NULL)
return NULL;
if ( (ct = SCMalloc(sizeof(SCClassConfClasstype))) == NULL)
return NULL;
memset(ct, 0, sizeof(SCClassConfClasstype));
if ( (ct->classtype = SCClassConfStringToLowercase(classtype)) == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
SCClassConfDeAllocClasstype(ct);
return NULL;
}
if (classtype_desc != NULL &&
(ct->classtype_desc = SCStrdup(classtype_desc)) == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
SCClassConfDeAllocClasstype(ct);
return NULL;
}
ct->classtype_id = classtype_id;
ct->priority = priority;
return ct;
}
/**
* \brief Frees a SCClassConfClasstype instance
*
* \param Pointer to the SCClassConfClasstype instance that has to be freed
*/
void SCClassConfDeAllocClasstype(SCClassConfClasstype *ct)
{
if (ct != NULL) {
if (ct->classtype != NULL)
SCFree(ct->classtype);
if (ct->classtype_desc != NULL)
SCFree(ct->classtype_desc);
SCFree(ct);
}
return;
}
/**
* \brief Hashing function to be used to hash the Classtype name. Would be
* supplied as an argument to the HashTableInit function for
* DetectEngineCtx->class_conf_ht.
*
* \param ht Pointer to the HashTable.
* \param data Pointer to the data to be hashed. In this case, the data
* would be a pointer to a SCClassConfClasstype instance.
* \param datalen Not used by this function.
*/
uint32_t SCClassConfClasstypeHashFunc(HashTable *ht, void *data, uint16_t datalen)
{
SCClassConfClasstype *ct = (SCClassConfClasstype *)data;
uint32_t hash = 0;
int i = 0;
int len = strlen(ct->classtype);
for (i = 0; i < len; i++)
hash += tolower((unsigned char)(ct->classtype)[i]);
hash = hash % ht->array_size;
return hash;
}
/**
* \brief Used to compare two Classtypes that have been stored in the HashTable.
* This function is supplied as an argument to the HashTableInit function
* for DetectionEngineCtx->class_conf_ct.
*
* \param data1 Pointer to the first SCClassConfClasstype to be compared.
* \param len1 Not used by this function.
* \param data2 Pointer to the second SCClassConfClasstype to be compared.
* \param len2 Not used by this function.
*
* \retval 1 On data1 and data2 being equal.
* \retval 0 On data1 and data2 not being equal.
*/
char SCClassConfClasstypeHashCompareFunc(void *data1, uint16_t datalen1,
void *data2, uint16_t datalen2)
{
SCClassConfClasstype *ct1 = (SCClassConfClasstype *)data1;
SCClassConfClasstype *ct2 = (SCClassConfClasstype *)data2;
int len1 = 0;
int len2 = 0;
if (ct1 == NULL || ct2 == NULL)
return 0;
if (ct1->classtype == NULL || ct2->classtype == NULL)
return 0;
len1 = strlen(ct1->classtype);
len2 = strlen(ct2->classtype);
if (len1 == len2 && memcmp(ct1->classtype, ct2->classtype, len1) == 0) {
SCLogDebug("Match found inside Classification-Config hash function");
return 1;
}
return 0;
}
/**
* \brief Used to free the Classification Config Hash Data that was stored in
* DetectEngineCtx->class_conf_ht Hashtable.
*
* \param ch Pointer to the data that has to be freed.
*/
void SCClassConfClasstypeHashFree(void *ch)
{
SCClassConfDeAllocClasstype(ch);
return;
}
/**
* \brief Loads the Classtype info from the classification.config file.
*
* The classification.config file contains the different classtypes,
* that can be used to label Signatures. Each line of the file should
* have the following format -
* classtype_name, classtype_description, priority
* None of the above parameters should hold a quote inside the file.
*
* \param de_ctx Pointer to the Detection Engine Context that should be updated
* with Classtype information.
*/
void SCClassConfLoadClassficationConfigFile(DetectEngineCtx *de_ctx, FILE *fd)
{
fd = SCClassConfInitContextAndLocalResources(de_ctx, fd);
if (fd == NULL) {
#ifdef UNITTESTS
if (RunmodeIsUnittests() && fd == NULL) {
return;
}
#endif
SCLogError(SC_ERR_OPENING_FILE, "please check the \"classification-file\" "
"option in your suricata.yaml file");
return;
}
SCClassConfParseFile(de_ctx, fd);
SCClassConfDeInitLocalResources(de_ctx, fd);
return;
}
/**
* \brief Gets the classtype from the corresponding hash table stored
* in the Detection Engine Context's class conf ht, given the
* classtype name.
*
* \param ct_name Pointer to the classtype name that has to be looked up.
* \param de_ctx Pointer to the Detection Engine Context.
*
* \retval lookup_ct_info Pointer to the SCClassConfClasstype instance from
* the hash table on success; NULL on failure.
*/
SCClassConfClasstype *SCClassConfGetClasstype(const char *ct_name,
DetectEngineCtx *de_ctx)
{
char name[strlen(ct_name) + 1];
size_t s;
for (s = 0; s < strlen(ct_name); s++)
name[s] = tolower((unsigned char)ct_name[s]);
name[s] = '\0';
SCClassConfClasstype ct_lookup = {0, name, NULL, 0 };
SCClassConfClasstype *lookup_ct_info = HashTableLookup(de_ctx->class_conf_ht,
&ct_lookup, 0);
return lookup_ct_info;
}
/*----------------------------------Unittests---------------------------------*/
#ifdef UNITTESTS
/**
* \brief Creates a dummy classification file, with all valid Classtypes, for
* testing purposes.
*
* \file_path Pointer to the file_path for the dummy classification file.
*/
FILE *SCClassConfGenerateValidDummyClassConfigFD01(void)
{
const char *buffer =
"config classification: nothing-wrong,Nothing Wrong With Us,3\n"
"config classification: unknown,Unknown are we,3\n"
"config classification: bad-unknown,We think it's bad, 2\n";
FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
if (fd == NULL)
SCLogDebug("Error with SCFmemopen() called by Classifiation Config test code");
return fd;
}
/**
* \brief Creates a dummy classification file, with some valid Classtypes and a
* couple of invalid Classtypes, for testing purposes.
*
* \file_path Pointer to the file_path for the dummy classification file.
*/
FILE *SCClassConfGenerateInValidDummyClassConfigFD02(void)
{
const char *buffer =
"config classification: not-suspicious,Not Suspicious Traffic,3\n"
"onfig classification: unknown,Unknown Traffic,3\n"
"config classification: _badunknown,Potentially Bad Traffic, 2\n"
"config classification: bamboola1,Unknown Traffic,3\n"
"config classification: misc-activity,Misc activity,-1\n"
"config classification: policy-violation,Potential Corporate "
"config classification: bamboola,Unknown Traffic,3\n";
FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
if (fd == NULL)
SCLogDebug("Error with SCFmemopen() called by Classifiation Config test code");
return fd;
}
/**
* \brief Creates a dummy classification file, with all invalid Classtypes, for
* testing purposes.
*
* \file_path Pointer to the file_path for the dummy classification file.
*/
FILE *SCClassConfGenerateInValidDummyClassConfigFD03(void)
{
const char *buffer =
"conig classification: not-suspicious,Not Suspicious Traffic,3\n"
"onfig classification: unknown,Unknown Traffic,3\n"
"config classification: _badunknown,Potentially Bad Traffic, 2\n"
"config classification: misc-activity,Misc activity,-1\n";
FILE *fd = SCFmemopen((void *)buffer, strlen(buffer), "r");
if (fd == NULL)
SCLogDebug("Error with SCFmemopen() called by Classifiation Config test code");
return fd;
}
/**
* \test Check that the classification file is loaded and the detection engine
* content class_conf_hash_table loaded with the classtype data.
*/
static int SCClassConfTest01(void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
int result = 0;
if (de_ctx == NULL)
return result;
FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
SCClassConfLoadClassficationConfigFile(de_ctx, fd);
if (de_ctx->class_conf_ht == NULL)
return result;
result = (de_ctx->class_conf_ht->count == 3);
if (result == 0) printf("de_ctx->class_conf_ht->count %u: ", de_ctx->class_conf_ht->count);
DetectEngineCtxFree(de_ctx);
return result;
}
/**
* \test Check that invalid classtypes present in the classification config file
* aren't loaded.
*/
static int SCClassConfTest02(void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
int result = 0;
if (de_ctx == NULL)
return result;
FILE *fd = SCClassConfGenerateInValidDummyClassConfigFD03();
SCClassConfLoadClassficationConfigFile(de_ctx, fd);
if (de_ctx->class_conf_ht == NULL)
return result;
result = (de_ctx->class_conf_ht->count == 0);
DetectEngineCtxFree(de_ctx);
return result;
}
/**
* \test Check that only valid classtypes are loaded into the hash table from
* the classfication.config file.
*/
static int SCClassConfTest03(void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
int result = 0;
if (de_ctx == NULL)
return result;
FILE *fd = SCClassConfGenerateInValidDummyClassConfigFD02();
SCClassConfLoadClassficationConfigFile(de_ctx, fd);
if (de_ctx->class_conf_ht == NULL)
return result;
result = (de_ctx->class_conf_ht->count == 3);
DetectEngineCtxFree(de_ctx);
return result;
}
/**
* \test Check if the classtype info from the classification.config file have
* been loaded into the hash table.
*/
static int SCClassConfTest04(void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
int result = 1;
if (de_ctx == NULL)
return 0;
FILE *fd = SCClassConfGenerateValidDummyClassConfigFD01();
SCClassConfLoadClassficationConfigFile(de_ctx, fd);
if (de_ctx->class_conf_ht == NULL)
return 0;
result = (de_ctx->class_conf_ht->count == 3);
result &= (SCClassConfGetClasstype("unknown", de_ctx) != NULL);
result &= (SCClassConfGetClasstype("unKnoWn", de_ctx) != NULL);
result &= (SCClassConfGetClasstype("bamboo", de_ctx) == NULL);
result &= (SCClassConfGetClasstype("bad-unknown", de_ctx) != NULL);
result &= (SCClassConfGetClasstype("BAD-UNKnOWN", de_ctx) != NULL);
result &= (SCClassConfGetClasstype("bed-unknown", de_ctx) == NULL);
DetectEngineCtxFree(de_ctx);
return result;
}
/**
* \test Check if the classtype info from the invalid classification.config file
* have not been loaded into the hash table, and cross verify to check
* that the hash table contains no classtype data.
*/
static int SCClassConfTest05(void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
int result = 1;
if (de_ctx == NULL)
return 0;
FILE *fd = SCClassConfGenerateInValidDummyClassConfigFD03();
SCClassConfLoadClassficationConfigFile(de_ctx, fd);
if (de_ctx->class_conf_ht == NULL)
return 0;
result = (de_ctx->class_conf_ht->count == 0);
result &= (SCClassConfGetClasstype("unknown", de_ctx) == NULL);
result &= (SCClassConfGetClasstype("unKnoWn", de_ctx) == NULL);
result &= (SCClassConfGetClasstype("bamboo", de_ctx) == NULL);
result &= (SCClassConfGetClasstype("bad-unknown", de_ctx) == NULL);
result &= (SCClassConfGetClasstype("BAD-UNKnOWN", de_ctx) == NULL);
result &= (SCClassConfGetClasstype("bed-unknown", de_ctx) == NULL);
DetectEngineCtxFree(de_ctx);
return result;
}
/**
* \test Check if the classtype info from the classification.config file have
* been loaded into the hash table.
*/
static int SCClassConfTest06(void)
{
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
int result = 1;
if (de_ctx == NULL)
return 0;
FILE *fd = SCClassConfGenerateInValidDummyClassConfigFD02();
SCClassConfLoadClassficationConfigFile(de_ctx, fd);
if (de_ctx->class_conf_ht == NULL)
return 0;
result = (de_ctx->class_conf_ht->count == 3);
result &= (SCClassConfGetClasstype("unknown", de_ctx) == NULL);
result &= (SCClassConfGetClasstype("not-suspicious", de_ctx) != NULL);
result &= (SCClassConfGetClasstype("bamboola1", de_ctx) != NULL);
result &= (SCClassConfGetClasstype("bamboola1", de_ctx) != NULL);
result &= (SCClassConfGetClasstype("BAMBOolA1", de_ctx) != NULL);
result &= (SCClassConfGetClasstype("unkNOwn", de_ctx) == NULL);
DetectEngineCtxFree(de_ctx);
return result;
}
#endif /* UNITTESTS */
/**
* \brief This function registers unit tests for Classification Config API.
*/
void SCClassConfRegisterTests(void)
{
#ifdef UNITTESTS
UtRegisterTest("SCClassConfTest01", SCClassConfTest01);
UtRegisterTest("SCClassConfTest02", SCClassConfTest02);
UtRegisterTest("SCClassConfTest03", SCClassConfTest03);
UtRegisterTest("SCClassConfTest04", SCClassConfTest04);
UtRegisterTest("SCClassConfTest05", SCClassConfTest05);
UtRegisterTest("SCClassConfTest06", SCClassConfTest06);
#endif /* UNITTESTS */
}