/* 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 <
[email protected]>
*
* Implements the fast_pattern keyword
*/
#include "suricata-common.h"
#include "detect.h"
#include "flow.h"
#include "detect-content.h"
#include "detect-parse.h"
#include "detect-engine.h"
#include "detect-engine-mpm.h"
#include "detect-fast-pattern.h"
#include "util-error.h"
#include "util-debug.h"
#include "util-unittest.h"
#include "util-unittest-helper.h"
#define PARSE_REGEX "^(\\s*only\\s*)|\\s*([0-9]+)\\s*,\\s*([0-9]+)\\s*$"
static pcre *parse_regex = NULL;
static pcre_extra *parse_regex_study = NULL;
static int DetectFastPatternSetup(DetectEngineCtx *, Signature *, const char *);
void DetectFastPatternRegisterTests(void);
/* holds the list of sm match lists that need to be searched for a keyword
* that has fp support */
SCFPSupportSMList *sm_fp_support_smlist_list = NULL;
/**
* \brief Checks if a particular list(Signature->sm_lists[]) is in the list
* of lists that need to be searched for a keyword that has fp support.
*
* \param list_id The list id.
*
* \retval 1 If supported.
* \retval 0 If not.
*/
int FastPatternSupportEnabledForSigMatchList(const DetectEngineCtx *de_ctx,
const int list_id)
{
if (sm_fp_support_smlist_list == NULL)
return 0;
if (list_id == DETECT_SM_LIST_PMATCH)
return 1;
return DetectBufferTypeSupportsMpmGetById(de_ctx, list_id);
#if 0
SCFPSupportSMList *tmp_smlist_fp = sm_fp_support_smlist_list;
while (tmp_smlist_fp != NULL) {
if (tmp_smlist_fp->list_id == list_id)
return 1;
tmp_smlist_fp = tmp_smlist_fp->next;
}
#endif
return 0;
}
/**
* \brief Lets one add a sm list id to be searched for potential fp supported
* keywords later.
*
* \param list_id SM list id.
* \param priority Priority for this list.
*/
void SupportFastPatternForSigMatchList(int list_id, int priority)
{
SCFPSupportSMList *ip = NULL;
/* insertion point - ip */
for (SCFPSupportSMList *tmp = sm_fp_support_smlist_list; tmp != NULL; tmp = tmp->next) {
if (list_id == tmp->list_id) {
SCLogDebug("SM list already registered.");
return;
}
/* We need a strict check to be sure that the current list
* was not already registered
* and other lists with the same priority hide it.
*/
if (priority < tmp->priority)
break;
ip = tmp;
}
if (sm_fp_support_smlist_list == NULL) {
SCFPSupportSMList *new = SCMalloc(sizeof(SCFPSupportSMList));
if (unlikely(new == NULL))
exit(EXIT_FAILURE);
memset(new, 0, sizeof(SCFPSupportSMList));
new->list_id = list_id;
new->priority = priority;
sm_fp_support_smlist_list = new;
return;
}
SCFPSupportSMList *new = SCMalloc(sizeof(SCFPSupportSMList));
if (unlikely(new == NULL))
exit(EXIT_FAILURE);
memset(new, 0, sizeof(SCFPSupportSMList));
new->list_id = list_id;
new->priority = priority;
if (ip == NULL) {
new->next = sm_fp_support_smlist_list;
sm_fp_support_smlist_list = new;
} else {
new->next = ip->next;
ip->next = new;
}
return;
}
/**
* \brief Registers the keywords(SMs) that should be given fp support.
*/
void SupportFastPatternForSigMatchTypes(void)
{
SupportFastPatternForSigMatchList(DETECT_SM_LIST_PMATCH, 3);
/* other types are handled by DetectMpmAppLayerRegister() */
#if 0
SCFPSupportSMList *tmp = sm_fp_support_smlist_list;
while (tmp != NULL) {
printf("%d - %d\n", tmp->list_id, tmp->priority);
tmp = tmp->next;
}
#endif
return;
}
/**
* \brief Registration function for fast_pattern keyword
*/
void DetectFastPatternRegister(void)
{
sigmatch_table[DETECT_FAST_PATTERN].name = "fast_pattern";
sigmatch_table[DETECT_FAST_PATTERN].desc = "force using preceding content in the multi pattern matcher";
sigmatch_table[DETECT_FAST_PATTERN].url = "/rules/prefilter-keywords.html#fast-pattern";
sigmatch_table[DETECT_FAST_PATTERN].Match = NULL;
sigmatch_table[DETECT_FAST_PATTERN].Setup = DetectFastPatternSetup;
sigmatch_table[DETECT_FAST_PATTERN].Free = NULL;
sigmatch_table[DETECT_FAST_PATTERN].RegisterTests = DetectFastPatternRegisterTests;
sigmatch_table[DETECT_FAST_PATTERN].flags |= SIGMATCH_NOOPT;
DetectSetupParseRegexes(PARSE_REGEX, &parse_regex, &parse_regex_study);
}
//static int DetectFastPatternParseArg(
/**
* \brief Configures the previous content context for a fast_pattern modifier
* keyword used in the rule.
*
* \param de_ctx Pointer to the Detection Engine Context.
* \param s Pointer to the Signature to which the current keyword belongs.
* \param null_str Should hold an empty string always.
*
* \retval 0 On success.
* \retval -1 On failure.
*/
static int DetectFastPatternSetup(DetectEngineCtx *de_ctx, Signature *s, const char *arg)
{
#define MAX_SUBSTRINGS 30
int ret = 0, res = 0;
int ov[MAX_SUBSTRINGS];
char arg_substr[128] = "";
DetectContentData *cd = NULL;
SigMatch *pm1 = DetectGetLastSMFromMpmLists(de_ctx, s);
SigMatch *pm2 = DetectGetLastSMFromLists(s, DETECT_CONTENT, -1);
if (pm1 == NULL && pm2 == NULL) {
SCLogError(SC_ERR_INVALID_SIGNATURE, "fast_pattern found inside "
"the rule, without a content context. Please use a "
"content based keyword before using fast_pattern");
return -1;
}
SigMatch *pm = NULL;
if (pm1 && pm2) {
if (pm1->idx > pm2->idx)
pm = pm1;
else
pm = pm2;
} else if (pm1 && !pm2) {
pm = pm1;
} else {
pm = pm2;
}
cd = (DetectContentData *)pm->ctx;
if ((cd->flags & DETECT_CONTENT_NEGATED) &&
((cd->flags & DETECT_CONTENT_DISTANCE) ||
(cd->flags & DETECT_CONTENT_WITHIN) ||
(cd->flags & DETECT_CONTENT_OFFSET) ||
(cd->flags & DETECT_CONTENT_DEPTH))) {
/* we can't have any of these if we are having "only" */
SCLogError(SC_ERR_INVALID_SIGNATURE, "fast_pattern; cannot be "
"used with negated content, along with relative modifiers");
goto error;
}
if (arg == NULL|| strcmp(arg, "") == 0) {
if (cd->flags & DETECT_CONTENT_FAST_PATTERN) {
SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple fast_pattern "
"options for the same content");
goto error;
}
else { /*allow only one content to have fast_pattern modifier*/
uint32_t list_id = 0;
for (list_id = 0; list_id < s->init_data->smlists_array_size; list_id++) {
SigMatch *sm = NULL;
for (sm = s->init_data->smlists[list_id]; sm != NULL; sm = sm->next) {
if (sm->type == DETECT_CONTENT) {
DetectContentData *tmp_cd = (DetectContentData *)sm->ctx;