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/detect-engine-siggroup.c

1213 lines
35 KiB
C

/* Copyright (C) 2007-2013 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 Victor Julien <victor@inliniac.net>
*
* Signature grouping part of the detection engine.
*/
#include "suricata-common.h"
#include "decode.h"
#include "flow-var.h"
#include "app-layer-protos.h"
#include "detect.h"
#include "detect-parse.h"
#include "detect-engine.h"
#include "detect-engine-address.h"
#include "detect-engine-mpm.h"
#include "detect-engine-siggroup.h"
#include "detect-engine-prefilter.h"
#include "detect-content.h"
#include "detect-uricontent.h"
#include "detect-tcp-flags.h"
#include "util-hash.h"
#include "util-hashlist.h"
#include "util-error.h"
#include "util-debug.h"
#include "util-cidr.h"
#include "util-unittest.h"
#include "util-unittest-helper.h"
#include "util-memcmp.h"
/* prototypes */
int SigGroupHeadClearSigs(SigGroupHead *);
void SigGroupHeadInitDataFree(SigGroupHeadInitData *sghid)
{
if (sghid->match_array != NULL) {
SCFree(sghid->match_array);
sghid->match_array = NULL;
}
if (sghid->sig_array != NULL) {
SCFree(sghid->sig_array);
sghid->sig_array = NULL;
}
if (sghid->app_mpms != NULL) {
SCFree(sghid->app_mpms);
}
if (sghid->pkt_mpms != NULL) {
SCFree(sghid->pkt_mpms);
}
PrefilterFreeEnginesList(sghid->tx_engines);
PrefilterFreeEnginesList(sghid->pkt_engines);
PrefilterFreeEnginesList(sghid->payload_engines);
SCFree(sghid);
}
static SigGroupHeadInitData *SigGroupHeadInitDataAlloc(uint32_t size)
{
SigGroupHeadInitData *sghid = SCMalloc(sizeof(SigGroupHeadInitData));
if (unlikely(sghid == NULL))
return NULL;
memset(sghid, 0x00, sizeof(SigGroupHeadInitData));
/* initialize the signature bitarray */
sghid->sig_size = size;
if ( (sghid->sig_array = SCMalloc(sghid->sig_size)) == NULL)
goto error;
memset(sghid->sig_array, 0, sghid->sig_size);
return sghid;
error:
SigGroupHeadInitDataFree(sghid);
return NULL;
}
void SigGroupHeadStore(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
void *ptmp;
//printf("de_ctx->sgh_array_cnt %u, de_ctx->sgh_array_size %u, de_ctx->sgh_array %p\n", de_ctx->sgh_array_cnt, de_ctx->sgh_array_size, de_ctx->sgh_array);
if (de_ctx->sgh_array_cnt < de_ctx->sgh_array_size) {
de_ctx->sgh_array[de_ctx->sgh_array_cnt] = sgh;
} else {
int increase = 16;
ptmp = SCRealloc(de_ctx->sgh_array,
sizeof(SigGroupHead *) * (increase + de_ctx->sgh_array_size));
if (ptmp == NULL) {
SCFree(de_ctx->sgh_array);
de_ctx->sgh_array = NULL;
return;
}
de_ctx->sgh_array = ptmp;
de_ctx->sgh_array_size += increase;
de_ctx->sgh_array[de_ctx->sgh_array_cnt] = sgh;
}
de_ctx->sgh_array_cnt++;
}
/**
* \brief Alloc a SigGroupHead and its signature bit_array.
*
* \param size Size of the sig_array that has to be created for this
* SigGroupHead.
*
* \retval sgh Pointer to the newly init SigGroupHead on success; or NULL in
* case of error.
*/
static SigGroupHead *SigGroupHeadAlloc(const DetectEngineCtx *de_ctx, uint32_t size)
{
SigGroupHead *sgh = SCMalloc(sizeof(SigGroupHead));
if (unlikely(sgh == NULL))
16 years ago
return NULL;
memset(sgh, 0, sizeof(SigGroupHead));
sgh->init = SigGroupHeadInitDataAlloc(size);
if (sgh->init == NULL)
goto error;
16 years ago
return sgh;
16 years ago
error:
SigGroupHeadFree(de_ctx, sgh);
16 years ago
return NULL;
}
/**
* \brief Free a SigGroupHead and its members.
*
* \param sgh Pointer to the SigGroupHead that has to be freed.
*/
void SigGroupHeadFree(const DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
16 years ago
if (sgh == NULL)
return;
SCLogDebug("sgh %p", sgh);
if (sgh->non_pf_other_store_array != NULL) {
SCFree(sgh->non_pf_other_store_array);
sgh->non_pf_other_store_array = NULL;
sgh->non_pf_other_store_cnt = 0;
}
if (sgh->non_pf_syn_store_array != NULL) {
SCFree(sgh->non_pf_syn_store_array);
sgh->non_pf_syn_store_array = NULL;
sgh->non_pf_syn_store_cnt = 0;
}
if (sgh->init != NULL) {
SigGroupHeadInitDataFree(sgh->init);
sgh->init = NULL;
}
PrefilterCleanupRuleGroup(de_ctx, sgh);
SCFree(sgh);
return;
}
/**
* \brief The hash function to be the used by the hash table -
* DetectEngineCtx->sgh_hash_table.
*
* \param ht Pointer to the hash table.
* \param data Pointer to the SigGroupHead.
* \param datalen Not used in our case.
*
* \retval hash The generated hash value.
*/
static uint32_t SigGroupHeadHashFunc(HashListTable *ht, void *data, uint16_t datalen)
{
SigGroupHead *sgh = (SigGroupHead *)data;
uint32_t hash = 0;
uint32_t b = 0;
SCLogDebug("hashing sgh %p", sgh);
for (b = 0; b < sgh->init->sig_size; b++)
hash += sgh->init->sig_array[b];
hash %= ht->array_size;
SCLogDebug("hash %"PRIu32" (sig_size %"PRIu32")", hash, sgh->init->sig_size);
return hash;
}
/**
* \brief The Compare function to be used by the SigGroupHead hash table -
* DetectEngineCtx->sgh_hash_table.
*
* \param data1 Pointer to the first SigGroupHead.
* \param len1 Not used.
* \param data2 Pointer to the second SigGroupHead.
* \param len2 Not used.
*
* \retval 1 If the 2 SigGroupHeads sent as args match.
* \retval 0 If the 2 SigGroupHeads sent as args do not match.
*/
static char SigGroupHeadCompareFunc(void *data1, uint16_t len1, void *data2,
uint16_t len2)
{
SigGroupHead *sgh1 = (SigGroupHead *)data1;
SigGroupHead *sgh2 = (SigGroupHead *)data2;
if (data1 == NULL || data2 == NULL)
return 0;
if (sgh1->init->sig_size != sgh2->init->sig_size)
return 0;
if (SCMemcmp(sgh1->init->sig_array, sgh2->init->sig_array, sgh1->init->sig_size) != 0)
return 0;
return 1;
}
/**
* \brief Initializes the hash table in the detection engine context to hold the
* SigGroupHeads.
*
* \param de_ctx Pointer to the detection engine context.
*
* \retval 0 On success.
* \retval -1 On failure.
*/
int SigGroupHeadHashInit(DetectEngineCtx *de_ctx)
{
de_ctx->sgh_hash_table = HashListTableInit(4096, SigGroupHeadHashFunc,
SigGroupHeadCompareFunc, NULL);
if (de_ctx->sgh_hash_table == NULL)
goto error;
return 0;
error:
return -1;
}
/**
* \brief Adds a SigGroupHead to the detection engine context SigGroupHead
* hash table.
*
* \param de_ctx Pointer to the detection engine context.
* \param sgh Pointer to the SigGroupHead.
*
* \retval ret 0 on Successfully adding the SigGroupHead; -1 on failure.
*/
int SigGroupHeadHashAdd(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
int ret = HashListTableAdd(de_ctx->sgh_hash_table, (void *)sgh, 0);
return ret;
}
int SigGroupHeadHashRemove(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
return HashListTableRemove(de_ctx->sgh_hash_table, (void *)sgh, 0);
}
/**
* \brief Used to lookup a SigGroupHead hash from the detection engine context
* SigGroupHead hash table.
*
* \param de_ctx Pointer to the detection engine context.
* \param sgh Pointer to the SigGroupHead.
*
* \retval rsgh On success a pointer to the SigGroupHead if the SigGroupHead is
* found in the hash table; NULL on failure.
*/
SigGroupHead *SigGroupHeadHashLookup(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
SCEnter();
SigGroupHead *rsgh = HashListTableLookup(de_ctx->sgh_hash_table,
(void *)sgh, 0);
SCReturnPtr(rsgh, "SigGroupHead");
}
/**
* \brief Frees the hash table - DetectEngineCtx->sgh_hash_table, allocated by
* SigGroupHeadHashInit() function.
*
* \param de_ctx Pointer to the detection engine context.
*/
void SigGroupHeadHashFree(DetectEngineCtx *de_ctx)
{
if (de_ctx->sgh_hash_table == NULL)
return;
HashListTableFree(de_ctx->sgh_hash_table);
de_ctx->sgh_hash_table = NULL;
return;
}
/**
* \brief Add a Signature to a SigGroupHead.
*
* \param de_ctx Pointer to the detection engine context.
* \param sgh Pointer to a SigGroupHead. Can be NULL also.
* \param s Pointer to the Signature that has to be added to the
* SigGroupHead.
*
* \retval 0 On success.
* \retval -1 On failure.
16 years ago
*/
int SigGroupHeadAppendSig(const DetectEngineCtx *de_ctx, SigGroupHead **sgh,
const Signature *s)
{
if (de_ctx == NULL)
return 0;
/* see if we have a head already */
16 years ago
if (*sgh == NULL) {
*sgh = SigGroupHeadAlloc(de_ctx, DetectEngineGetMaxSigId(de_ctx) / 8 + 1);
if (*sgh == NULL)
goto error;
}
/* enable the sig in the bitarray */
(*sgh)->init->sig_array[s->num / 8] |= 1 << (s->num % 8);
return 0;
error:
return -1;
}
/**
* \brief Clears the bitarray holding the sids for this SigGroupHead.
*
* \param sgh Pointer to the SigGroupHead.
*
* \retval 0 Always.
*/
int SigGroupHeadClearSigs(SigGroupHead *sgh)
{
16 years ago
if (sgh == NULL)
return 0;
if (sgh->init->sig_array != NULL)
memset(sgh->init->sig_array, 0, sgh->init->sig_size);
sgh->init->sig_cnt = 0;
return 0;
}
/**
* \brief Copies the bitarray holding the sids from the source SigGroupHead to
* the destination SigGroupHead.
*
* \param de_ctx Pointer to the detection engine context.
* \param src Pointer to the source SigGroupHead.
* \param dst Pointer to the destination SigGroupHead.
*
* \retval 0 On success.
* \retval -1 On failure.
*/
int SigGroupHeadCopySigs(DetectEngineCtx *de_ctx, SigGroupHead *src, SigGroupHead **dst)
{
uint32_t idx = 0;
if (src == NULL || de_ctx == NULL)
return 0;
if (*dst == NULL) {
*dst = SigGroupHeadAlloc(de_ctx, DetectEngineGetMaxSigId(de_ctx) / 8 + 1);
if (*dst == NULL)
goto error;
}
/* do the copy */
for (idx = 0; idx < src->init->sig_size; idx++)
(*dst)->init->sig_array[idx] = (*dst)->init->sig_array[idx] | src->init->sig_array[idx];
if (src->init->whitelist)
(*dst)->init->whitelist = MAX((*dst)->init->whitelist, src->init->whitelist);
return 0;
error:
return -1;
}
/**
* \brief Updates the SigGroupHead->sig_cnt with the total count of all the
* Signatures present in this SigGroupHead.
*
* \param sgh Pointer to the SigGroupHead.
* \param max_idx Maximum sid of the all the Signatures present in this
* SigGroupHead.
*/
void SigGroupHeadSetSigCnt(SigGroupHead *sgh, uint32_t max_idx)
{
uint32_t sig;
sgh->init->sig_cnt = 0;
for (sig = 0; sig < max_idx + 1; sig++) {
if (sgh->init->sig_array[sig / 8] & (1 << (sig % 8)))
sgh->init->sig_cnt++;
}
return;
}
void SigGroupHeadSetProtoAndDirection(SigGroupHead *sgh,
uint8_t ipproto, int dir)
{
if (sgh && sgh->init) {
SCLogDebug("setting proto %u and dir %d on sgh %p", ipproto, dir, sgh);
sgh->init->protos[ipproto] = 1;
sgh->init->direction |= dir;
}
}
/**
* \brief Helper function used to print the list of sids for the Signatures
* present in this SigGroupHead.
*
* \param de_ctx Pointer to the detection engine context.
* \param sgh Pointer to the SigGroupHead.
*/
void SigGroupHeadPrintSigs(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
SCEnter();
if (sgh == NULL) {
SCReturn;
}
uint32_t u;
SCLogDebug("The Signatures present in this SigGroupHead are: ");
for (u = 0; u < (sgh->init->sig_size * 8); u++) {
if (sgh->init->sig_array[u / 8] & (1 << (u % 8))) {
SCLogDebug("%" PRIu32, u);
printf("s->num %"PRIu32" ", u);
}
}
SCReturn;
}
/**
* \brief Create an array with all the internal ids of the sigs that this
* sig group head will check for.
*
* \param de_ctx Pointer to the detection engine context.
* \param sgh Pointer to the SigGroupHead.
* \param max_idx The maximum value of the sid in the SigGroupHead arg.
*
* \retval 0 success
* \retval -1 error
16 years ago
*/
int SigGroupHeadBuildMatchArray(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
uint32_t max_idx)
{
Signature *s = NULL;
uint32_t idx = 0;
uint32_t sig = 0;
if (sgh == NULL)
return 0;
BUG_ON(sgh->init->match_array != NULL);
sgh->init->match_array = SCMalloc(sgh->init->sig_cnt * sizeof(Signature *));
if (sgh->init->match_array == NULL)
return -1;
memset(sgh->init->match_array, 0, sgh->init->sig_cnt * sizeof(Signature *));
for (sig = 0; sig < max_idx + 1; sig++) {
if (!(sgh->init->sig_array[(sig / 8)] & (1 << (sig % 8))) )
continue;
s = de_ctx->sig_array[sig];
if (s == NULL)
continue;
sgh->init->match_array[idx] = s;
idx++;
}
return 0;
}
/**
* \brief Set the need magic flag in the sgh.
*
* \param de_ctx detection engine ctx for the signatures
* \param sgh sig group head to set the flag in
*/
void SigGroupHeadSetFilemagicFlag(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
#ifdef HAVE_MAGIC
Signature *s = NULL;
uint32_t sig = 0;
if (sgh == NULL)
return;
for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
s = sgh->init->match_array[sig];
if (s == NULL)
continue;
if (SignatureIsFilemagicInspecting(s)) {
sgh->flags |= SIG_GROUP_HEAD_HAVEFILEMAGIC;
break;
}
}
#endif
return;
}
/**
* \brief Set the need size flag in the sgh.
*
* \param de_ctx detection engine ctx for the signatures
* \param sgh sig group head to set the flag in
*/
void SigGroupHeadSetFilesizeFlag(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
Signature *s = NULL;
uint32_t sig = 0;
if (sgh == NULL)
return;
for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
s = sgh->init->match_array[sig];
if (s == NULL)
continue;
if (SignatureIsFilesizeInspecting(s)) {
sgh->flags |= SIG_GROUP_HEAD_HAVEFILESIZE;
break;
}
}
return;
}
/**
* \brief Set the need hash flag in the sgh.
*
* \param de_ctx detection engine ctx for the signatures
* \param sgh sig group head to set the flag in
*/
void SigGroupHeadSetFileHashFlag(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
Signature *s = NULL;
uint32_t sig = 0;
if (sgh == NULL)
return;
for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
s = sgh->init->match_array[sig];
if (s == NULL)
continue;
if (SignatureIsFileMd5Inspecting(s)) {
sgh->flags |= SIG_GROUP_HEAD_HAVEFILEMD5;
SCLogDebug("sgh %p has filemd5", sgh);
break;
}
if (SignatureIsFileSha1Inspecting(s)) {
sgh->flags |= SIG_GROUP_HEAD_HAVEFILESHA1;
SCLogDebug("sgh %p has filesha1", sgh);
break;
}
if (SignatureIsFileSha256Inspecting(s)) {
sgh->flags |= SIG_GROUP_HEAD_HAVEFILESHA256;
SCLogDebug("sgh %p has filesha256", sgh);
break;
}
}
return;
}
/**
* \brief Set the filestore_cnt in the sgh.
*
* \param de_ctx detection engine ctx for the signatures
* \param sgh sig group head to set the counter in
*/
void SigGroupHeadSetFilestoreCount(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
Signature *s = NULL;
uint32_t sig = 0;
if (sgh == NULL)
return;
for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
s = sgh->init->match_array[sig];
if (s == NULL)
continue;
if (SignatureIsFilestoring(s)) {
sgh->filestore_cnt++;
}
}
return;
}
/** \brief build an array of rule id's for sigs with no prefilter
* Also updated de_ctx::non_pf_store_cnt_max to track the highest cnt
*/
int SigGroupHeadBuildNonPrefilterArray(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
Signature *s = NULL;
uint32_t sig = 0;
uint32_t non_pf = 0;
uint32_t non_pf_syn = 0;
if (sgh == NULL)
return 0;
BUG_ON(sgh->non_pf_other_store_array != NULL);
for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
s = sgh->init->match_array[sig];
if (s == NULL)
continue;
if (!(s->flags & SIG_FLAG_PREFILTER) || (s->flags & SIG_FLAG_MPM_NEG)) {
if (!(DetectFlagsSignatureNeedsSynPackets(s))) {
non_pf++;
}
non_pf_syn++;
}
}
if (non_pf == 0 && non_pf_syn == 0) {
sgh->non_pf_other_store_array = NULL;
sgh->non_pf_syn_store_array = NULL;
return 0;
}
if (non_pf > 0) {
sgh->non_pf_other_store_array = SCMalloc(non_pf * sizeof(SignatureNonPrefilterStore));
BUG_ON(sgh->non_pf_other_store_array == NULL);
memset(sgh->non_pf_other_store_array, 0, non_pf * sizeof(SignatureNonPrefilterStore));
}
if (non_pf_syn > 0) {
sgh->non_pf_syn_store_array = SCMalloc(non_pf_syn * sizeof(SignatureNonPrefilterStore));
BUG_ON(sgh->non_pf_syn_store_array == NULL);
memset(sgh->non_pf_syn_store_array, 0, non_pf_syn * sizeof(SignatureNonPrefilterStore));
}
for (sig = 0; sig < sgh->init->sig_cnt; sig++) {
s = sgh->init->match_array[sig];
if (s == NULL)
continue;
if (!(s->flags & SIG_FLAG_PREFILTER) || (s->flags & SIG_FLAG_MPM_NEG)) {
if (!(DetectFlagsSignatureNeedsSynPackets(s))) {
BUG_ON(sgh->non_pf_other_store_cnt >= non_pf);
BUG_ON(sgh->non_pf_other_store_array == NULL);
sgh->non_pf_other_store_array[sgh->non_pf_other_store_cnt].id = s->num;
sgh->non_pf_other_store_array[sgh->non_pf_other_store_cnt].mask = s->mask;
detect: rewrite of the detect engine Use per tx detect_flags to track prefilter. Detect flags are used for 2 things: 1. marking tx as fully inspected 2. tracking already run prefilter (incl mpm) engines This supercedes the MpmIDs API for directionless tracking of the prefilter engines. When we have no SGH we have to flag the txs that are 'complete' as inspected as well. Special handling for the stream engine: If a rule mixes TX inspection and STREAM inspection, we can encounter the case where the rule is evaluated against multiple transactions during a single inspection run. As the stream data is exactly the same for each of those runs, it's wasteful to rerun inspection of the stream portion of the rule. This patch enables caching of the stream 'inspect engine' result in the local 'RuleMatchCandidateTx' array. This is valid only during the live of a single inspection run. Remove stateful inspection from 'mask' (SignatureMask). The mask wasn't used in most cases for those rules anyway, as there we rely on the prefilter. Add a alproto check to catch the remaining cases. When building the active non-mpm/non-prefilter list check not just the mask, but also the alproto. This especially helps stateful rules with negated mpm. Simplify AppLayerParserHasDecoderEvents usage in detection to only return true if protocol detection events are set. Other detection is done in inspect engines. Move rule group lookup and handling into it's own function. Handle 'post lookup' tasks immediately, instead of after the first detect run. The tasks were independent of the initial detection. Many cleanups and much refactoring.
8 years ago
sgh->non_pf_other_store_array[sgh->non_pf_other_store_cnt].alproto = s->alproto;
sgh->non_pf_other_store_cnt++;
}
BUG_ON(sgh->non_pf_syn_store_cnt >= non_pf_syn);
BUG_ON(sgh->non_pf_syn_store_array == NULL);
sgh->non_pf_syn_store_array[sgh->non_pf_syn_store_cnt].id = s->num;
sgh->non_pf_syn_store_array[sgh->non_pf_syn_store_cnt].mask = s->mask;
detect: rewrite of the detect engine Use per tx detect_flags to track prefilter. Detect flags are used for 2 things: 1. marking tx as fully inspected 2. tracking already run prefilter (incl mpm) engines This supercedes the MpmIDs API for directionless tracking of the prefilter engines. When we have no SGH we have to flag the txs that are 'complete' as inspected as well. Special handling for the stream engine: If a rule mixes TX inspection and STREAM inspection, we can encounter the case where the rule is evaluated against multiple transactions during a single inspection run. As the stream data is exactly the same for each of those runs, it's wasteful to rerun inspection of the stream portion of the rule. This patch enables caching of the stream 'inspect engine' result in the local 'RuleMatchCandidateTx' array. This is valid only during the live of a single inspection run. Remove stateful inspection from 'mask' (SignatureMask). The mask wasn't used in most cases for those rules anyway, as there we rely on the prefilter. Add a alproto check to catch the remaining cases. When building the active non-mpm/non-prefilter list check not just the mask, but also the alproto. This especially helps stateful rules with negated mpm. Simplify AppLayerParserHasDecoderEvents usage in detection to only return true if protocol detection events are set. Other detection is done in inspect engines. Move rule group lookup and handling into it's own function. Handle 'post lookup' tasks immediately, instead of after the first detect run. The tasks were independent of the initial detection. Many cleanups and much refactoring.
8 years ago
sgh->non_pf_syn_store_array[sgh->non_pf_syn_store_cnt].alproto = s->alproto;
sgh->non_pf_syn_store_cnt++;
}
}
/* track highest cnt for any sgh in our de_ctx */
uint32_t max = MAX(sgh->non_pf_other_store_cnt, sgh->non_pf_syn_store_cnt);
if (max > de_ctx->non_pf_store_cnt_max)
de_ctx->non_pf_store_cnt_max = max;
return 0;
}
/**
* \brief Check if a SigGroupHead contains a Signature, whose sid is sent as an
* argument.
*
* \param de_ctx Pointer to the detection engine context.
* \param sgh Pointer to the SigGroupHead that has to be checked for the
* presence of a Signature.
* \param sid The Signature id(sid) that has to be checked in the SigGroupHead.
*
* \retval 1 On successfully finding the sid in the SigGroupHead.
* \retval 0 If the sid is not found in the SigGroupHead
16 years ago
*/
int SigGroupHeadContainsSigId(DetectEngineCtx *de_ctx, SigGroupHead *sgh,
uint32_t sid)
{
SCEnter();
uint32_t sig = 0;
Signature *s = NULL;
uint32_t max_sid = DetectEngineGetMaxSigId(de_ctx);
if (sgh == NULL) {
SCReturnInt(0);
}
for (sig = 0; sig < max_sid; sig++) {
if (sgh->init->sig_array == NULL) {
SCReturnInt(0);
}
/* Check if the SigGroupHead has an entry for the sid */
if ( !(sgh->init->sig_array[sig / 8] & (1 << (sig % 8))) )
continue;
/* If we have reached here, we have an entry for sid in the SigGrouHead.
* Retrieve the Signature from the detection engine context */
s = de_ctx->sig_array[sig];
if (s == NULL)
continue;
/* If the retrieved Signature matches the sid arg, we have a match */
if (s->id == sid) {
SCReturnInt(1);
}
}
SCReturnInt(0);
}
/*----------------------------------Unittests---------------------------------*/
#ifdef UNITTESTS
int SigAddressPrepareStage1(DetectEngineCtx *);
/**
* \test Check if a SigGroupHead hash table is properly allocated and
* deallocated when calling SigGroupHeadHashInit() and
* SigGroupHeadHashFree() respectively.
*/
static int SigGroupHeadTest03(void)
{
int result = 1;
DetectEngineCtx de_ctx;
SigGroupHeadHashInit(&de_ctx);
result &= (de_ctx.sgh_hash_table != NULL);
SigGroupHeadHashFree(&de_ctx);
result &= (de_ctx.sgh_hash_table == NULL);
return result;
}
/**
* \test Check if a SigGroupHeadAppendSig() correctly appends a sid to a
* SigGroupHead() and SigGroupHeadContainsSigId() correctly indicates
* the presence of a sid.
*/
static int SigGroupHeadTest06(void)
{
int result = 1;
SigGroupHead *sh = NULL;
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
Signature *prev_sig = NULL;
if (de_ctx == NULL)
return 0;
de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:1;)");
if (de_ctx->sig_list == NULL) {
result = 0;
goto end;
}
prev_sig = de_ctx->sig_list;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:2;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:3;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:4;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:5;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
SigAddressPrepareStage1(de_ctx);
SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list);
SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next);
SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next->next->next);
SigGroupHeadSetSigCnt(sh, 4);
result &= (sh->init->sig_cnt == 3);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 1) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 2) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 3) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 4) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 5) == 1);
SigGroupHeadFree(de_ctx, sh);
end:
SigCleanSignatures(de_ctx);
DetectEngineCtxFree(de_ctx);
return result;
}
/**
* \test Check if a SigGroupHeadAppendSig(), correctly appends a sid to a
* SigGroupHead() and SigGroupHeadContainsSigId(), correctly indicates
* the presence of a sid and SigGroupHeadClearSigs(), correctly clears
* the SigGroupHead->sig_array and SigGroupHead->sig_cnt.
*/
static int SigGroupHeadTest07(void)
{
int result = 1;
SigGroupHead *sh = NULL;
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
Signature *prev_sig = NULL;
if (de_ctx == NULL)
return 0;
de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:1;)");
if (de_ctx->sig_list == NULL) {
result = 0;
goto end;
}
prev_sig = de_ctx->sig_list;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:2;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:3;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:4;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:5;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
SigAddressPrepareStage1(de_ctx);
SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list);
SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next);
SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next->next->next);
SigGroupHeadSetSigCnt(sh, 4);
result &= (sh->init->sig_cnt == 3);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 1) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 2) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 3) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 4) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 5) == 1);
SigGroupHeadClearSigs(sh);
result &= (sh->init->sig_cnt == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 1) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 2) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 3) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 4) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 5) == 0);
SigGroupHeadFree(de_ctx, sh);
end:
SigCleanSignatures(de_ctx);
DetectEngineCtxFree(de_ctx);
return result;
}
/**
* \test Check if SigGroupHeadCopySigs(), correctly copies the sig_array from
* the source to the destination SigGroupHead.
*/
static int SigGroupHeadTest08(void)
{
int result = 1;
SigGroupHead *src_sh = NULL;
SigGroupHead *dst_sh = NULL;
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
Signature *prev_sig = NULL;
if (de_ctx == NULL)
return 0;
de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:1;)");
if (de_ctx->sig_list == NULL) {
result = 0;
goto end;
}
prev_sig = de_ctx->sig_list;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:2;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:3;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:4;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:5;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
SigAddressPrepareStage1(de_ctx);
SigGroupHeadAppendSig(de_ctx, &src_sh, de_ctx->sig_list);
SigGroupHeadAppendSig(de_ctx, &src_sh, de_ctx->sig_list->next->next);
SigGroupHeadAppendSig(de_ctx, &src_sh, de_ctx->sig_list->next->next->next->next);
SigGroupHeadSetSigCnt(src_sh, 4);
result &= (src_sh->init->sig_cnt == 3);
result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 1) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 2) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 3) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 4) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 5) == 1);
SigGroupHeadCopySigs(de_ctx, src_sh, &dst_sh);
SigGroupHeadSetSigCnt(dst_sh, 4);
result &= (dst_sh->init->sig_cnt == 3);
result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 1) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 2) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 3) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 4) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 5) == 1);
SigGroupHeadFree(de_ctx, src_sh);
SigGroupHeadFree(de_ctx, dst_sh);
end:
SigCleanSignatures(de_ctx);
DetectEngineCtxFree(de_ctx);
return result;
}
/**
* \test Check if SigGroupHeadBuildMatchArray(), correctly updates the
* match array with the sids.
*/
static int SigGroupHeadTest09(void)
{
int result = 1;
SigGroupHead *sh = NULL;
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
Signature *prev_sig = NULL;
if (de_ctx == NULL)
return 0;
de_ctx->sig_list = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:1;)");
if (de_ctx->sig_list == NULL) {
result = 0;
goto end;
}
prev_sig = de_ctx->sig_list;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:2;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:3;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:4;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
prev_sig->next = SigInit(de_ctx, "alert tcp any any -> any any "
"(msg:\"SigGroupHead tests\"; content:\"test1\"; "
"content:\"test2\"; content:\"test3\"; sid:5;)");
if (prev_sig->next == NULL) {
result = 0;
goto end;
}
prev_sig = prev_sig->next;
SigAddressPrepareStage1(de_ctx);
SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list);
SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next);
SigGroupHeadAppendSig(de_ctx, &sh, de_ctx->sig_list->next->next->next->next);
SigGroupHeadSetSigCnt(sh, 4);
SigGroupHeadBuildMatchArray(de_ctx, sh, 4);
result &= (sh->init->match_array[0] == de_ctx->sig_list);
result &= (sh->init->match_array[1] == de_ctx->sig_list->next->next);
result &= (sh->init->match_array[2] == de_ctx->sig_list->next->next->next->next);
SigGroupHeadFree(de_ctx, sh);
end:
SigCleanSignatures(de_ctx);
DetectEngineCtxFree(de_ctx);
return result;
}
/**
* \test ICMP(?) sig grouping bug.
*/
static int SigGroupHeadTest10(void)
{
int result = 0;
DetectEngineCtx *de_ctx = DetectEngineCtxInit();
Signature *s = NULL;
Packet *p = NULL;
DetectEngineThreadCtx *det_ctx = NULL;
ThreadVars th_v;
memset(&th_v, 0, sizeof(ThreadVars));
p = UTHBuildPacketSrcDst(NULL, 0, IPPROTO_ICMP, "192.168.1.1", "1.2.3.4");
p->icmpv4h->type = 5;
p->icmpv4h->code = 1;
/* originally ip's were
p.src.addr_data32[0] = 0xe08102d3;
p.dst.addr_data32[0] = 0x3001a8c0;
*/
if (de_ctx == NULL)
return 0;
s = DetectEngineAppendSig(de_ctx, "alert icmp 192.168.0.0/16 any -> any any (icode:>1; itype:11; sid:1; rev:1;)");
if (s == NULL) {
goto end;
}
s = DetectEngineAppendSig(de_ctx, "alert icmp any any -> 192.168.0.0/16 any (icode:1; itype:5; sid:2; rev:1;)");
if (s == NULL) {
goto end;
}
SigGroupBuild(de_ctx);
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
AddressDebugPrint(&p->dst);
const SigGroupHead *sgh = SigMatchSignaturesGetSgh(de_ctx, p);
if (sgh == NULL) {
goto end;
}
result = 1;
end:
SigCleanSignatures(de_ctx);
DetectEngineCtxFree(de_ctx);
UTHFreePackets(&p, 1);
return result;
}
#endif
void SigGroupHeadRegisterTests(void)
{
#ifdef UNITTESTS
UtRegisterTest("SigGroupHeadTest03", SigGroupHeadTest03);
UtRegisterTest("SigGroupHeadTest06", SigGroupHeadTest06);
UtRegisterTest("SigGroupHeadTest07", SigGroupHeadTest07);
UtRegisterTest("SigGroupHeadTest08", SigGroupHeadTest08);
UtRegisterTest("SigGroupHeadTest09", SigGroupHeadTest09);
UtRegisterTest("SigGroupHeadTest10", SigGroupHeadTest10);
#endif
}