diff --git a/src/Makefile.am b/src/Makefile.am index 466cac2fed..edfb57edd2 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -168,6 +168,7 @@ util-daemon.c util-daemon.h \ util-random.c util-random.h \ util-classification-config.c util-classification-config.h \ util-threshold-config.c util-threshold-config.h \ +util-reference-config.c util-reference-config.h \ util-strlcatu.c \ util-strlcpyu.c \ util-cuda.c util-cuda.h \ diff --git a/src/decode.h b/src/decode.h index 2a02b34041..b911ed5e11 100644 --- a/src/decode.h +++ b/src/decode.h @@ -208,7 +208,7 @@ typedef struct PacketAlert_ { uint32_t sid; char *msg; char *class_msg; - Reference *references; + DetectReference *references; uint8_t flags; } PacketAlert; diff --git a/src/detect-engine-uri.c b/src/detect-engine-uri.c index eff5a5b513..5a2754a50b 100644 --- a/src/detect-engine-uri.c +++ b/src/detect-engine-uri.c @@ -2221,7 +2221,7 @@ static int UriTestSig16(void) de_ctx->mpm_matcher = MPM_B2G; de_ctx->flags |= DE_QUIET; - s = de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any any (msg:\"ET TROJAN Downadup/Conficker A or B Worm reporting\"; flow:to_server,established; uricontent:\"/search?q=\"; pcre:\"/^\\/search\\?q=[0-9]{1,3}(&aq=7(\\?[0-9a-f]{8})?)?/U\"; pcre:\"/\\x0d\\x0aHost\\: \\d+\\.\\d+\\.\\d+\\.\\d+\\x0d\\x0a/\"; reference:url,www.f-secure.com/weblog/archives/00001584.html; reference:url,doc.emergingthreats.net/bin/view/Main/2009024; reference:url,www.emergingthreats.net/cgi-bin/cvsweb.cgi/sigs/VIRUS/TROJAN_Conficker; sid:2009024; rev:9;)"); + s = de_ctx->sig_list = SigInit(de_ctx, "drop tcp any any -> any any (msg:\"ET TROJAN Downadup/Conficker A or B Worm reporting\"; flow:to_server,established; uricontent:\"/search?q=\"; pcre:\"/^\\/search\\?q=[0-9]{1,3}(&aq=7(\\?[0-9a-f]{8})?)?/U\"; pcre:\"/\\x0d\\x0aHost\\: \\d+\\.\\d+\\.\\d+\\.\\d+\\x0d\\x0a/\"; sid:2009024; rev:9;)"); if (s == NULL) { goto end; } diff --git a/src/detect-parse.c b/src/detect-parse.c index 168d103557..c718879daa 100644 --- a/src/detect-parse.c +++ b/src/detect-parse.c @@ -992,8 +992,8 @@ Signature *SigAlloc (void) { static void SigRefFree (Signature *s) { SCEnter(); - Reference *ref = NULL; - Reference *next_ref = NULL; + DetectReference *ref = NULL; + DetectReference *next_ref = NULL; if (s == NULL) { SCReturn; diff --git a/src/detect-reference.c b/src/detect-reference.c index fab7c340f5..eae7b8ba95 100644 --- a/src/detect-reference.c +++ b/src/detect-reference.c @@ -19,6 +19,7 @@ * \file * * \author Breno Silva + * \author Anoop Saldanha * * Implements the reference keyword support */ @@ -35,6 +36,7 @@ #include "decode-events.h" #include "stream-tcp.h" +#include "util-reference-config.h" #include "detect-reference.h" #include "util-unittest.h" @@ -43,24 +45,13 @@ #define PARSE_REGEX "^\\s*([A-Za-z]+)\\s*,\"?\\s*\"?\\s*([a-zA-Z0-9\\-_\\.\\/\\?\\=]+)\"?\\s*\"?" -/* Static prefix for references - Maybe we should move them to reference.config in the future */ -char REFERENCE_BUGTRAQ[] = "http://www.securityfocus.com/bid/"; -char REFERENCE_CVE[] = "http://cve.mitre.org/cgi-bin/cvename.cgi?name="; -char REFERENCE_NESSUS[] = "http://cgi.nessus.org/plugins/dump.php3?id="; -char REFERENCE_ARACHNIDS[] = "http://www.whitehats.com/info/IDS"; -char REFERENCE_MCAFEE[] = "http://vil.nai.com/vil/dispVirus.asp?virus_k="; -char REFERENCE_URL[] = "http://"; -char REFERENCE_TELUS[] = "http://"; -char REFERENCE_BID[] = "http://"; -char REFERENCE_SECUNIA[] = "http://"; - static pcre *parse_regex; static pcre_extra *parse_regex_study; static int DetectReferenceSetup(DetectEngineCtx *, Signature *s, char *str); /** - * \brief Registration function for reference: keyword + * \brief Registration function for the reference: keyword */ void DetectReferenceRegister(void) { @@ -96,7 +87,7 @@ error: /** * \brief Free a Reference object */ -void DetectReferenceFree(Reference *ref) +void DetectReferenceFree(DetectReference *ref) { SCEnter(); @@ -112,22 +103,21 @@ void DetectReferenceFree(Reference *ref) * \internal * \brief This function is used to parse reference options passed via reference: keyword * - * \param rawstr Pointer to the user provided reference options + * \param rawstr Pointer to the user provided reference options. * * \retval ref Pointer to signature reference on success. * \retval NULL On failure. */ -static Reference *DetectReferenceParse(char *rawstr) +static DetectReference *DetectReferenceParse(char *rawstr, DetectEngineCtx *de_ctx) { SCEnter(); - Reference *ref = NULL; - char *str = NULL; + DetectReference *ref = NULL; #define MAX_SUBSTRINGS 30 int ret = 0, res = 0; int ov[MAX_SUBSTRINGS]; - const char *ref_key = NULL; - const char *ref_content = NULL; + const char *key = NULL; + const char *content = NULL; ret = pcre_exec(parse_regex, parse_regex_study, rawstr, strlen(rawstr), 0, 0, ov, MAX_SUBSTRINGS); @@ -137,79 +127,60 @@ static Reference *DetectReferenceParse(char *rawstr) goto error; } - ref = SCMalloc(sizeof(Reference)); + ref = SCMalloc(sizeof(DetectReference)); if (ref == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "malloc failed: %s", strerror(errno)); goto error; } - memset(ref, 0, sizeof(Reference)); + memset(ref, 0, sizeof(DetectReference)); - res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, &ref_key); + res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 1, &key); if (res < 0) { SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); goto error; } - res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, &ref_content); + res = pcre_get_substring((char *)rawstr, ov, MAX_SUBSTRINGS, 2, &content); if (res < 0) { SCLogError(SC_ERR_PCRE_GET_SUBSTRING, "pcre_get_substring failed"); goto error; } - if (ref_key == NULL || ref_content == NULL) + if (key == NULL || content == NULL) goto error; - if (strcasecmp(ref_key,"cve") == 0) { - ref->key = REFERENCE_CVE; - } else if (strcasecmp(ref_key,"bugtraq") == 0) { - ref->key = REFERENCE_BUGTRAQ; - } else if (strcasecmp(ref_key,"nessus") == 0) { - ref->key = REFERENCE_NESSUS; - } else if (strcasecmp(ref_key,"url") == 0) { - ref->key = REFERENCE_URL; - } else if (strcasecmp(ref_key,"mcafee") == 0) { - ref->key = REFERENCE_MCAFEE; - } else if (strcasecmp(ref_key,"arachnids") == 0) { - ref->key = REFERENCE_ARACHNIDS; - } else if (strcasecmp(ref_key,"telus") == 0) { - ref->key = REFERENCE_ARACHNIDS; - } else if (strcasecmp(ref_key,"bid") == 0) { - ref->key = REFERENCE_ARACHNIDS; - } else if (strcasecmp(ref_key,"secunia") == 0) { - ref->key = REFERENCE_SECUNIA; + SCRConfReference *ref_conf = SCRConfAllocSCRConfReference(key, NULL); + SCRConfReference *lookup_ref_conf = HashTableLookup(de_ctx->reference_conf_ht, + ref_conf, 0); + if (lookup_ref_conf != NULL) { + ref->key = lookup_ref_conf->url; } else { SCLogError(SC_ERR_REFERENCE_UNKNOWN, "unknown reference key \"%s\". " - "Supported keys are cve, bugtraq, nessus, url, mcafee, " - "arachnids.", ref_key); + "Supported keys are defined in reference.config file. Please " + "have a look at the conf param \"reference-config-file\"", key); goto error; } + SCRConfDeAllocSCRConfReference(ref_conf); /* make a copy so we can free pcre's substring */ - str = SCStrdup((char *)ref_content); - if (str == NULL) { + ref->reference = SCStrdup((char *)content); + if (ref->reference == NULL) { SCLogError(SC_ERR_MEM_ALLOC, "strdup failed: %s", strerror(errno)); goto error; } - ref->reference = str; - /* free the substrings */ - pcre_free_substring(ref_key); - pcre_free_substring(ref_content); - + pcre_free_substring(key); + pcre_free_substring(content); SCReturnPtr(ref, "Reference"); error: - if (ref_key != NULL) { - pcre_free_substring(ref_key); - } - if (ref_content != NULL) { - pcre_free_substring(ref_content); - } - - if (ref != NULL) { + if (key != NULL) + pcre_free_substring(key); + if (content != NULL) + pcre_free_substring(content); + if (ref != NULL) DetectReferenceFree(ref); - } SCReturnPtr(NULL, "Reference"); } @@ -226,15 +197,15 @@ error: * \retval 0 On Success. * \retval -1 On Failure. */ -static int DetectReferenceSetup (DetectEngineCtx *de_ctx, Signature *s, - char *rawstr) +static int DetectReferenceSetup(DetectEngineCtx *de_ctx, Signature *s, + char *rawstr) { SCEnter(); - Reference *ref = NULL; - Reference *actual_reference = NULL; + DetectReference *ref = NULL; + DetectReference *sig_refs = NULL; - ref = DetectReferenceParse(rawstr); + ref = DetectReferenceParse(rawstr, de_ctx); if (ref == NULL) goto error; @@ -242,19 +213,15 @@ static int DetectReferenceSetup (DetectEngineCtx *de_ctx, Signature *s, if (s->references == NULL) { s->references = ref; - ref->next = NULL; } else { - actual_reference = s->references; - - while (actual_reference->next != NULL) { - actual_reference = actual_reference->next; + sig_refs = s->references; + while (sig_refs->next != NULL) { + sig_refs = sig_refs->next; } - - actual_reference->next = ref; + sig_refs->next = ref; ref->next = NULL; } - SCLogDebug("s->references %p", s->references); SCReturnInt(0); error: @@ -265,30 +232,34 @@ error: SCReturnInt(-1); } +/***************************************Unittests******************************/ + #ifdef UNITTESTS /** * \test one valid reference. * - * \retval 1 on succces - * \retval 0 on failure + * \retval 1 on succces. + * \retval 0 on failure. */ static int DetectReferenceParseTest01(void) { int result = 0; Signature *s = NULL; - Reference *ref = NULL; + DetectReference *ref = NULL; DetectEngineCtx *de_ctx = DetectEngineCtxInit(); if (de_ctx == NULL) { goto cleanup; } - de_ctx->flags |= DE_QUIET; - s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any " - "(msg:\"One reference\"; reference:cve,001-2010; sid:2;)"); + SCRConfGenerateValidDummyReferenceConfigFD01(); + SCRConfLoadReferenceConfigFile(de_ctx); + SCRConfDeleteDummyReferenceConfigFD(); + s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any " + "(msg:\"One reference\"; reference:one,001-2010; sid:2;)"); if (s == NULL) { goto cleanup; } @@ -298,8 +269,8 @@ static int DetectReferenceParseTest01(void) } ref = s->references; - if (strcmp(ref->key,"http://cve.mitre.org/cgi-bin/cvename.cgi?name=") != 0 || - strcmp(ref->reference,"001-2010") != 0) { + if (strcmp(ref->key, "http://www.one.com") != 0 || + strcmp(ref->reference, "001-2010") != 0) { goto cleanup; } @@ -316,8 +287,8 @@ cleanup: /** * \test for two valid references. * - * \retval 1 on succces - * \retval 0 on failure + * \retval 1 on succces. + * \retval 0 on failure. */ static int DetectReferenceParseTest02(void) { @@ -328,13 +299,16 @@ static int DetectReferenceParseTest02(void) if (de_ctx == NULL) { goto cleanup; } - de_ctx->flags |= DE_QUIET; + SCRConfGenerateValidDummyReferenceConfigFD01(); + SCRConfLoadReferenceConfigFile(de_ctx); + SCRConfDeleteDummyReferenceConfigFD(); + s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any " "(msg:\"Two references\"; " - "reference:url,www.openinfosecfoundation.org; " - "reference:cve,001-2010; sid:2;)"); + "reference:one,openinfosecdoundation.txt; " + "reference:two,001-2010; sid:2;)"); if (s == NULL) { printf("sig parse failed: "); goto cleanup; @@ -345,14 +319,13 @@ static int DetectReferenceParseTest02(void) goto cleanup; } - if (strcmp(s->references->key, "http://") != 0 || - strcmp(s->references->reference, "www.openinfosecfoundation.org") != 0) { + if (strcmp(s->references->key, "http://www.one.com") != 0 || + strcmp(s->references->reference, "openinfosecdoundation.txt") != 0) { printf("first ref failed: "); goto cleanup; } - if (strcmp(s->references->next->key, - "http://cve.mitre.org/cgi-bin/cvename.cgi?name=") != 0 || + if (strcmp(s->references->next->key, "http://www.two.com") != 0 || strcmp(s->references->next->reference, "001-2010") != 0) { printf("second ref failed: "); goto cleanup; @@ -368,10 +341,10 @@ cleanup: } /** - * \test parsing: invalid reference + * \test parsing: invalid reference. * - * \retval 1 on succces - * \retval 0 on failure + * \retval 1 on succces. + * \retval 0 on failure. */ static int DetectReferenceParseTest03(void) { @@ -381,9 +354,12 @@ static int DetectReferenceParseTest03(void) if (de_ctx == NULL) { goto cleanup; } - de_ctx->flags |= DE_QUIET; + SCRConfGenerateValidDummyReferenceConfigFD01(); + SCRConfLoadReferenceConfigFile(de_ctx); + SCRConfDeleteDummyReferenceConfigFD(); + s = de_ctx->sig_list = SigInit(de_ctx, "alert icmp any any -> any any " "(msg:\"invalid ref\"; " "reference:unknownkey,001-2010; sid:2;)"); diff --git a/src/detect-reference.h b/src/detect-reference.h index a8e12ea962..6c513fe143 100644 --- a/src/detect-reference.h +++ b/src/detect-reference.h @@ -28,27 +28,31 @@ #include "decode-ipv4.h" #include "decode-tcp.h" -/** Signature reference list */ -typedef struct Reference_ { - char *key; /**< pointer to key */ - char *reference; /**< reference data */ - struct Reference_ *next; /**< next reference in the signature */ -} Reference; +/** + * \brief Signature reference list. + */ +typedef struct DetectReference_ { + /* pointer to key */ + char *key; + /* reference data */ + char *reference; + /* next reference in the signature */ + struct DetectReference_ *next; +} DetectReference; /** - * Registration function for reference: keyword + * Registration function for Reference keyword */ -void DetectReferenceRegister (void); +void DetectReferenceRegister(void); /** - * This function registers unit tests for Reference + * This function registers unit tests for Reference keyword. */ void ReferenceRegisterTests(void); /** * Free function for a Reference object */ -void DetectReferenceFree(Reference *); +void DetectReferenceFree(DetectReference *); #endif /*__DETECT_REFERENCE_H__ */ - diff --git a/src/detect.h b/src/detect.h index 77ac7e9287..caccc467c0 100644 --- a/src/detect.h +++ b/src/detect.h @@ -382,7 +382,7 @@ typedef struct Signature_ { char *class_msg; /** Reference */ - Reference *references; + DetectReference *references; /* Be careful, this pointer is only valid while parsing the sig, * to warn the user about any possible problem */ @@ -503,6 +503,8 @@ typedef struct DetectEngineCtx_ { /* hash table used for holding the classification config info */ HashTable *class_conf_ht; + /* hash table used for holding the reference config info */ + HashTable *reference_conf_ht; /* main sigs */ DetectEngineLookupFlow flow_gh[FLOW_STATES]; diff --git a/src/suricata.c b/src/suricata.c index 28943a8f10..5f53d8c0d4 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -118,6 +118,7 @@ #include "util-rule-vars.h" #include "util-classification-config.h" #include "util-threshold-config.h" +#include "util-reference-config.h" #include "util-profiling.h" #include "defrag.h" @@ -951,6 +952,7 @@ int main(int argc, char **argv) UtilActionRegisterTests(); SCClassConfRegisterTests(); SCThresholdConfRegisterTests(); + SCRConfRegisterTests(); SSLParserRegisterTests(); #ifdef __SC_CUDA_SUPPORT__ SCCudaRegisterTests(); @@ -1059,6 +1061,12 @@ int main(int argc, char **argv) DetectEngineCtx *de_ctx = DetectEngineCtxInit(); SCClassConfLoadClassficationConfigFile(de_ctx); + if (SCRConfLoadReferenceConfigFile(de_ctx) == -1) { + SCLogInfo("Having trouble loading references from reference.config"); + exit(EXIT_FAILURE); + } + + exit(EXIT_FAILURE); ActionInitConfig(); diff --git a/src/util-error.c b/src/util-error.c index f1de265270..158433de83 100644 --- a/src/util-error.c +++ b/src/util-error.c @@ -191,6 +191,7 @@ const char * SCErrorToString(SCError err) CASE_CODE (SC_WARN_COMPATIBILITY); CASE_CODE (SC_ERR_DCERPC); CASE_CODE (SC_ERR_AHO_CORASICK); + CASE_CODE (SC_ERR_REFERENCE_CONFIG); default: return "UNKNOWN_ERROR"; diff --git a/src/util-error.h b/src/util-error.h index ed1120c77b..f35fab7337 100644 --- a/src/util-error.h +++ b/src/util-error.h @@ -202,6 +202,7 @@ typedef enum { SC_ERR_DCERPC, SC_ERR_DETECT_PREPARE, /**< preparing the detection engine failed */ SC_ERR_AHO_CORASICK, + SC_ERR_REFERENCE_CONFIG, } SCError; const char *SCErrorToString(SCError); diff --git a/src/util-reference-config.c b/src/util-reference-config.c new file mode 100644 index 0000000000..d3ad9afcc4 --- /dev/null +++ b/src/util-reference-config.c @@ -0,0 +1,794 @@ +/* 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 + */ + +#include "suricata-common.h" +#include "detect.h" +#include "detect-engine.h" +#include "util-hash.h" + +#include "util-reference-config.h" +#include "conf.h" +#include "util-unittest.h" +#include "util-error.h" +#include "util-debug.h" +#include "util-fmemopen.h" + +/* Regex to parse each line from reference.config file. The first substring + * is for the system name and the second for the url */ +/*-----------------------------------------------------------system-------------------url----*/ +#define SC_RCONF_REGEX "^\\s*config\\s+reference\\s*:\\s*([a-zA-Z][a-zA-Z0-9-_]*)\\s+(.+)\\s*$" + +/* Default path for the reference.conf file */ +#define SC_RCONF_DEFAULT_FILE_PATH "reference.config" + +/* Holds a pointer to the default path for the reference.config file */ +static const char *file_path = SC_RCONF_DEFAULT_FILE_PATH; +static FILE *fd = NULL; +static pcre *regex = NULL; +static pcre_extra *regex_study = NULL; + +/* the hash functions */ +uint32_t SCRConfReferenceHashFunc(HashTable *ht, void *data, uint16_t datalen); +char SCRConfReferenceHashCompareFunc(void *data1, uint16_t datalen1, + void *data2, uint16_t datalen2); +void SCRConfReferenceHashFree(void *ch); + +/* used to get the reference.config file path */ +static char *SCRConfGetConfFilename(void); + +/** + * \brief Inits the context to be used by the Reference Config parsing API. + * + * This function initializes the hash table to be used by the Detection + * Engine Context to hold the data from reference.config file, + * obtains the file descriptor to parse the reference.config file, and + * inits the regex used to parse the lines from reference.config file. + * + * \param de_ctx Pointer to the Detection Engine Context. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +static int SCRConfInitContext(DetectEngineCtx *de_ctx) +{ + char *filename = NULL; + const char *eb = NULL; + int eo; + int opts = 0; + + /* init the hash table to be used by the reference config references */ + de_ctx->reference_conf_ht = HashTableInit(4096, SCRConfReferenceHashFunc, + SCRConfReferenceHashCompareFunc, + SCRConfReferenceHashFree); + if (de_ctx->reference_conf_ht == NULL) { + SCLogError(SC_ERR_HASH_TABLE_INIT, "Error initializing the hash " + "table"); + return -1; + } + + /* if it is not NULL, use the file descriptor. The hack so that we can + * avoid using a dummy reference file for testing purposes and + * instead use an input stream against a buffer containing the + * reference strings */ + if (fd == NULL) { + filename = SCRConfGetConfFilename(); + if ((fd = fopen(filename, "r")) == NULL) { + SCLogError(SC_ERR_FOPEN, "Error opening file: \"%s\": %s", filename, + strerror(errno)); + goto error; + } + } + + regex = pcre_compile(SC_RCONF_REGEX, opts, &eb, &eo, NULL); + if (regex == NULL) { + SCLogDebug("Compile of \"%s\" failed at offset %" PRId32 ": %s", + SC_RCONF_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->reference_conf_ht != NULL) { + HashTableFree(de_ctx->reference_conf_ht); + de_ctx->reference_conf_ht = NULL; + } + if (fd != NULL) { + fclose(fd); + fd = NULL; + } + + return -1; +} + + +/** + * \brief Returns the path for the Reference 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 reference.config file which is + * "./reference.config". + * + * \retval log_filename Pointer to a string containing the path for the + * reference.config file. + */ +static char *SCRConfGetConfFilename(void) +{ + char *path = (char *)file_path; + ConfGet("reference-config-file", &path); + return path; +} + +/** + * \brief Releases resources used by the Reference Config API. + */ +static void SCRConfDeInitContext(DetectEngineCtx *de_ctx) +{ + if (fd != NULL) + fclose(fd); + file_path = SC_RCONF_DEFAULT_FILE_PATH; + fd = NULL; + + return; +} + +/** + * \brief Converts a string to lowercase. + * + * \param str Pointer to the string to be converted. + */ +static char *SCRConfStringToLowercase(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"); + 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 reference config file and adds it to Reference + * Config hash table DetectEngineCtx->reference_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 int SCRConfAddReference(char *rawstr, DetectEngineCtx *de_ctx) +{ + const char *system = NULL; + const char *url = NULL; + + SCRConfReference *ref_new = NULL; + SCRConfReference *ref_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_REFERENCE_CONFIG, "Invalid Reference Config in " + "reference.config file"); + goto error; + } + + /* retrieve the reference system */ + ret = pcre_get_substring((char *)rawstr, ov, 30, 1, &system); + if (ret < 0) { + SCLogInfo("pcre_get_substring() failed"); + goto error; + } + + /* retrieve the reference url */ + ret = pcre_get_substring((char *)rawstr, ov, 30, 2, &url); + if (ret < 0) { + SCLogInfo("pcre_get_substring() failed"); + goto error; + } + + /* Create a new instance of the parsed Reference string */ + ref_new = SCRConfAllocSCRConfReference(system, url); + + /* Check if the Reference is present in the HashTable. In case it's present + * ignore it, as it's a duplicate. If not present, add it to the table */ + ref_lookup = HashTableLookup(de_ctx->reference_conf_ht, ref_new, 0); + if (ref_lookup == NULL) { + if (HashTableAdd(de_ctx->reference_conf_ht, ref_new, 0) < 0) { + SCLogDebug("HashTable Add failed"); + } + } else { + SCLogDebug("Duplicate reference found inside reference.config"); + SCRConfDeAllocSCRConfReference(ref_new); + } + + /* free the substrings */ + pcre_free_substring(system); + pcre_free_substring(url); + return 0; + + error: + if (system) + pcre_free_substring(system); + if (url) + pcre_free_substring(url); + + 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 SCRConfIsLineBlankOrComment(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 Reference Config file and updates the + * DetectionEngineCtx->reference_conf_ht with the Reference information. + * + * \param de_ctx Pointer to the Detection Engine Context. + */ +static void SCRConfParseFile(DetectEngineCtx *de_ctx) +{ + char line[1024]; + uint8_t i = 1; + + while (fgets(line, sizeof(line), fd) != NULL) { + if (SCRConfIsLineBlankOrComment(line)) + continue; + + SCRConfAddReference(line, de_ctx); + i++; + } + + SCLogInfo("Added \"%d\" reference types from the reference.config file", + de_ctx->reference_conf_ht->count); + + return; +} + +/** + * \brief Returns a new SCRConfReference instance. The reference string + * is converted into lowercase, before being assigned to the instance. + * + * \param system Pointer to the system. + * \param url Pointer to the reference url. + * + * \retval ref Pointer to the new instance of SCRConfReference. + */ +SCRConfReference *SCRConfAllocSCRConfReference(const char *system, + const char *url) +{ + SCRConfReference *ref = NULL; + + if (system == NULL) { + SCLogError(SC_ERR_INVALID_SIGNATURE, "Invalid arguments. system NULL"); + return NULL; + } + + if ((ref = SCMalloc(sizeof(SCRConfReference))) == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); + exit(EXIT_FAILURE); + } + memset(ref, 0, sizeof(SCRConfReference)); + + if ((ref->system = SCRConfStringToLowercase(system)) == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); + exit(EXIT_FAILURE); + } + + if (url != NULL && (ref->url = SCStrdup(url)) == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating memory"); + exit(EXIT_FAILURE); + } + + return ref; +} + +/** + * \brief Frees a SCRConfReference instance. + * + * \param Pointer to the SCRConfReference instance that has to be freed. + */ +void SCRConfDeAllocSCRConfReference(SCRConfReference *ref) +{ + if (ref != NULL) { + if (ref->system != NULL) + SCFree(ref->system); + + if (ref->url != NULL) + SCFree(ref->url); + + SCFree(ref); + } + + return; +} + +/** + * \brief Hashing function to be used to hash the Reference name. Would be + * supplied as an argument to the HashTableInit function for + * DetectEngineCtx->reference_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 SCRConfReference instance. + * \param datalen Not used by this function. + */ +uint32_t SCRConfReferenceHashFunc(HashTable *ht, void *data, uint16_t datalen) +{ + SCRConfReference *ref = (SCRConfReference *)data; + uint32_t hash = 0; + int i = 0; + + int len = strlen(ref->system); + + for (i = 0; i < len; i++) + hash += tolower(ref->system[i]); + + hash = hash % ht->array_size; + + return hash; +} + +/** + * \brief Used to compare two References that have been stored in the HashTable. + * This function is supplied as an argument to the HashTableInit function + * for DetectionEngineCtx->reference_conf_ct. + * + * \param data1 Pointer to the first SCRConfReference to be compared. + * \param len1 Not used by this function. + * \param data2 Pointer to the second SCRConfReference 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 SCRConfReferenceHashCompareFunc(void *data1, uint16_t datalen1, + void *data2, uint16_t datalen2) +{ + SCRConfReference *ref1 = (SCRConfReference *)data1; + SCRConfReference *ref2 = (SCRConfReference *)data2; + int len1 = 0; + int len2 = 0; + + if (ref1 == NULL || ref2 == NULL) + return 0; + + if (ref1->system == NULL || ref2->system == NULL) + return 0; + + len1 = strlen(ref1->system); + len2 = strlen(ref2->system); + + if (len1 == len2 && memcmp(ref1->system, ref2->system, len1) == 0) { + SCLogDebug("Match found inside Reference-Config hash function"); + return 1; + } + + return 0; +} + +/** + * \brief Used to free the Reference Config Hash Data that was stored in + * DetectEngineCtx->reference_conf_ht Hashtable. + * + * \param data Pointer to the data that has to be freed. + */ +void SCRConfReferenceHashFree(void *data) +{ + SCRConfDeAllocSCRConfReference(data); + + return; +} + +/** + * \brief Loads the Reference info from the reference.config file. + * + * The reference.config file contains references that can be used in + * Signatures. Each line of the file should have the following format - + * config reference: system_name, reference_url. + * + * \param de_ctx Pointer to the Detection Engine Context that should be updated + * with reference information. + * + * \retval 0 On success. + * \retval -1 On failure. + */ +int SCRConfLoadReferenceConfigFile(DetectEngineCtx *de_ctx) +{ + if (SCRConfInitContext(de_ctx) == -1) { + SCLogError(SC_ERR_REFERENCE_CONFIG, "Error initializing reference " + "config API"); + return -1; + } + + SCRConfParseFile(de_ctx); + SCRConfDeInitContext(de_ctx); + + return 0; +} + + +/*----------------------------------Unittests---------------------------------*/ + + +#ifdef UNITTESTS + +/** + * \brief Creates a dummy reference config, with all valid references, for + * testing purposes. + */ +void SCRConfGenerateValidDummyReferenceConfigFD01(void) +{ + const char *buffer = + "config reference: one http://www.one.com\n" + "config reference: two http://www.two.com\n" + "config reference: three http://www.three.com\n" + "config reference: one http://www.one.com\n" + "config reference: three http://www.three.com\n"; + + fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); + if (fd == NULL) + SCLogDebug("Error with SCFmemopen() called by Reference Config test code"); + + return; +} + +/** + * \brief Creates a dummy reference config, with some valid references and a + * couple of invalid references, for testing purposes. + */ +void SCRConfGenerateInValidDummyReferenceConfigFD02(void) +{ + const char *buffer = + "config reference: one http://www.one.com\n" + "config_ reference: two http://www.two.com\n" + "config reference_: three http://www.three.com\n" + "config reference: four\n" + "config reference five http://www.five.com\n"; + + fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); + if (fd == NULL) + SCLogDebug("Error with SCFmemopen() called by Reference Config test code"); + + return; +} + +/** + * \brief Creates a dummy reference config, with all invalid references, for + * testing purposes. + */ +void SCRConfGenerateInValidDummyReferenceConfigFD03(void) +{ + const char *buffer = + "config reference one http://www.one.com\n" + "config_ reference: two http://www.two.com\n" + "config reference_: three http://www.three.com\n" + "config reference: four\n"; + + fd = SCFmemopen((void *)buffer, strlen(buffer), "r"); + if (fd == NULL) + SCLogDebug("Error with SCFmemopen() called by Reference Config test code"); + + return; +} + +/** + * \brief Deletes the FD, if set by the other testing functions. + */ +void SCRConfDeleteDummyReferenceConfigFD(void) +{ + if (fd != NULL) { + fclose(fd); + fd = NULL; + } + + return; +} + +/** + * \test Check that the reference file is loaded and the detection engine + * content reference_conf_ht loaded with the reference data. + */ +int SCRConfTest01(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + int result = 0; + + if (de_ctx == NULL) + return result; + + SCRConfGenerateValidDummyReferenceConfigFD01(); + SCRConfLoadReferenceConfigFile(de_ctx); + SCRConfDeleteDummyReferenceConfigFD(); + + if (de_ctx->reference_conf_ht == NULL) + goto end; + + result = (de_ctx->reference_conf_ht->count == 3); + if (result == 0) + printf("FAILED: de_ctx->reference_conf_ht->count %u: ", de_ctx->reference_conf_ht->count); + + end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Check that invalid references present in the reference.config file + * aren't loaded. + */ +int SCRConfTest02(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + int result = 0; + + if (de_ctx == NULL) + return result; + + SCRConfGenerateInValidDummyReferenceConfigFD03(); + SCRConfLoadReferenceConfigFile(de_ctx); + SCRConfDeleteDummyReferenceConfigFD(); + + if (de_ctx->reference_conf_ht == NULL) + goto end; + + result = (de_ctx->reference_conf_ht->count == 0); + + + end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Check that only valid references are loaded into the hash table from + * the reference.config file. + */ +int SCRConfTest03(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + int result = 0; + + if (de_ctx == NULL) + return result; + + SCRConfGenerateInValidDummyReferenceConfigFD02(); + SCRConfLoadReferenceConfigFile(de_ctx); + SCRConfDeleteDummyReferenceConfigFD(); + + if (de_ctx->reference_conf_ht == NULL) + goto end; + + result = (de_ctx->reference_conf_ht->count == 1); + + end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Check if the reference info from the reference.config file have + * been loaded into the hash table. + */ +int SCRConfTest04(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + SCRConfReference *ref = NULL; + int result = 1; + + if (de_ctx == NULL) + return 0; + + SCRConfGenerateValidDummyReferenceConfigFD01(); + SCRConfLoadReferenceConfigFile(de_ctx); + SCRConfDeleteDummyReferenceConfigFD(); + + if (de_ctx->reference_conf_ht == NULL) + goto end; + + result = (de_ctx->reference_conf_ht->count == 3); + + ref = SCRConfAllocSCRConfReference("one", "http://www.one.com"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) != NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("two", "http://www.two.com"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) != NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("three", "http://www.three.com"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) != NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("four", "http://www.four.com"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) == NULL); + SCRConfDeAllocSCRConfReference(ref); + + end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Check if the reference info from the invalid reference.config file + * have not been loaded into the hash table, and cross verify to check + * that the hash table contains no reference data. + */ +int SCRConfTest05(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + SCRConfReference *ref = NULL; + int result = 1; + + if (de_ctx == NULL) + return 0; + + SCRConfGenerateInValidDummyReferenceConfigFD03(); + SCRConfLoadReferenceConfigFile(de_ctx); + SCRConfDeleteDummyReferenceConfigFD(); + + if (de_ctx->reference_conf_ht == NULL) + goto end; + + result = (de_ctx->reference_conf_ht->count == 0); + + ref = SCRConfAllocSCRConfReference("one", "one"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) == NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("two", "two"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) == NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("three", "three"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) == NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("four", "four"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) == NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("five", "five"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) == NULL); + SCRConfDeAllocSCRConfReference(ref); + + end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +/** + * \test Check if the reference info from the reference.config file have + * been loaded into the hash table. + */ +int SCRConfTest06(void) +{ + DetectEngineCtx *de_ctx = DetectEngineCtxInit(); + SCRConfReference *ref = NULL; + int result = 1; + + if (de_ctx == NULL) + return 0; + + SCRConfGenerateInValidDummyReferenceConfigFD02(); + SCRConfLoadReferenceConfigFile(de_ctx); + SCRConfDeleteDummyReferenceConfigFD(); + + if (de_ctx->reference_conf_ht == NULL) + goto end; + + result = (de_ctx->reference_conf_ht->count == 1); + + ref = SCRConfAllocSCRConfReference("one", "one"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) != NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("two", "two"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) == NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("three", "three"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) == NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("four", "four"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) == NULL); + SCRConfDeAllocSCRConfReference(ref); + + ref = SCRConfAllocSCRConfReference("five", "five"); + result &= (HashTableLookup(de_ctx->reference_conf_ht, ref, 0) == NULL); + SCRConfDeAllocSCRConfReference(ref); + + end: + if (de_ctx != NULL) + DetectEngineCtxFree(de_ctx); + return result; +} + +#endif /* UNITTESTS */ + +/** + * \brief This function registers unit tests for Reference Config API. + */ +void SCRConfRegisterTests(void) +{ + +#ifdef UNITTESTS + UtRegisterTest("SCRConfTest01", SCRConfTest01, 1); + UtRegisterTest("SCRConfTest02", SCRConfTest02, 1); + UtRegisterTest("SCRConfTest03", SCRConfTest03, 1); + UtRegisterTest("SCRConfTest04", SCRConfTest04, 1); + UtRegisterTest("SCRConfTest05", SCRConfTest05, 1); + UtRegisterTest("SCRConfTest06", SCRConfTest06, 1); +#endif /* UNITTESTS */ + + return; +} diff --git a/src/util-reference-config.h b/src/util-reference-config.h new file mode 100644 index 0000000000..567869e7ae --- /dev/null +++ b/src/util-reference-config.h @@ -0,0 +1,48 @@ +/* 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 + */ + +#ifndef __UTIL_REFERENCE_CONFIG_H__ +#define __UTIL_REFERENCE_CONFIG_H__ + +/** + * \brief Holds a reference from the file - reference.config. + */ +typedef struct SCRConfReference_ { + /* The system name. This is the primary key for a reference. */ + char *system; + /* The url for the above reference */ + char *url; +} SCRConfReference; + +SCRConfReference *SCRConfAllocSCRConfReference(const char *, const char *); +void SCRConfDeAllocSCRConfReference(SCRConfReference *); +int SCRConfLoadReferenceConfigFile(DetectEngineCtx *); +void SCRConfRegisterTests(void); + +/* these below functions are only used by unittests */ +void SCRConfGenerateValidDummyReferenceConfigFD01(void); +void SCRConfGenerateInValidDummyReferenceConfigFD02(void); +void SCRConfGenerateInValidDummyReferenceConfigFD03(void); +void SCRConfDeleteDummyReferenceConfigFD(void); + +#endif /* __UTIL_REFERENCE_CONFIG_H__ */ diff --git a/suricata.yaml b/suricata.yaml index b99e4c7063..5b9a50fe74 100644 --- a/suricata.yaml +++ b/suricata.yaml @@ -443,6 +443,7 @@ rule-files: - emerging-current_events.rules classification-file: /etc/suricata/classification.config +reference-config-file: /etc/suricata/reference.config # Holds variables that would be used by the engine. vars: