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

1290 lines
36 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-content.h"
#include "detect-uricontent.h"
#include "detect-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->sig_array != NULL) {
SCFree(sghid->sig_array);
sghid->sig_array = NULL;
}
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))
return NULL;
memset(sgh, 0, sizeof(SigGroupHead));
sgh->init = SigGroupHeadInitDataAlloc(size);
if (sgh->init == NULL)
goto error;
return sgh;
error:
SigGroupHeadFree(sgh);
return NULL;
}
/**
* \brief Free a SigGroupHead and its members.
*
* \param sgh Pointer to the SigGroupHead that has to be freed.
*/
void SigGroupHeadFree(SigGroupHead *sgh)
{
if (sgh == NULL)
return;
SCLogDebug("sgh %p", sgh);
if (sgh->match_array != NULL) {
SCFree(sgh->match_array);
sgh->match_array = NULL;
}
if (sgh->non_mpm_other_store_array != NULL) {
SCFree(sgh->non_mpm_other_store_array);
sgh->non_mpm_other_store_array = NULL;
sgh->non_mpm_other_store_cnt = 0;
}
if (sgh->non_mpm_syn_store_array != NULL) {
SCFree(sgh->non_mpm_syn_store_array);
sgh->non_mpm_syn_store_array = NULL;
sgh->non_mpm_syn_store_cnt = 0;
}
sgh->sig_cnt = 0;
if (sgh->init != NULL) {
SigGroupHeadInitDataFree(sgh->init);
sgh->init = NULL;
}
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.
*/
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.
*/
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;
}
static uint16_t SignatureGetMpmPatternLen(const Signature *s, const int list)
{
if (s->sm_lists[list] != NULL && s->mpm_sm != NULL &&
SigMatchListSMBelongsTo(s, s->mpm_sm) == list)
{
DetectContentData *cd = (DetectContentData *)s->mpm_sm->ctx;
return cd->content_len;
}
return 0;
}
/**
* \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.
*/
int SigGroupHeadAppendSig(const DetectEngineCtx *de_ctx, SigGroupHead **sgh,
const Signature *s)
{
if (de_ctx == NULL)
return 0;
/* see if we have a head already */
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)
{
if (sgh == NULL)
return 0;
if (sgh->init->sig_array != NULL)
memset(sgh->init->sig_array, 0, sgh->init->sig_size);
sgh->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->sig_cnt = 0;
for (sig = 0; sig < max_idx + 1; sig++) {
if (sgh->init->sig_array[sig / 8] & (1 << (sig % 8)))
sgh->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
*/
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->match_array != NULL);
sgh->match_array = SCMalloc(sgh->sig_cnt * sizeof(Signature *));
if (sgh->match_array == NULL)
return -1;
memset(sgh->match_array,0, sgh->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->match_array[idx] = s;
idx++;
}
return 0;
}
/**
* \brief Set the need md5 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)
{
Signature *s = NULL;
uint32_t sig = 0;
if (sgh == NULL)
return;
for (sig = 0; sig < sgh->sig_cnt; sig++) {
s = sgh->match_array[sig];
if (s == NULL)
continue;
if (SignatureIsFilemagicInspecting(s)) {
sgh->flags |= SIG_GROUP_HEAD_HAVEFILEMAGIC;
break;
}
}
return;
}
/**
* \brief Get size of the shortest mpm pattern.
*
* \param de_ctx detection engine ctx for the signatures
* \param sgh sig group head to set the flag in
* \param list sm_list to consider
*/
uint16_t SigGroupHeadGetMinMpmSize(DetectEngineCtx *de_ctx,
SigGroupHead *sgh, int list)
{
Signature *s = NULL;
uint32_t sig = 0;
uint16_t min = USHRT_MAX;
if (sgh == NULL)
return 0;
for (sig = 0; sig < sgh->sig_cnt; sig++) {
s = sgh->match_array[sig];
if (s == NULL)
continue;
uint16_t mpm_content_minlen = SignatureGetMpmPatternLen(s, DETECT_SM_LIST_PMATCH);
if (mpm_content_minlen > 0) {
if (mpm_content_minlen < min)
min = mpm_content_minlen;
SCLogDebug("mpm_content_minlen %u", mpm_content_minlen);
}
}
if (min == USHRT_MAX)
min = 0;
SCLogDebug("min mpm size %u", min);
return min;
}
/**
* \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->sig_cnt; sig++) {
s = sgh->match_array[sig];
if (s == NULL)
continue;
if (SignatureIsFilesizeInspecting(s)) {
sgh->flags |= SIG_GROUP_HEAD_HAVEFILESIZE;
break;
}
}
return;
}
/**
* \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 SigGroupHeadSetFileMd5Flag(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
Signature *s = NULL;
uint32_t sig = 0;
if (sgh == NULL)
return;
for (sig = 0; sig < sgh->sig_cnt; sig++) {
s = sgh->match_array[sig];
if (s == NULL)
continue;
if (SignatureIsFileMd5Inspecting(s)) {
sgh->flags |= SIG_GROUP_HEAD_HAVEFILEMD5;
SCLogDebug("sgh %p has filemd5", 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->sig_cnt; sig++) {
s = sgh->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 mpm
* Also updated de_ctx::non_mpm_store_cnt_max to track the highest cnt
*/
int SigGroupHeadBuildNonMpmArray(DetectEngineCtx *de_ctx, SigGroupHead *sgh)
{
Signature *s = NULL;
uint32_t sig = 0;
uint32_t non_mpm = 0;
uint32_t non_mpm_syn = 0;
if (sgh == NULL)
return 0;
BUG_ON(sgh->non_mpm_other_store_array != NULL);
for (sig = 0; sig < sgh->sig_cnt; sig++) {
s = sgh->match_array[sig];
if (s == NULL)
continue;
if (s->mpm_sm == NULL || (s->flags & SIG_FLAG_MPM_NEG)) {
if (!(DetectFlagsSignatureNeedsSynPackets(s))) {
non_mpm++;
}
non_mpm_syn++;
}
}
if (non_mpm == 0 && non_mpm_syn == 0) {
sgh->non_mpm_other_store_array = NULL;
sgh->non_mpm_syn_store_array = NULL;
return 0;
}
if (non_mpm > 0) {
sgh->non_mpm_other_store_array = SCMalloc(non_mpm * sizeof(SignatureNonMpmStore));
BUG_ON(sgh->non_mpm_other_store_array == NULL);
memset(sgh->non_mpm_other_store_array, 0, non_mpm * sizeof(SignatureNonMpmStore));
}
if (non_mpm_syn > 0) {
sgh->non_mpm_syn_store_array = SCMalloc(non_mpm_syn * sizeof(SignatureNonMpmStore));
BUG_ON(sgh->non_mpm_syn_store_array == NULL);
memset(sgh->non_mpm_syn_store_array, 0, non_mpm_syn * sizeof(SignatureNonMpmStore));
}
for (sig = 0; sig < sgh->sig_cnt; sig++) {
s = sgh->match_array[sig];
if (s == NULL)
continue;
if (s->mpm_sm == NULL || (s->flags & SIG_FLAG_MPM_NEG)) {
if (!(DetectFlagsSignatureNeedsSynPackets(s))) {
BUG_ON(sgh->non_mpm_other_store_cnt >= non_mpm);
BUG_ON(sgh->non_mpm_other_store_array == NULL);
sgh->non_mpm_other_store_array[sgh->non_mpm_other_store_cnt].id = s->num;
sgh->non_mpm_other_store_array[sgh->non_mpm_other_store_cnt].mask = s->mask;
sgh->non_mpm_other_store_cnt++;
}
BUG_ON(sgh->non_mpm_syn_store_cnt >= non_mpm_syn);
BUG_ON(sgh->non_mpm_syn_store_array == NULL);
sgh->non_mpm_syn_store_array[sgh->non_mpm_syn_store_cnt].id = s->num;
sgh->non_mpm_syn_store_array[sgh->non_mpm_syn_store_cnt].mask = s->mask;
sgh->non_mpm_syn_store_cnt++;
}
}
/* track highest cnt for any sgh in our de_ctx */
uint32_t max = MAX(sgh->non_mpm_other_store_cnt, sgh->non_mpm_syn_store_cnt);
if (max > de_ctx->non_mpm_store_cnt_max)
de_ctx->non_mpm_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
*/
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:0;)");
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:1;)");
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: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;
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->sig_cnt == 3);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 0) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 1) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 2) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 3) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 4) == 1);
SigGroupHeadFree(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:0;)");
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:1;)");
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: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;
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->sig_cnt == 3);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 0) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 1) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 2) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 3) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 4) == 1);
SigGroupHeadClearSigs(sh);
result &= (sh->sig_cnt == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, sh, 0) == 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);
SigGroupHeadFree(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:0;)");
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:1;)");
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: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;
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->sig_cnt == 3);
result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 0) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 1) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 2) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 3) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, src_sh, 4) == 1);
SigGroupHeadCopySigs(de_ctx, src_sh, &dst_sh);
SigGroupHeadSetSigCnt(dst_sh, 4);
result &= (dst_sh->sig_cnt == 3);
result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 0) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 1) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 2) == 1);
result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 3) == 0);
result &= (SigGroupHeadContainsSigId(de_ctx, dst_sh, 4) == 1);
SigGroupHeadFree(src_sh);
SigGroupHeadFree(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:0;)");
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:1;)");
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: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;
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->match_array[0] == de_ctx->sig_list);
result &= (sh->match_array[1] == de_ctx->sig_list->next->next);
result &= (sh->match_array[2] == de_ctx->sig_list->next->next->next->next);
SigGroupHeadFree(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);
SigGroupHead *sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
if (sgh == NULL) {
goto end;
}
result = 1;
end:
SigCleanSignatures(de_ctx);
DetectEngineCtxFree(de_ctx);
UTHFreePackets(&p, 1);
return result;
}
/**
* \test sig grouping bug.
*/
static int SigGroupHeadTest11(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 = UTHBuildPacketReal(NULL, 0, IPPROTO_TCP, "192.168.1.1", "1.2.3.4", 60000, 80);
if (de_ctx == NULL || p == NULL)
return 0;
s = DetectEngineAppendSig(de_ctx, "alert tcp any 1024: -> any 1024: (content:\"abc\"; sid:1;)");
if (s == NULL) {
goto end;
}
s = DetectEngineAppendSig(de_ctx, "alert tcp any any -> any any (content:\"def\"; http_client_body; sid:2;)");
if (s == NULL) {
goto end;
}
SigGroupBuild(de_ctx);
DetectEngineThreadCtxInit(&th_v, (void *)de_ctx, (void *)&det_ctx);
AddressDebugPrint(&p->dst);
SigGroupHead *sgh = SigMatchSignaturesGetSgh(de_ctx, det_ctx, p);
if (sgh == NULL) {
goto end;
}
/* check if hcbd flag is set in sgh */
if (!(sgh->flags & SIG_GROUP_HEAD_MPM_HCBD)) {
printf("sgh has not SIG_GROUP_HEAD_MPM_HCBD flag set: ");
goto end;
}
/* check if sig 2 is part of the sgh */
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);
UtRegisterTest("SigGroupHeadTest11", SigGroupHeadTest11);
#endif
}