diff --git a/doc/userguide/rules/payload-keywords.rst b/doc/userguide/rules/payload-keywords.rst index e2e4d42a6b..1e71540254 100644 --- a/doc/userguide/rules/payload-keywords.rst +++ b/doc/userguide/rules/payload-keywords.rst @@ -126,6 +126,24 @@ Example: .. image:: payload-keywords/content4.png +startswith +---------- + +The ``startswith`` keyword is similar to ``depth``. It takes no arguments +and must follow a ``content`` keyword. It modifies the ``content`` to match +exactly at the start of a buffer. + +Example:: + + content:"GET|20|"; startswith; + +``startswith`` is a short hand notation for:: + + content:"GET|20|"; depth:4; offset:0; + +``startswith`` cannot be mixed with ``depth``, ``offset``, ``within`` or +``distance`` for the same pattern. + offset ------ diff --git a/src/detect-content.h b/src/detect-content.h index 7335d279cc..3bcf7c83bf 100644 --- a/src/detect-content.h +++ b/src/detect-content.h @@ -56,6 +56,7 @@ #define DETECT_CONTENT_WITHIN_NEXT BIT_U32(17) #define DETECT_CONTENT_DISTANCE_NEXT BIT_U32(18) +#define DETECT_CONTENT_STARTS_WITH BIT_U32(19) /** a relative match to this content is next, used in matching phase */ #define DETECT_CONTENT_RELATIVE_NEXT (DETECT_CONTENT_WITHIN_NEXT|DETECT_CONTENT_DISTANCE_NEXT) diff --git a/src/detect-depth.c b/src/detect-depth.c index 3c8af60462..0e7b4007a6 100644 --- a/src/detect-depth.c +++ b/src/detect-depth.c @@ -42,6 +42,7 @@ #include "util-debug.h" static int DetectDepthSetup (DetectEngineCtx *, Signature *, const char *); +static int DetectStartsWithSetup (DetectEngineCtx *, Signature *, const char *); void DetectDepthRegister (void) { @@ -52,6 +53,12 @@ void DetectDepthRegister (void) sigmatch_table[DETECT_DEPTH].Setup = DetectDepthSetup; sigmatch_table[DETECT_DEPTH].Free = NULL; sigmatch_table[DETECT_DEPTH].RegisterTests = NULL; + + sigmatch_table[DETECT_STARTS_WITH].name = "startswith"; + sigmatch_table[DETECT_STARTS_WITH].desc = "pattern must be at the start of a buffer (same as 'depth:';)"; + sigmatch_table[DETECT_STARTS_WITH].url = DOC_URL DOC_VERSION "/rules/payload-keywords.html#startswith"; + sigmatch_table[DETECT_STARTS_WITH].Setup = DetectStartsWithSetup; + sigmatch_table[DETECT_STARTS_WITH].flags |= SIGMATCH_NOOPT; } static int DetectDepthSetup (DetectEngineCtx *de_ctx, Signature *s, const char *depthstr) @@ -128,3 +135,56 @@ static int DetectDepthSetup (DetectEngineCtx *de_ctx, Signature *s, const char * end: return ret; } + +static int DetectStartsWithSetup (DetectEngineCtx *de_ctx, Signature *s, const char *unused) +{ + SigMatch *pm = NULL; + int ret = -1; + + /* retrieve the sm to apply the depth against */ + pm = DetectGetLastSMFromLists(s, DETECT_CONTENT, -1); + if (pm == NULL) { + SCLogError(SC_ERR_DEPTH_MISSING_CONTENT, "startswith needs a " + "preceding content option"); + goto end; + } + + /* verify other conditions. */ + DetectContentData *cd = (DetectContentData *)pm->ctx; + + if (cd->flags & DETECT_CONTENT_DEPTH) { + SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple " + "depth/startswith settings for the same content"); + goto end; + } + if ((cd->flags & DETECT_CONTENT_WITHIN) || (cd->flags & DETECT_CONTENT_DISTANCE)) { + SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use a relative " + "keyword like within/distance with a absolute " + "relative keyword like depth/offset for the same " + "content."); + goto end; + } + if (cd->flags & DETECT_CONTENT_NEGATED && cd->flags & DETECT_CONTENT_FAST_PATTERN) { + SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative " + "negated keyword set along with a fast_pattern"); + goto end; + } + if (cd->flags & DETECT_CONTENT_FAST_PATTERN_ONLY) { + SCLogError(SC_ERR_INVALID_SIGNATURE, "can't have a relative " + "keyword set along with a fast_pattern:only;"); + goto end; + } + if (cd->flags & DETECT_CONTENT_OFFSET) { + SCLogError(SC_ERR_INVALID_SIGNATURE, "can't mix offset " + "with startswith"); + goto end; + } + + cd->depth = cd->content_len; + cd->flags |= DETECT_CONTENT_DEPTH; + cd->flags |= DETECT_CONTENT_STARTS_WITH; + + ret = 0; + end: + return ret; +} diff --git a/src/detect-engine-register.h b/src/detect-engine-register.h index 09f88cc4b1..4b6310d3cd 100644 --- a/src/detect-engine-register.h +++ b/src/detect-engine-register.h @@ -60,6 +60,7 @@ enum { DETECT_URICONTENT, DETECT_PCRE, DETECT_DEPTH, + DETECT_STARTS_WITH, DETECT_DISTANCE, DETECT_WITHIN, DETECT_OFFSET, diff --git a/src/detect-offset.c b/src/detect-offset.c index 31a835adf0..bed23a2d1b 100644 --- a/src/detect-offset.c +++ b/src/detect-offset.c @@ -69,6 +69,10 @@ int DetectOffsetSetup (DetectEngineCtx *de_ctx, Signature *s, const char *offset /* verify other conditions */ DetectContentData *cd = (DetectContentData *)pm->ctx; + if (cd->flags & DETECT_CONTENT_STARTS_WITH) { + SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use offset with startswith"); + goto end; + } if (cd->flags & DETECT_CONTENT_OFFSET) { SCLogError(SC_ERR_INVALID_SIGNATURE, "can't use multiple offsets for the same content. "); goto end; diff --git a/src/tests/detect-engine-content-inspection.c b/src/tests/detect-engine-content-inspection.c index bc14abe62a..214c5c3fa8 100644 --- a/src/tests/detect-engine-content-inspection.c +++ b/src/tests/detect-engine-content-inspection.c @@ -225,6 +225,16 @@ static int DetectEngineContentInspectionTest10(void) { TEST_FOOTER; } +static int DetectEngineContentInspectionTest11(void) { + TEST_HEADER; + TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\";", true, 2); + TEST_RUN("ab", 2, "content:\"a\"; startswith; content:\"b\"; within:1; distance:0;", true, 2); + TEST_RUN("ab", 2, "content:\"ab\"; startswith;", true, 1); + TEST_RUN("ab", 2, "content:\"a\"; startswith;", true, 1); + TEST_RUN("ab", 2, "content:\"b\"; startswith;", false, 1); + TEST_FOOTER; +} + void DetectEngineContentInspectionRegisterTests(void) { UtRegisterTest("DetectEngineContentInspectionTest01", @@ -247,6 +257,8 @@ void DetectEngineContentInspectionRegisterTests(void) DetectEngineContentInspectionTest09); UtRegisterTest("DetectEngineContentInspectionTest10", DetectEngineContentInspectionTest10); + UtRegisterTest("DetectEngineContentInspectionTest11 startswith", + DetectEngineContentInspectionTest11); } #undef TEST_HEADER