mirror of https://github.com/OISF/suricata
Support for Classtype keyword and Classification Config file
parent
f0be69dcd0
commit
bc4df59414
@ -1,35 +1,354 @@
|
||||
/* CLASSTYPE part of the detection engine. */
|
||||
/** Copyright (c) 2009 Open Information Security Foundation.
|
||||
* \author Anoop Saldanha <poonaatsoc@gmail.com>
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "decode.h"
|
||||
|
||||
#include "detect.h"
|
||||
#include "detect-parse.h"
|
||||
#include "detect-engine.h"
|
||||
#include "detect-engine-mpm.h"
|
||||
#include "detect-classtype.h"
|
||||
#include "flow-var.h"
|
||||
#include "util-classification-config.h"
|
||||
#include "util-error.h"
|
||||
#include "util-debug.h"
|
||||
#include "util-unittest.h"
|
||||
|
||||
#define DETECT_CLASSTYPE_REGEX "^\\s*([a-zA-Z][a-zA-Z0-9-_]*)\\s*$"
|
||||
|
||||
static pcre *regex = NULL;
|
||||
static pcre_extra *regex_study = NULL;
|
||||
|
||||
int DetectClasstypeSetup(DetectEngineCtx *, Signature *, SigMatch *, char *);
|
||||
void DetectClasstypeRegisterTests(void);
|
||||
|
||||
/**
|
||||
* \brief Registers the handler functions for the "Classtype" keyword.
|
||||
*/
|
||||
void DetectClasstypeRegister(void)
|
||||
{
|
||||
const char *eb = NULL;
|
||||
int eo;
|
||||
int opts = 0;
|
||||
|
||||
int DetectClasstypeSetup (DetectEngineCtx *, Signature *s, SigMatch *m, char *str);
|
||||
SCLogDebug("Registering the Classtype keyword handler");
|
||||
|
||||
void DetectClasstypeRegister (void) {
|
||||
sigmatch_table[DETECT_CLASSTYPE].name = "classtype";
|
||||
sigmatch_table[DETECT_CLASSTYPE].Match = NULL;
|
||||
sigmatch_table[DETECT_CLASSTYPE].Setup = DetectClasstypeSetup;
|
||||
sigmatch_table[DETECT_CLASSTYPE].Free = NULL;
|
||||
sigmatch_table[DETECT_CLASSTYPE].RegisterTests = NULL;
|
||||
sigmatch_table[DETECT_CLASSTYPE].RegisterTests = DetectClasstypeRegisterTests;
|
||||
|
||||
regex = pcre_compile(DETECT_CLASSTYPE_REGEX, opts, &eb, &eo, NULL);
|
||||
if (regex == NULL) {
|
||||
SCLogDebug("Compile of \"%s\" failed at offset %" PRId32 ": %s",
|
||||
DETECT_CLASSTYPE_REGEX, eo, eb);
|
||||
goto end;
|
||||
}
|
||||
|
||||
regex_study = pcre_study(regex, 0, &eb);
|
||||
if (eb != NULL) {
|
||||
SCLogDebug("pcre study failed: %s", eb);
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Parses the raw string supplied with the "Classtype" keyword.
|
||||
*
|
||||
* \param Pointer to the string to be parsed.
|
||||
*
|
||||
* \retval ct_name Pointer to the parsed string on Success; NULL on failure.
|
||||
*/
|
||||
static inline const char *DetectClasstypeParseRawString(char *rawstr)
|
||||
{
|
||||
const char *ct_name = NULL;
|
||||
#define MAX_SUBSTRINGS 30
|
||||
int ret = 0;
|
||||
int ov[MAX_SUBSTRINGS];
|
||||
|
||||
/* get rid of the double quotes if present */
|
||||
if (rawstr[0] == '\"' && rawstr[strlen(rawstr) - 1] == '\"') {
|
||||
if ( (rawstr = strdup(rawstr + 1)) == NULL) {
|
||||
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
rawstr[strlen(rawstr) - 1] = '\0';
|
||||
}
|
||||
|
||||
ret = pcre_exec(regex, regex_study, rawstr, strlen(rawstr), 0, 0, ov, 30);
|
||||
if (ret < 0) {
|
||||
SCLogWarning(SC_ERR_INVALID_SIGNATURE, "Invalid Classtype in Signature");
|
||||
goto end;
|
||||
}
|
||||
|
||||
ret = pcre_get_substring((char *)rawstr, ov, 30, 1, &ct_name);
|
||||
if (ret < 0) {
|
||||
SCLogInfo("pcre_get_substring() failed");
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
return ct_name;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Gets the classtype from the corresponding hash table stored
|
||||
* in the Detection Engine Context, 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.
|
||||
*/
|
||||
static inline SCClassConfClasstype *DetectClasstypeGetClasstypeInfo(const char *ct_name,
|
||||
DetectEngineCtx *de_ctx)
|
||||
{
|
||||
SCClassConfClasstype *ct_info = SCClassConfAllocClasstype(ct_name, NULL,
|
||||
0);
|
||||
SCClassConfClasstype *lookup_ct_info = HashTableLookup(de_ctx->class_conf_ht,
|
||||
ct_info, 0);
|
||||
|
||||
SCClassConfDeAllocClasstype(ct_info);
|
||||
return lookup_ct_info;
|
||||
}
|
||||
|
||||
int DetectClasstypeSetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, char *rawstr)
|
||||
/**
|
||||
* \brief The setup function that would be called when the Signature parsing
|
||||
* module encounters the "Classtype" keyword.
|
||||
*
|
||||
* \param de_ctx Pointer to the Detection Engine Context.
|
||||
* \param s Pointer the current Signature instance that is being parsed.
|
||||
* \param m Pointer to the previous SigMatch.
|
||||
* \param rawstr Pointer to the argument supplied to the classtype keyword.
|
||||
*
|
||||
* \retval 0 On success
|
||||
* \retval -1 On failure
|
||||
*/
|
||||
int DetectClasstypeSetup(DetectEngineCtx *de_ctx, Signature *s, SigMatch *m,
|
||||
char *rawstr)
|
||||
{
|
||||
char *str = rawstr;
|
||||
char dubbed = 0;
|
||||
const char *parsed_ct_name = NULL;
|
||||
SCClassConfClasstype *ct = NULL;
|
||||
|
||||
if ( (parsed_ct_name = DetectClasstypeParseRawString(rawstr)) == NULL) {
|
||||
SCLogDebug("Error parsing classtype argument supplied with the "
|
||||
"classtype keyword");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* strip "'s */
|
||||
if (rawstr[0] == '\"' && rawstr[strlen(rawstr)-1] == '\"') {
|
||||
str = strdup(rawstr+1);
|
||||
str[strlen(rawstr)-2] = '\0';
|
||||
dubbed = 1;
|
||||
ct = DetectClasstypeGetClasstypeInfo(parsed_ct_name, de_ctx);
|
||||
if (ct == NULL) {
|
||||
SCLogDebug("Unknown Classtype: \"%s\". Invalidating the Signature",
|
||||
parsed_ct_name);
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* XXX */
|
||||
/* if we have retrieved the classtype, assign the message to be displayed
|
||||
* for this Signature by fast.log, if a Packet matches this Signature */
|
||||
s->class_msg = ct->classtype_desc;
|
||||
|
||||
/* if a priority keyword has appeared before the classtype, s->prio would
|
||||
* hold a value which is != -1, in which case we don't overwrite the value.
|
||||
* Otherwise, overwrite the value */
|
||||
if (s->prio == -1)
|
||||
s->prio = ct->priority;
|
||||
|
||||
if (dubbed) free(str);
|
||||
return 0;
|
||||
|
||||
error:
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*------------------------------Unittests-------------------------------------*/
|
||||
|
||||
#ifdef UNITTESTS
|
||||
|
||||
/**
|
||||
* \test Check that supplying an invalid classtype in the rule, results in the
|
||||
* rule being invalidated.
|
||||
*/
|
||||
int DetectClasstypeTest01()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
if (de_ctx == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
SCClassConfGenerateValidDummyClassConfigFile01("/var/log/eidps/classification.config");
|
||||
SCClassConfLoadClassficationConfigFile(de_ctx);
|
||||
SCClassConfDeleteDummyClassificationConfigFile("/var/log/eidps/classification.config");
|
||||
|
||||
de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Classtype test\"; "
|
||||
"Classtype:not_available; sid:1;)");
|
||||
if (de_ctx->sig_list == NULL)
|
||||
result = 1;
|
||||
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check that both valid and invalid classtypes in a rule are handled
|
||||
* properly, with rules containing invalid classtypes being rejected
|
||||
* and the ones containing valid classtypes parsed and returned.
|
||||
*/
|
||||
int DetectClasstypeTest02()
|
||||
{
|
||||
int result = 0;
|
||||
Signature *last = NULL;
|
||||
Signature *sig = NULL;
|
||||
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
if (de_ctx == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
SCClassConfGenerateValidDummyClassConfigFile01("/var/log/eidps/classification.config");
|
||||
SCClassConfLoadClassficationConfigFile(de_ctx);
|
||||
SCClassConfDeleteDummyClassificationConfigFile("/var/log/eidps/classification.config");
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Classtype test\"; Classtype:unknown; sid:1;)");
|
||||
de_ctx->sig_list = last = sig;
|
||||
result = (sig != NULL);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Classtype test\"; Classtype:not-there; sid:1;)");
|
||||
last->next = sig;
|
||||
result &= (sig == NULL);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Classtype test\"; Classtype:atteMPted-dos; sid:1;)");
|
||||
last->next = sig;
|
||||
last = sig;
|
||||
result &= (sig != NULL);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Classtype test\"; Classtype:attempted-dos; sid:1;)");
|
||||
last->next = sig;
|
||||
last = sig;
|
||||
result &= (sig != NULL);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Classtype test\"; Classtype:attempted_dos; sid:1;)");
|
||||
last->next = sig;
|
||||
result &= (sig == NULL);
|
||||
|
||||
SigCleanSignatures(de_ctx);
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check that the signatures are assigned priority based on classtype they
|
||||
* are given.
|
||||
*/
|
||||
int DetectClasstypeTest03()
|
||||
{
|
||||
int result = 0;
|
||||
Signature *last = NULL;
|
||||
Signature *sig = NULL;
|
||||
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
if (de_ctx == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
SCClassConfGenerateValidDummyClassConfigFile01("/var/log/eidps/classification.config");
|
||||
SCClassConfLoadClassficationConfigFile(de_ctx);
|
||||
SCClassConfDeleteDummyClassificationConfigFile("/var/log/eidps/classification.config");
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Classtype test\"; Classtype:bad-unknown; priority:1; sid:1;)");
|
||||
de_ctx->sig_list = last = sig;
|
||||
result = (sig != NULL);
|
||||
result &= (sig->prio == 1);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Classtype test\"; Classtype:atteMPted-dos; "
|
||||
"priority:3; sid:1;)");
|
||||
last->next = sig;
|
||||
last = sig;
|
||||
result &= (sig != NULL);
|
||||
result &= (sig->prio == 3);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Classtype test\"; "
|
||||
"Classtype:attempted-dos; priority:1; sid:1;)");
|
||||
last->next = sig;
|
||||
last = sig;
|
||||
result &= (sig != NULL);
|
||||
result &= (sig->prio == 1);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Classtype test\"; "
|
||||
"priority:1; Classtype:unknown; sid:1;)");
|
||||
last->next = sig;
|
||||
last = sig;
|
||||
result &= (sig != NULL);
|
||||
result &= (sig->prio == 1);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Classtype test\"; "
|
||||
"priority:2; Classtype:unknown; sid:1;)");
|
||||
last->next = sig;
|
||||
last = sig;
|
||||
result &= (sig != NULL);
|
||||
result &= (sig->prio == 2);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Classtype test\"; "
|
||||
"sid:1;)");
|
||||
last->next = sig;
|
||||
last = sig;
|
||||
result &= (sig != NULL);
|
||||
result &= (sig->prio == 3);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Classtype test\"; "
|
||||
"Classtype:unknown; sid:1;)");
|
||||
last->next = sig;
|
||||
last = sig;
|
||||
result &= (sig != NULL);
|
||||
result &= (sig->prio == 3);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any (msg:\"Classtype test\"; "
|
||||
"Classtype:bad-unknown; sid:1;)");
|
||||
last->next = sig;
|
||||
last = sig;
|
||||
result &= (sig != NULL);
|
||||
result &= (sig->prio == 2);
|
||||
|
||||
SigCleanSignatures(de_ctx);
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* UNITTESTS */
|
||||
|
||||
/**
|
||||
* \brief This function registers unit tests for Classification Config API.
|
||||
*/
|
||||
void DetectClasstypeRegisterTests(void)
|
||||
{
|
||||
|
||||
#ifdef UNITTESTS
|
||||
|
||||
UtRegisterTest("DetectClasstypeTest01", DetectClasstypeTest01, 1);
|
||||
UtRegisterTest("DetectClasstypeTest02", DetectClasstypeTest02, 1);
|
||||
UtRegisterTest("DetectClasstypeTest03", DetectClasstypeTest03, 1);
|
||||
|
||||
#endif /* UNITTESTS */
|
||||
|
||||
}
|
||||
|
@ -1,8 +1,12 @@
|
||||
/** Copyright (c) 2009 Open Information Security Foundation.
|
||||
* \author Anoop Saldanha <poonaatsoc@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef __DETECT_CLASSTYPE_H__
|
||||
#define __DETECT_CLASSTYPE_H__
|
||||
|
||||
/* prototypes */
|
||||
void DetectClasstypeRegister (void);
|
||||
void DetectClasstypeRegister(void);
|
||||
|
||||
#endif /* __DETECT_CLASSTYPE_H__ */
|
||||
|
||||
|
@ -1,33 +1,178 @@
|
||||
/* PRIORITY part of the detection engine. */
|
||||
/** Copyright (c) 2009 Open Information Security Foundation.
|
||||
* \author Victor Julien <victor@inliniac.net>
|
||||
* \author Anoop Saldanha <poonaatsoc@gmail.com>
|
||||
*/
|
||||
|
||||
#include "suricata-common.h"
|
||||
#include "detect.h"
|
||||
#include "detect-parse.h"
|
||||
#include "detect-engine.h"
|
||||
#include "detect-engine-mpm.h"
|
||||
#include "util-error.h"
|
||||
#include "util-debug.h"
|
||||
#include "util-unittest.h"
|
||||
|
||||
#define DETECT_PRIORITY_REGEX "^\\s*(\\d+|\"\\d+\")\\s*$"
|
||||
|
||||
static pcre *regex = NULL;
|
||||
static pcre_extra *regex_study = NULL;
|
||||
|
||||
int DetectPrioritySetup (DetectEngineCtx *, Signature *s, SigMatch *m, char *sidstr);
|
||||
void SCPriorityRegisterTests(void);
|
||||
|
||||
/**
|
||||
* \brief Registers the handler functions for the "priority" keyword
|
||||
*/
|
||||
void DetectPriorityRegister (void)
|
||||
{
|
||||
const char *eb = NULL;
|
||||
int eo;
|
||||
int opts = 0;
|
||||
|
||||
void DetectPriorityRegister (void) {
|
||||
sigmatch_table[DETECT_PRIORITY].name = "priority";
|
||||
sigmatch_table[DETECT_PRIORITY].Match = NULL;
|
||||
sigmatch_table[DETECT_PRIORITY].Setup = DetectPrioritySetup;
|
||||
sigmatch_table[DETECT_PRIORITY].Free = NULL;
|
||||
sigmatch_table[DETECT_PRIORITY].RegisterTests = NULL;
|
||||
sigmatch_table[DETECT_PRIORITY].RegisterTests = SCPriorityRegisterTests;
|
||||
|
||||
regex = pcre_compile(DETECT_PRIORITY_REGEX, opts, &eb, &eo, NULL);
|
||||
if (regex == NULL) {
|
||||
SCLogDebug("Compile of \"%s\" failed at offset %" PRId32 ": %s",
|
||||
DETECT_PRIORITY_REGEX, eo, eb);
|
||||
goto end;
|
||||
}
|
||||
|
||||
regex_study = pcre_study(regex, 0, &eb);
|
||||
if (eb != NULL) {
|
||||
SCLogDebug("pcre study failed: %s", eb);
|
||||
goto end;
|
||||
}
|
||||
|
||||
end:
|
||||
return;
|
||||
}
|
||||
|
||||
int DetectPrioritySetup (DetectEngineCtx *de_ctx, Signature *s, SigMatch *m, char *rawstr)
|
||||
{
|
||||
char *str = rawstr;
|
||||
char dubbed = 0;
|
||||
|
||||
/* strip "'s */
|
||||
if (rawstr[0] == '\"' && rawstr[strlen(rawstr)-1] == '\"') {
|
||||
str = strdup(rawstr+1);
|
||||
str[strlen(rawstr)-2] = '\0';
|
||||
dubbed = 1;
|
||||
const char *prio_str = 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 Priority in Signature "
|
||||
"- %s", rawstr);
|
||||
return -1;
|
||||
}
|
||||
|
||||
s->prio = (uint32_t)atoi(str);
|
||||
ret = pcre_get_substring((char *)rawstr, ov, 30, 1, &prio_str);
|
||||
if (ret < 0) {
|
||||
SCLogInfo("pcre_get_substring() failed");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* if we have reached here, we have had a valid priority. Assign it */
|
||||
s->prio = atoi(prio_str);
|
||||
|
||||
if (dubbed) free(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*------------------------------Unittests-------------------------------------*/
|
||||
|
||||
#ifdef UNITTESTS
|
||||
|
||||
int DetectPriorityTest01()
|
||||
{
|
||||
int result = 0;
|
||||
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
if (de_ctx == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Priority test\"; priority:2; sid:1;)");
|
||||
if (de_ctx->sig_list != NULL)
|
||||
result = 1;
|
||||
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
int DetectPriorityTest02()
|
||||
{
|
||||
int result = 0;
|
||||
Signature *last = NULL;
|
||||
Signature *sig = NULL;
|
||||
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
if (de_ctx == NULL) {
|
||||
goto end;
|
||||
}
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Priority test\"; priority:1; sid:1;)");
|
||||
de_ctx->sig_list = last = sig;
|
||||
result = (sig != NULL);
|
||||
result &= (sig->prio == 1);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Priority test\"; priority:boo; sid:1;)");
|
||||
last->next = sig;
|
||||
result &= (sig == NULL);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Priority test\"; priority:10boo; sid:1;)");
|
||||
last->next = sig;
|
||||
result &= (sig == NULL);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Priority test\"; priority:b10oo; sid:1;)");
|
||||
last->next = sig;
|
||||
result &= (sig == NULL);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Priority test\"; priority:boo10; sid:1;)");
|
||||
last->next = sig;
|
||||
result &= (sig == NULL);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Priority test\"; priority:-1; sid:1;)");
|
||||
last->next = sig;
|
||||
result &= (sig == NULL);
|
||||
|
||||
sig = SigInit(de_ctx, "alert tcp any any -> any any "
|
||||
"(msg:\"Priority test\"; sid:1;)");
|
||||
last->next = sig;
|
||||
last = sig;
|
||||
result &= (sig != NULL);
|
||||
result &= (sig->prio == 3);
|
||||
|
||||
SigCleanSignatures(de_ctx);
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
|
||||
end:
|
||||
return result;
|
||||
}
|
||||
|
||||
|
||||
#endif /* UNITTESTS */
|
||||
|
||||
/**
|
||||
* \brief This function registers unit tests for Classification Config API.
|
||||
*/
|
||||
void SCPriorityRegisterTests(void)
|
||||
{
|
||||
|
||||
#ifdef UNITTESTS
|
||||
|
||||
UtRegisterTest("DetectPriorityTest01", DetectPriorityTest01, 1);
|
||||
UtRegisterTest("DetectPriorityTest02", DetectPriorityTest02, 1);
|
||||
|
||||
#endif /* UNITTESTS */
|
||||
|
||||
}
|
||||
|
@ -0,0 +1,853 @@
|
||||
/** Copyright (c) 2009 Open Information Security Foundation.
|
||||
* \author Anoop Saldanha <poonaatsoc@gmail.com>
|
||||
*/
|
||||
|
||||
#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"
|
||||
|
||||
/* 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 */
|
||||
#define SC_CLASS_CONF_DEF_CONF_FILEPATH "/root/classification.config1"
|
||||
|
||||
/* Holds a pointer to the default path for the classification.config file */
|
||||
static const char *default_file_path = SC_CLASS_CONF_DEF_CONF_FILEPATH;
|
||||
static FILE *fd = NULL;
|
||||
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);
|
||||
|
||||
/**
|
||||
* \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 char *SCClassConfGetConfFilename(void)
|
||||
{
|
||||
char *log_filename = (char *)default_file_path;
|
||||
|
||||
ConfGet("classification-file", &log_filename);
|
||||
|
||||
return log_filename;
|
||||
}
|
||||
|
||||
/**
|
||||
* \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 0 On success.
|
||||
* \retval -1 On failure.
|
||||
*/
|
||||
static inline int SCClassConfInitContext(DetectEngineCtx *de_ctx)
|
||||
{
|
||||
char *filename = NULL;
|
||||
const char *eb = NULL;
|
||||
int eo;
|
||||
int opts = 0;
|
||||
|
||||
/* init the hash table to be used by the classification config Classtypes */
|
||||
de_ctx->class_conf_ht = HashTableInit(4096, SCClassConfClasstypeHashFunc,
|
||||
SCClassConfClasstypeHashCompareFunc,
|
||||
SCClassConfClasstypeHashFree);
|
||||
if (de_ctx->class_conf_ht == NULL) {
|
||||
SCLogError(SC_ERR_HASH_TABLE_INIT_FAILED, "Error initializing the hash "
|
||||
"table");
|
||||
return -1;
|
||||
}
|
||||
|
||||
filename = SCClassConfGetConfFilename();
|
||||
if ( (fd = fopen(filename, "r")) == NULL) {
|
||||
SCLogError(SC_ERR_FOPEN_ERROR, "Error opening file: \"%s\"", filename);
|
||||
goto error;
|
||||
}
|
||||
|
||||
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);
|
||||
goto error;
|
||||
}
|
||||
|
||||
regex_study = pcre_study(regex, 0, &eb);
|
||||
if (eb != NULL) {
|
||||
SCLogDebug("pcre study failed: %s", eb);
|
||||
goto error;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
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);
|
||||
return -1;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Releases resources used by the Classification Config API.
|
||||
*/
|
||||
static void SCClassConfDeInitContext(void)
|
||||
{
|
||||
fclose(fd);
|
||||
default_file_path = SC_CLASS_CONF_DEF_CONF_FILEPATH;
|
||||
|
||||
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 = strdup(str)) == NULL) {
|
||||
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
temp_str = new_str;
|
||||
while (*temp_str != '\0') {
|
||||
*temp_str = tolower(*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 de_ctx Pointer to the Detection Engine Context.
|
||||
*
|
||||
* \retval 0 On success.
|
||||
* \retval -1 On failure.
|
||||
*/
|
||||
static inline int SCClassConfAddClasstype(char *rawstr, DetectEngineCtx *de_ctx)
|
||||
{
|
||||
const char *ct_name = NULL;
|
||||
const char *ct_desc = NULL;
|
||||
const char *ct_priority_str = NULL;
|
||||
int ct_priority = 0;
|
||||
|
||||
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_get_substring((char *)rawstr, ov, 30, 1, &ct_name);
|
||||
if (ret < 0) {
|
||||
SCLogInfo("pcre_get_substring() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* retrieve the classtype description */
|
||||
ret = pcre_get_substring((char *)rawstr, ov, 30, 2, &ct_desc);
|
||||
if (ret < 0) {
|
||||
SCLogInfo("pcre_get_substring() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
/* retrieve the classtype priority */
|
||||
ret = pcre_get_substring((char *)rawstr, ov, 30, 3, &ct_priority_str);
|
||||
if (ret < 0) {
|
||||
SCLogInfo("pcre_get_substring() failed");
|
||||
goto error;
|
||||
}
|
||||
|
||||
ct_priority = atoi(ct_priority_str);
|
||||
|
||||
/* Create a new instance of the parsed Classtype string */
|
||||
ct_new = SCClassConfAllocClasstype(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");
|
||||
}
|
||||
|
||||
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(*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 inline void SCClassConfParseFile(DetectEngineCtx *de_ctx)
|
||||
{
|
||||
char line[1024];
|
||||
|
||||
while (fgets(line, sizeof(line), fd) != NULL) {
|
||||
if (SCClassConfIsLineBlankOrComment(line))
|
||||
continue;
|
||||
|
||||
SCClassConfAddClasstype(line, de_ctx);
|
||||
}
|
||||
|
||||
SCLogInfo("Added \"%d\" classification types from the classification file",
|
||||
de_ctx->class_conf_ht->count);
|
||||
|
||||
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(const char *classtype,
|
||||
const char *classtype_desc,
|
||||
int priority)
|
||||
{
|
||||
SCClassConfClasstype *ct = NULL;
|
||||
|
||||
if (classtype == NULL)
|
||||
return NULL;
|
||||
|
||||
if ( (ct = malloc(sizeof(SCClassConfClasstype))) == NULL) {
|
||||
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
memset(ct, 0, sizeof(SCClassConfClasstype));
|
||||
|
||||
if ( (ct->classtype = SCClassConfStringToLowercase(classtype)) == NULL) {
|
||||
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (classtype_desc != NULL &&
|
||||
(ct->classtype_desc = strdup(classtype_desc)) == NULL) {
|
||||
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
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)
|
||||
free(ct->classtype);
|
||||
|
||||
if (ct->classtype_desc != NULL)
|
||||
free(ct->classtype_desc);
|
||||
|
||||
free(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((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)
|
||||
{
|
||||
if (SCClassConfInitContext(de_ctx) == -1) {
|
||||
SCLogDebug("Error initializing classification config API");
|
||||
return;
|
||||
}
|
||||
|
||||
SCClassConfParseFile(de_ctx);
|
||||
SCClassConfDeInitContext();
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
/*----------------------------------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.
|
||||
*/
|
||||
void SCClassConfGenerateValidDummyClassConfigFile01(const char *file_path)
|
||||
{
|
||||
FILE *fd = NULL;
|
||||
|
||||
default_file_path = file_path;
|
||||
|
||||
if ( (fd = fopen(default_file_path, "w+")) == NULL) {
|
||||
SCLogError(SC_ERR_FOPEN_ERROR, "Error opening file: \"%s\"", file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
fputs("config classification: not-suspicious,Not Suspicious Traffic,3\n", fd);
|
||||
fputs("config classification: unknown,Unknown Traffic,3\n", fd);
|
||||
fputs("config classification: bad-unknown,Potentially Bad Traffic, 2\n", fd);
|
||||
fputs("config classification: attempted-recon,Attempted Information "
|
||||
"Leak,2\n", fd);
|
||||
fputs("config classification: successful-recon-limited,Information "
|
||||
"Leak,2\n", fd);
|
||||
fputs("config classification: successful-recon-largescale,Large Scale "
|
||||
"Information Leak,2\n", fd);
|
||||
fputs("config classification: attempted-dos,Attempted Denial of "
|
||||
"Service,2\n", fd);
|
||||
fputs("config classification: successful-dos,Denial of Service,2\n", fd);
|
||||
fputs("config classification: attempted-user,Attempted User Privilege "
|
||||
"Gain,1\n", fd);
|
||||
fputs("config classification: unsuccessful-user,Unsuccessful User "
|
||||
"Privilege Gain,1\n", fd);
|
||||
fputs("config classification: successful-user,Successful User Privilege "
|
||||
"Gain,1\n", fd);
|
||||
fputs("config classification: attempted-admin,Attempted Administrator "
|
||||
"Privilege Gain,1\n", fd);
|
||||
fputs("config classification: successful-admin,Successful Administrator "
|
||||
"Privilege Gain,1\n", fd);
|
||||
fputs("config classification: rpc-portmap-decode,Decode of an RPC "
|
||||
"Query,2\n", fd);
|
||||
fputs("config classification: shellcode-detect,Executable code was "
|
||||
"detected,1\n", fd);
|
||||
fputs("config classification: string-detect,A suspicious string was "
|
||||
"detected,3\n", fd);
|
||||
fputs("config classification: suspicious-filename-detect,A suspicious "
|
||||
"filename was detected,2\n", fd);
|
||||
fputs("config classification: suspicious-login,An attempted login using "
|
||||
"a suspicious username was detected,2\n", fd);
|
||||
fputs("config classification: system-call-detect,A system call was "
|
||||
"detected,2\n", fd);
|
||||
fputs("config classification: tcp-connection,A TCP connection was "
|
||||
"detected,4\n", fd);
|
||||
fputs("config classification: trojan-activity,A Network Trojan was "
|
||||
"detected, 1\n", fd);
|
||||
fputs("config classification: unusual-client-port-connection,A client "
|
||||
"was using an unusual port,2\n", fd);
|
||||
fputs("config classification: network-scan,Detection of a Network "
|
||||
"Scan,3\n", fd);
|
||||
fputs("config classification: denial-of-service,Detection of a Denial "
|
||||
"of Service Attack,2\n", fd);
|
||||
fputs("config classification: non-standard-protocol,Detection of a "
|
||||
"non-standard protocol or event,2\n", fd);
|
||||
fputs("config classification: protocol-command-decode,Generic Protocol "
|
||||
"Command Decode,3\n", fd);
|
||||
fputs("config classification: web-application-activity,access to a "
|
||||
"potentially vulnerable web application,2\n", fd);
|
||||
fputs("config classification: web-application-attack,Web Application "
|
||||
"Attack,1\n", fd);
|
||||
fputs("config classification: misc-activity,Misc activity,3\n", fd);
|
||||
fputs("config classification: misc-attack,Misc Attack,2\n", fd);
|
||||
fputs("config classification: icmp-event,Generic ICMP event,3\n", fd);
|
||||
fputs("config classification: kickass-porn,SCORE! Get the lotion!,1\n", fd);
|
||||
fputs("config classification: policy-violation,Potential Corporate "
|
||||
"Privacy Violation,1\n", fd);
|
||||
fputs("config classification: default-login-attempt,Attempt to login by "
|
||||
"a default username and password,2\n", fd);
|
||||
|
||||
fclose(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.
|
||||
*/
|
||||
void SCClassConfGenerateInValidDummyClassConfigFile02(const char *file_path)
|
||||
{
|
||||
FILE *fd = NULL;
|
||||
|
||||
default_file_path = file_path;
|
||||
|
||||
if ( (fd = fopen(default_file_path, "w+")) == NULL) {
|
||||
SCLogError(SC_ERR_FOPEN_ERROR, "Error opening file: \"%s\"", file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
fputs("config classification: not-suspicious,Not Suspicious Traffic,3\n", fd);
|
||||
fputs("onfig classification: unknown,Unknown Traffic,3\n", fd);
|
||||
fputs("config classification: _badunknown,Potentially Bad Traffic, 2\n", fd);
|
||||
fputs("config classification: bamboola1,Unknown Traffic,3\n", fd);
|
||||
fputs("config classification: misc-activity,Misc activity,-1\n", fd);
|
||||
fputs("config classification: policy-violation,Potential Corporate ", fd);
|
||||
fputs("config classification: bamboola,Unknown Traffic,3\n", fd);
|
||||
|
||||
fclose(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.
|
||||
*/
|
||||
void SCClassConfGenerateInValidDummyClassConfigFile03(const char *file_path)
|
||||
{
|
||||
FILE *fd = NULL;
|
||||
|
||||
default_file_path = file_path;
|
||||
|
||||
if ( (fd = fopen(default_file_path, "w+")) == NULL) {
|
||||
SCLogError(SC_ERR_FOPEN_ERROR, "Error opening file: \"%s\"", file_path);
|
||||
return;
|
||||
}
|
||||
|
||||
fputs("conig classification: not-suspicious,Not Suspicious Traffic,3\n", fd);
|
||||
fputs("onfig classification: unknown,Unknown Traffic,3\n", fd);
|
||||
fputs("config classification: _badunknown,Potentially Bad Traffic, 2\n", fd);
|
||||
fputs("config classification: misc-activity,Misc activity,-1\n", fd);
|
||||
|
||||
fclose(fd);
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Deletes a file, whose path is specified as the argument.
|
||||
*
|
||||
* \file_path Pointer to the file_path that has to be deleted.
|
||||
*/
|
||||
void SCClassConfDeleteDummyClassificationConfigFile(const char *file_path)
|
||||
{
|
||||
remove(file_path);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check that the classification file is loaded and the detection engine
|
||||
* content class_conf_hash_table loaded with the classtype data.
|
||||
*/
|
||||
int SCClassConfTest01(void)
|
||||
{
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
int result = 0;
|
||||
|
||||
if (de_ctx == NULL)
|
||||
return result;
|
||||
|
||||
SCClassConfGenerateValidDummyClassConfigFile01("/var/log/eidps/classification.config");
|
||||
SCClassConfLoadClassficationConfigFile(de_ctx);
|
||||
SCClassConfDeleteDummyClassificationConfigFile("/var/log/eidps/classification.config");
|
||||
|
||||
if (de_ctx->class_conf_ht == NULL)
|
||||
return result;
|
||||
|
||||
result = (de_ctx->class_conf_ht->count == 34);
|
||||
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
free(de_ctx->class_conf_ht);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check that invalid classtypes present in the classification config file
|
||||
* aren't loaded.
|
||||
*/
|
||||
int SCClassConfTest02(void)
|
||||
{
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
int result = 0;
|
||||
|
||||
if (de_ctx == NULL)
|
||||
return result;
|
||||
|
||||
SCClassConfGenerateInValidDummyClassConfigFile03("/var/log/eidps/classification.config");
|
||||
SCClassConfLoadClassficationConfigFile(de_ctx);
|
||||
SCClassConfDeleteDummyClassificationConfigFile("/var/log/eidps/classification.config");
|
||||
|
||||
if (de_ctx->class_conf_ht == NULL)
|
||||
return result;
|
||||
|
||||
result = (de_ctx->class_conf_ht->count == 0);
|
||||
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
free(de_ctx->class_conf_ht);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check that only valid classtypes are loaded into the hash table from
|
||||
* the classfication.config file.
|
||||
*/
|
||||
int SCClassConfTest03(void)
|
||||
{
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
int result = 0;
|
||||
|
||||
if (de_ctx == NULL)
|
||||
return result;
|
||||
|
||||
SCClassConfGenerateInValidDummyClassConfigFile02("/var/log/eidps/classification.config");
|
||||
SCClassConfLoadClassficationConfigFile(de_ctx);
|
||||
SCClassConfDeleteDummyClassificationConfigFile("/var/log/eidps/classification.config");
|
||||
|
||||
if (de_ctx->class_conf_ht == NULL)
|
||||
return result;
|
||||
|
||||
result = (de_ctx->class_conf_ht->count == 3);
|
||||
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
free(de_ctx->class_conf_ht);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check if the classtype info from the classification.config file have
|
||||
* been loaded into the hash table.
|
||||
*/
|
||||
int SCClassConfTest04(void)
|
||||
{
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
SCClassConfClasstype *ct = NULL;
|
||||
int result = 1;
|
||||
|
||||
if (de_ctx == NULL)
|
||||
return 0;
|
||||
|
||||
SCClassConfGenerateValidDummyClassConfigFile01("/var/log/eidps/classification.config");
|
||||
SCClassConfLoadClassficationConfigFile(de_ctx);
|
||||
SCClassConfDeleteDummyClassificationConfigFile("/var/log/eidps/classification.config");
|
||||
|
||||
if (de_ctx->class_conf_ht == NULL)
|
||||
return 0;
|
||||
|
||||
result = (de_ctx->class_conf_ht->count == 34);
|
||||
|
||||
ct = SCClassConfAllocClasstype("unknown", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) != NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("unKnoWn", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) != NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("bamboo", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) == NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("bad-unknown", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) != NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("BAD-UNKnOWN", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) != NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("bed-unknown", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) == NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
free(de_ctx->class_conf_ht);
|
||||
|
||||
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.
|
||||
*/
|
||||
int SCClassConfTest05(void)
|
||||
{
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
SCClassConfClasstype *ct = NULL;
|
||||
int result = 1;
|
||||
|
||||
if (de_ctx == NULL)
|
||||
return 0;
|
||||
|
||||
SCClassConfGenerateInValidDummyClassConfigFile03("/var/log/eidps/classification.config");
|
||||
SCClassConfLoadClassficationConfigFile(de_ctx);
|
||||
SCClassConfDeleteDummyClassificationConfigFile("/var/log/eidps/classification.config");
|
||||
|
||||
if (de_ctx->class_conf_ht == NULL)
|
||||
return 0;
|
||||
|
||||
result = (de_ctx->class_conf_ht->count == 0);
|
||||
|
||||
ct = SCClassConfAllocClasstype("unknown", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) == NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("unKnoWn", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) == NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("bamboo", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) == NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("bad-unknown", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) == NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("BAD-UNKnOWN", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) == NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("bed-unknown", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) == NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
free(de_ctx->class_conf_ht);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* \test Check if the classtype info from the classification.config file have
|
||||
* been loaded into the hash table.
|
||||
*/
|
||||
int SCClassConfTest06(void)
|
||||
{
|
||||
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
|
||||
SCClassConfClasstype *ct = NULL;
|
||||
int result = 1;
|
||||
|
||||
if (de_ctx == NULL)
|
||||
return 0;
|
||||
|
||||
SCClassConfGenerateInValidDummyClassConfigFile02("/var/log/eidps/classification.config");
|
||||
SCClassConfLoadClassficationConfigFile(de_ctx);
|
||||
SCClassConfDeleteDummyClassificationConfigFile("/var/log/eidps/classification.config");
|
||||
|
||||
if (de_ctx->class_conf_ht == NULL)
|
||||
return 0;
|
||||
|
||||
result = (de_ctx->class_conf_ht->count == 3);
|
||||
|
||||
ct = SCClassConfAllocClasstype("unknown", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) == NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("not-suspicious", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) != NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("bamboola1", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) != NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("bamboola1", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) != NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("BAMBOolA1", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) != NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
ct = SCClassConfAllocClasstype("unkNOwn", NULL, 0);
|
||||
result &= (HashTableLookup(de_ctx->class_conf_ht, ct, 0) == NULL);
|
||||
SCClassConfDeAllocClasstype(ct);
|
||||
|
||||
DetectEngineCtxFree(de_ctx);
|
||||
free(de_ctx->class_conf_ht);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
#endif /* UNITTESTS */
|
||||
|
||||
/**
|
||||
* \brief This function registers unit tests for Classification Config API.
|
||||
*/
|
||||
void SCClassConfRegisterTests(void)
|
||||
{
|
||||
|
||||
#ifdef UNITTESTS
|
||||
|
||||
UtRegisterTest("SCClassConfTest01", SCClassConfTest01, 1);
|
||||
UtRegisterTest("SCClassConfTest02", SCClassConfTest02, 1);
|
||||
UtRegisterTest("SCClassConfTest03", SCClassConfTest03, 1);
|
||||
UtRegisterTest("SCClassConfTest04", SCClassConfTest04, 1);
|
||||
UtRegisterTest("SCClassConfTest05", SCClassConfTest05, 1);
|
||||
UtRegisterTest("SCClassConfTest06", SCClassConfTest06, 1);
|
||||
|
||||
#endif /* UNITTESTS */
|
||||
|
||||
}
|
@ -0,0 +1,34 @@
|
||||
/** Copyright (c) 2009 Open Information Security Foundation.
|
||||
* \author Anoop Saldanha <poonaatsoc@gmail.com>
|
||||
*/
|
||||
|
||||
#ifndef __UTIL_CLASSIFICATION_CONFIG_H__
|
||||
#define __UTIL_CLASSIFICATION_CONFIG_H__
|
||||
|
||||
/**
|
||||
* \brief Container for a Classtype from the Classification.config file.
|
||||
*/
|
||||
typedef struct SCClassConfClasstype_ {
|
||||
/* The classtype name. This is the primary key for a Classification. */
|
||||
char *classtype;
|
||||
|
||||
/* Description for a classification. Would be used while printing out
|
||||
* the classification info for a Signature, by the fast-log module. */
|
||||
char *classtype_desc;
|
||||
|
||||
/* The priority this classification type carries */
|
||||
int priority;
|
||||
} SCClassConfClasstype;
|
||||
|
||||
SCClassConfClasstype *SCClassConfAllocClasstype(const char *, const char *,
|
||||
int);
|
||||
void SCClassConfDeAllocClasstype(SCClassConfClasstype *);
|
||||
void SCClassConfLoadClassficationConfigFile(DetectEngineCtx *);
|
||||
void SCClassConfRegisterTests(void);
|
||||
|
||||
void SCClassConfGenerateValidDummyClassConfigFile01(const char *);
|
||||
void SCClassConfGenerateInValidDummyClassConfigFile02(const char *);
|
||||
void SCClassConfGenerateInValidDummyClassConfigFile03(const char *);
|
||||
void SCClassConfDeleteDummyClassificationConfigFile(const char *);
|
||||
|
||||
#endif /* __UTIL_CLASSIFICATION_CONFIG_H__ */
|
Loading…
Reference in New Issue