diff --git a/rust/src/asn1/mod.rs b/rust/src/asn1/mod.rs index c88bcdfc86..cadbc0c164 100644 --- a/rust/src/asn1/mod.rs +++ b/rust/src/asn1/mod.rs @@ -234,7 +234,7 @@ fn asn1_decode( /// input must be a valid buffer of at least input_len bytes /// pointer must be freed using `rs_asn1_free` #[no_mangle] -pub(crate) unsafe extern "C" fn rs_asn1_decode( +pub unsafe extern "C" fn rs_asn1_decode( input: *const u8, input_len: u32, ad_ptr: *const DetectAsn1Data, @@ -278,7 +278,7 @@ pub unsafe extern "C" fn rs_asn1_free(ptr: *mut Asn1) { /// /// Returns 1 if any of the options match, 0 if not #[no_mangle] -pub(crate) unsafe extern "C" fn rs_asn1_checks( +pub unsafe extern "C" fn rs_asn1_checks( ptr: *const Asn1, ad_ptr: *const DetectAsn1Data, ) -> u8 { diff --git a/rust/src/asn1/parse_rules.rs b/rust/src/asn1/parse_rules.rs index c1cfae66c0..592b8b6cac 100644 --- a/rust/src/asn1/parse_rules.rs +++ b/rust/src/asn1/parse_rules.rs @@ -34,7 +34,7 @@ const ASN1_DEFAULT_MAX_FRAMES: u16 = 30; /// /// pointer must be free'd using `rs_detect_asn1_free` #[no_mangle] -pub(crate) unsafe extern "C" fn rs_detect_asn1_parse(input: *const c_char) -> *mut DetectAsn1Data { +pub unsafe extern "C" fn rs_detect_asn1_parse(input: *const c_char) -> *mut DetectAsn1Data { if input.is_null() { return std::ptr::null_mut(); } @@ -71,7 +71,7 @@ pub(crate) unsafe extern "C" fn rs_detect_asn1_parse(input: *const c_char) -> *m /// /// ptr must be a valid object obtained using `rs_detect_asn1_parse` #[no_mangle] -pub(crate) unsafe extern "C" fn rs_detect_asn1_free(ptr: *mut DetectAsn1Data) { +pub unsafe extern "C" fn rs_detect_asn1_free(ptr: *mut DetectAsn1Data) { if ptr.is_null() { return; } @@ -80,7 +80,7 @@ pub(crate) unsafe extern "C" fn rs_detect_asn1_free(ptr: *mut DetectAsn1Data) { /// Struct to hold parsed asn1 keyword options #[derive(Debug, PartialEq)] -pub(crate) struct DetectAsn1Data { +pub struct DetectAsn1Data { pub bitstring_overflow: bool, pub double_overflow: bool, pub oversize_length: Option, diff --git a/src/detect-asn1.c b/src/detect-asn1.c index f0c2ab3dd3..964987da5c 100644 --- a/src/detect-asn1.c +++ b/src/detect-asn1.c @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2010 Open Information Security Foundation +/* Copyright (C) 2020 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 @@ -18,14 +18,13 @@ /** * \file detect-asn1.c * - * \author Pablo Rincon Crespo - * * Implements "asn1" keyword */ #include "suricata-common.h" #include "debug.h" #include "decode.h" +#include "rust.h" #include "detect.h" #include "detect-parse.h" @@ -37,10 +36,6 @@ #include "util-unittest-helper.h" #include "util-byte.h" #include "util-debug.h" -#include "util-decode-asn1.h" - -/* delimiters for functions/arguments */ -const char *ASN_DELIM = " \t,\n"; static int DetectAsn1Match(DetectEngineThreadCtx *, Packet *, const Signature *, const SigMatchCtx *); @@ -62,80 +57,17 @@ void DetectAsn1Register(void) return; } -/** - * \brief The main checks are done here - * This function implements the detection of the following options: - * - oversize_length - * - bitstring_overflow - * - double_overflow - * We can add more checks here easily since we have all the data of the - * node avaliable. If we need all the tree, we can just pass the - * ASN1 ctx as argument and perform the checks here - * \param node pointer to the Asn1Node to inspect - * \param ad pointer to the parsed options of the asn1 keyword (which hold the - * checks that we want to perform, and the lenght of oversize check - * \retval 1 if any of the options match, 0 if not - */ -static uint8_t DetectAsn1Checks(Asn1Node *node, const DetectAsn1Data *ad) -{ - - /* oversize_length will check if a node has a length greater than - * the user supplied length */ - if (ad->flags & ASN1_OVERSIZE_LEN) { - if (node->len.len > ad->oversize_length - || node->data.len > ad->oversize_length) - return 1; - } - - /* 8.6 */ - /* bitstring_overflow check a malformed option where the number of bits - * to ignore is greater than the length decoded (in bits) */ - if (ad->flags & ASN1_BITSTRING_OVF) { - if (node->id.class_tag == ASN1_BER_CLASS_UNIV && - node->id.tag_num == ASN1_UNITAG_BIT_STRING && - node->id.tag_type == ASN1_TAG_TYPE_PRIMITIVE) - { - if (node->len.len > 0 && node->data.ptr != NULL - && (node->len.len) * 8 < (uint8_t) *node->data.ptr) - { - return 1; - } - } - } - - /* double_overflow checks a known issue that affect the MSASN1 library - * when decoding double/real types. If the endoding is ASCII, - * and the buffer is greater than 256, the array is overflown - */ - if (ad->flags & ASN1_DOUBLE_OVF) { - if (node->id.class_tag == ASN1_BER_CLASS_UNIV && - node->id.tag_num == ASN1_UNITAG_REAL && - node->id.tag_type == ASN1_TAG_TYPE_PRIMITIVE) - { - if (node->len.len > 0 && node->data.ptr != NULL - && !((uint8_t) *node->data.ptr & 0xC0) - && (node->len.len > 256 || node->data.len > 256)) - { - return 1; - } - } - } - - /* Good to know :) */ - return 0; -} - /** * \brief This function will decode the asn1 data and inspect the resulting * nodes to detect if any of the specified checks match this data * - * \param t pointer to thread vars - * \param det_ctx pointer to the pattern matcher thread + * \param det_ctx pointer to the detect engine thread context * \param p pointer to the current packet - * \param m pointer to the sigmatch that we will cast into DetectAsn1Data + * \param s pointer to the signature + * \param ctx pointer to the sigmatch that we will cast into `DetectAsn1Data` * - * \retval 0 no match * \retval 1 match + * \retval 0 no match */ static int DetectAsn1Match(DetectEngineThreadCtx *det_ctx, Packet *p, const Signature *s, const SigMatchCtx *ctx) @@ -148,171 +80,46 @@ static int DetectAsn1Match(DetectEngineThreadCtx *det_ctx, Packet *p, } const DetectAsn1Data *ad = (const DetectAsn1Data *)ctx; - uint16_t offset; - if (ad->flags & ASN1_ABSOLUTE_OFFSET) { - offset = ad->absolute_offset; - } else if (ad->flags & ASN1_RELATIVE_OFFSET) { - // relative offset in regards to the last content match - // This range check is done when relative_offset - BUG_ON(ad->relative_offset > UINT16_MAX || ad->relative_offset < -UINT16_MAX); + Asn1 *asn1 = rs_asn1_decode(p->payload, p->payload_len, ad); - int64_t tmp_offset = det_ctx->buffer_offset + ad->relative_offset; + ret = rs_asn1_checks(asn1, ad); - // check for under/overflow before downcasting - if (tmp_offset < 0 || tmp_offset > UINT16_MAX) { - return 0; - } + rs_asn1_free(asn1); - offset = (uint16_t)tmp_offset; - } else { - offset = 0; - } - - if (offset >= p->payload_len) { - return 0; - } - - Asn1Ctx *ac = SCAsn1CtxNew(); - if (ac == NULL) - return 0; - - SCAsn1CtxInit(ac, p->payload + offset, p->payload_len - offset); - SCAsn1Decode(ac, ac->cur_frame); - - /* Ok, now we have all the data. Let's check the nodes */ - - if (ac->cur_frame > 0 || (ac->asn1_stack[0] != NULL && ac->asn1_stack[0]->id.ptr != NULL)) { - /* We spect at least one node */ - uint16_t n_iter = 0; - ret = 0; - - for (; n_iter <= ac->cur_frame; n_iter++) { - Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter); - - if (node == NULL || node->id.ptr == NULL) - continue; /* Should not happen */ - - ret = DetectAsn1Checks(node, ad); - /* Got a match? */ - if (ret == 1) - break; - } - } - - SCAsn1CtxDestroy(ac); return ret; } /** * \brief This function is used to parse asn1 options passed via asn1: keyword * - * \param asn1str Pointer to the user provided asn1 options + * \param asn1str pointer to the user provided asn1 options * - * \retval fd pointer to DetectAsn1Data on success + * \retval pointer to `DetectAsn1Data` on success * \retval NULL on failure */ -static DetectAsn1Data *DetectAsn1Parse(const char *instr) +static DetectAsn1Data *DetectAsn1Parse(const char *asn1str) { - DetectAsn1Data *fd = NULL; - char *tok = NULL; - uint32_t ov_len = 0; - uint16_t abs_off = 0; - int32_t rel_off = 0; - uint8_t flags = 0; - char *saveptr = NULL; - - char *asn1str = SCStrdup(instr); - if (asn1str == NULL) - return NULL; - - tok = strtok_r(asn1str, ASN_DELIM, &saveptr); - if (tok == NULL) { + DetectAsn1Data *ad = rs_detect_asn1_parse(asn1str); + + if (ad == NULL) { SCLogError(SC_ERR_INVALID_VALUE, "Malformed asn1 argument: %s", asn1str); - SCFree(asn1str); - return NULL; - } - - while (tok != NULL) { - if (strcasecmp("bitstring_overflow", tok) == 0) { - /* No arg here, just set the flag */ - flags |= ASN1_BITSTRING_OVF; - } else if (strcasecmp("double_overflow", tok) == 0) { - /* No arg here, just set the flag */ - flags |= ASN1_DOUBLE_OVF; - } else if (strcasecmp("oversize_length", tok) == 0) { - flags |= ASN1_OVERSIZE_LEN; - /* get the param */ - tok = strtok_r(NULL, ASN_DELIM, &saveptr); - if ( tok == NULL || - StringParseUint32(&ov_len, 10, 0, tok) <= 0) - { - SCLogError(SC_ERR_INVALID_VALUE, "Malformed value for " - "oversize_length: %s", tok); - goto error; - } - } else if (strcasecmp("absolute_offset", tok) == 0) { - flags |= ASN1_ABSOLUTE_OFFSET; - /* get the param */ - tok = strtok_r(NULL, ASN_DELIM, &saveptr); - if (tok == NULL || - StringParseUint16(&abs_off, 10, 0, tok) <= 0) - { - SCLogError(SC_ERR_INVALID_VALUE, "Malformed value for " - "absolute_offset: %s", tok); - goto error; - } - } else if (strcasecmp("relative_offset",tok) == 0) { - flags |= ASN1_RELATIVE_OFFSET; - /* get the param */ - tok = strtok_r(NULL, ASN_DELIM, &saveptr); - - // Range check on uint16_t max as uint16_t is the type of the buffer - // the offset is relative to - if (tok == NULL || - StringParseI32RangeCheck(&rel_off, 10, 0, tok, -UINT16_MAX, UINT16_MAX) <= 0) - { - SCLogError(SC_ERR_INVALID_VALUE, "Malformed value for " - "relative_offset: %s", tok); - goto error; - } - } else { - SCLogError(SC_ERR_INVALID_VALUE, "Malformed asn1 argument: %s", - asn1str); - goto error; - } - tok = strtok_r(NULL, ASN_DELIM, &saveptr); - } - - fd = SCMalloc(sizeof(DetectAsn1Data)); - if (unlikely(fd == NULL)) { - goto error; } - memset(fd, 0x00, sizeof(DetectAsn1Data)); - - fd->flags = flags; - fd->oversize_length = ov_len; /* Length argument if needed */ - fd->absolute_offset = abs_off; /* Length argument if needed */ - fd->relative_offset = rel_off; /* Length argument if needed */ - SCFree(asn1str); - return fd; -error: - SCFree(asn1str); - return NULL; + return ad; } /** * \brief this function is used to add the parsed asn1 data into * the current signature * - * \param de_ctx pointer to the Detection Engine Context - * \param s pointer to the Current Signature + * \param de_ctx pointer to the detection engine context + * \param s pointer to the current signature * \param asn1str pointer to the user provided asn1 options * - * \retval 0 on Success - * \retval -1 on Failure + * \retval 0 on success + * \retval -1 on failure */ static int DetectAsn1Setup(DetectEngineCtx *de_ctx, Signature *s, const char *asn1str) { @@ -320,7 +127,8 @@ static int DetectAsn1Setup(DetectEngineCtx *de_ctx, Signature *s, const char *as SigMatch *sm = NULL; ad = DetectAsn1Parse(asn1str); - if (ad == NULL) goto error; + if (ad == NULL) + goto error; /* Okay so far so good, lets get this into a SigMatch * and put it in the Signature. */ @@ -336,720 +144,27 @@ static int DetectAsn1Setup(DetectEngineCtx *de_ctx, Signature *s, const char *as return 0; error: - if (ad != NULL) - DetectAsn1Free(de_ctx, ad); if (sm != NULL) SCFree(sm); + if (ad != NULL) + DetectAsn1Free(de_ctx, ad); return -1; - } /** - * \brief this function will free memory associated with DetectAsn1Data + * \brief this function will free memory associated with `DetectAsn1Data` * - * \param ad pointer to DetectAsn1Data + * \param de_ctx pointer to the detection engine context + * \param ptr point to `DetectAsn1Data` */ static void DetectAsn1Free(DetectEngineCtx *de_ctx, void *ptr) { DetectAsn1Data *ad = (DetectAsn1Data *)ptr; - SCFree(ad); + rs_detect_asn1_free(ad); } #ifdef UNITTESTS -/** - * \test DetectAsn1TestParse01 check that we parse oversize_length correctly - */ -static int DetectAsn1TestParse01(void) -{ - int result = 0; - char str[] = "oversize_length 1024"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL) { - if (ad->oversize_length == 1024 && (ad->flags & ASN1_OVERSIZE_LEN)) { - result = 1; - } - DetectAsn1Free(NULL, ad); - } - - return result; -} - -/** - * \test DetectAsn1TestParse02 check that we parse absolute_offset correctly - */ -static int DetectAsn1TestParse02(void) -{ - int result = 0; - DetectAsn1Data *ad = NULL; - char str[] = "absolute_offset 1024"; - - ad = DetectAsn1Parse(str); - if (ad != NULL && ad->absolute_offset == 1024 - && (ad->flags & ASN1_ABSOLUTE_OFFSET)) { - DetectAsn1Free(NULL, ad); - result = 1; - } - - return result; -} - -/** - * \test DetectAsn1TestParse03 check that we parse relative_offset correctly - */ -static int DetectAsn1TestParse03(void) -{ - int result = 0; - char str[] = "relative_offset 1024"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && ad->relative_offset == 1024 - && (ad->flags & ASN1_RELATIVE_OFFSET)) { - DetectAsn1Free(NULL, ad); - result = 1; - } - - return result; -} - -/** - * \test DetectAsn1TestParse04 check that we parse bitstring_overflow correctly - */ -static int DetectAsn1TestParse04(void) -{ - int result = 0; - char str[] = "bitstring_overflow"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && (ad->flags & ASN1_BITSTRING_OVF)) { - DetectAsn1Free(NULL, ad); - result = 1; - } - - return result; -} - -/** - * \test DetectAsn1TestParse05 check that we parse double_overflow correctly - */ -static int DetectAsn1TestParse05(void) -{ - int result = 0; - char str[] = "double_overflow"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && (ad->flags & ASN1_DOUBLE_OVF)) { - DetectAsn1Free(NULL, ad); - result = 1; - } - - return result; -} - -/** - * \test DetectAsn1TestParse06 check that we fail if a needed arg is not given - */ -static int DetectAsn1TestParse06(void) -{ - int result = 1; - char str[] = "absolute_offset"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL) { - DetectAsn1Free(NULL, ad); - result = 0; - } - - return result; -} - -/** - * \test DetectAsn1TestParse07 check that we fail if a needed arg is not given - */ -static int DetectAsn1TestParse07(void) -{ - int result = 1; - char str[] = "relative_offset"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL) { - DetectAsn1Free(NULL, ad); - result = 0; - } - - return result; -} - -/** - * \test DetectAsn1TestParse08 check that we fail if a needed arg is not given - */ -static int DetectAsn1TestParse08(void) -{ - int result = 1; - char str[] = "oversize_length"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL) { - DetectAsn1Free(NULL, ad); - result = 0; - } - - return result; -} - - - -/** - * \test DetectAsn1TestParse09 test that we break on invalid options - */ -static int DetectAsn1TestParse09(void) -{ - int result = 1; - DetectAsn1Data *fd = NULL; - char str[] = "oversize_length 1024, lalala 360"; - - fd = DetectAsn1Parse(str); - if (fd != NULL) { - result = 0; - DetectAsn1Free(NULL, fd); - } - - return result; -} - -/** - * \test DetectAsn1TestParse10 test that we break with a empty string - */ -static int DetectAsn1TestParse10(void) -{ - int result = 1; - DetectAsn1Data *fd = NULL; - char str[] = ""; - - fd = DetectAsn1Parse(str); - if (fd != NULL) { - result = 0; - DetectAsn1Free(NULL, fd); - } - - return result; -} - -/** - * \test DetectAsn1TestParse11 check for combinations of keywords - */ -static int DetectAsn1TestParse11(void) -{ - int result = 0; - char str[] = "oversize_length 1024, relative_offset 10"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && ad->oversize_length == 1024 - && (ad->flags & ASN1_OVERSIZE_LEN) - && ad->relative_offset == 10 - && (ad->flags & ASN1_RELATIVE_OFFSET)) - { - DetectAsn1Free(NULL, ad); - result = 1; - } - - return result; -} - -/** - * \test DetectAsn1TestParse12 check for combinations of keywords - */ -static int DetectAsn1TestParse12(void) -{ - int result = 0; - char str[] = "oversize_length 1024 absolute_offset 10"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && ad->oversize_length == 1024 - && (ad->flags & ASN1_OVERSIZE_LEN) - && ad->absolute_offset == 10 - && (ad->flags & ASN1_ABSOLUTE_OFFSET)) - { - DetectAsn1Free(NULL, ad); - result = 1; - } - - return result; -} - -/** - * \test DetectAsn1TestParse13 check for combinations of keywords - */ -static int DetectAsn1TestParse13(void) -{ - int result = 0; - char str[] = "oversize_length 1024 absolute_offset 10, bitstring_overflow"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && ad->oversize_length == 1024 - && (ad->flags & ASN1_OVERSIZE_LEN) - && (ad->flags & ASN1_BITSTRING_OVF) - && ad->absolute_offset == 10 - && (ad->flags & ASN1_ABSOLUTE_OFFSET)) - { - DetectAsn1Free(NULL, ad); - result = 1; - } - - return result; -} - -/** - * \test DetectAsn1TestParse14 check for combinations of keywords - */ -static int DetectAsn1TestParse14(void) -{ - int result = 0; - char str[] = "double_overflow, oversize_length 1024 absolute_offset 10," - " bitstring_overflow"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && ad->oversize_length == 1024 - && (ad->flags & ASN1_OVERSIZE_LEN) - && (ad->flags & ASN1_BITSTRING_OVF) - && (ad->flags & ASN1_DOUBLE_OVF) - && ad->absolute_offset == 10 - && (ad->flags & ASN1_ABSOLUTE_OFFSET)) - { - DetectAsn1Free(NULL, ad); - result = 1; - } - - return result; -} - -/** - * \test DetectAsn1TestParse15 check for combinations of keywords - */ -static int DetectAsn1TestParse15(void) -{ - int result = 0; - char str[] = "double_overflow, oversize_length 1024 relative_offset 10," - " bitstring_overflow"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && ad->oversize_length == 1024 - && (ad->flags & ASN1_OVERSIZE_LEN) - && (ad->flags & ASN1_BITSTRING_OVF) - && (ad->flags & ASN1_DOUBLE_OVF) - && ad->relative_offset == 10 - && (ad->flags & ASN1_RELATIVE_OFFSET)) - { - DetectAsn1Free(NULL, ad); - result = 1; - } - - return result; -} - -/** - * \test DetectAsn1Test01 Ensure that the checks work when they should - */ -static int DetectAsn1Test01(void) -{ - /* Match if any of the nodes after offset 0 has greater length than 10 */ - char str[] = "oversize_length 132 absolute_offset 0"; - - DetectAsn1Data *ad = DetectAsn1Parse(str); - FAIL_IF_NULL(ad); - FAIL_IF_NOT(ad->oversize_length == 132); - FAIL_IF_NOT(ad->flags & ASN1_OVERSIZE_LEN); - FAIL_IF_NOT(ad->absolute_offset == 0); - FAIL_IF_NOT(ad->flags & ASN1_ABSOLUTE_OFFSET); - - // Example from the specification X.690-0207 Appendix A.3 - char buf[] = "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01" - "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" - "\x42\x01\x33\xA1\x0A\x43\x08""19710917" - "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" - "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" - "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111" - "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05" - "Jones""\xA0\x0A\x43\x08""19590717"; - - Asn1Ctx *ac = SCAsn1CtxNew(); - FAIL_IF_NULL(ac); - - uint16_t len = strlen((char *)buf)-1; - - SCAsn1CtxInit(ac, (uint8_t *)buf, len); - SCAsn1Decode(ac, ac->cur_frame); - - /* The first node has length 133, so it should match the oversize */ - FAIL_IF_NOT(ac->cur_frame > 0); - - /* We spect at least one node */ - uint16_t n_iter = 0; - int result = 0; - for (; n_iter <= ac->cur_frame; n_iter++) { - Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter); - - if (node == NULL || node->id.ptr == NULL) - continue; /* Should not happen */ - - result = DetectAsn1Checks(node, ad); - /* Got a match? */ - if (result == 1) - break; - } - FAIL_IF(result != 1); - - SCAsn1CtxDestroy(ac); - DetectAsn1Free(NULL, ad); - - PASS; -} - -/** - * \test DetectAsn1Test02 Ensure that the checks work when they should - */ -static int DetectAsn1Test02(void) -{ - int result = 0; - /* Match if any of the nodes has the bitstring overflow condition */ - char str[] = "oversize_length 133, absolute_offset 0"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && ad->oversize_length == 133 - && (ad->flags & ASN1_OVERSIZE_LEN) - && ad->absolute_offset == 0 - && (ad->flags & ASN1_ABSOLUTE_OFFSET)) - { - // Example from the specification X.690-0207 Appendix A.3 - uint8_t *buf = (uint8_t*) "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01" - "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" - "\x42\x01\x33\xA1\x0A\x43\x08""19710917" - "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" - "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" - "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111" - "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05" - "Jones""\xA0\x0A\x43\x08""19590717"; - - Asn1Ctx *ac = SCAsn1CtxNew(); - if (ac == NULL) - return 0; - - uint16_t len = strlen((char *)buf)-1; - - SCAsn1CtxInit(ac, buf, len); - - SCAsn1Decode(ac, ac->cur_frame); - - /* The first node has length 133, so it should match the oversize */ - if (ac->cur_frame > 0) { - /* We spect at least one node */ - uint16_t n_iter = 0; - - for (; n_iter <= ac->cur_frame; n_iter++) { - Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter); - - if (node == NULL || node->id.ptr == NULL) - continue; /* Should not happen */ - - result |= DetectAsn1Checks(node, ad); - } - } - - /* Got a match? We don't have nodes greater than 133, it should not */ - if (result == 1) { - printf("Error, oversize_length should not match" - " any of the nodes: "); - result = 0; - } else { - result = 1; - } - - SCAsn1CtxDestroy(ac); - DetectAsn1Free(NULL, ad); - - } - - return result; -} - -/** - * \test DetectAsn1Test03 Ensure that the checks work when they should - */ -static int DetectAsn1Test03(void) -{ - int result = 0; - /* Match if any of the nodes after offset 0 has a bitstring overflow */ - char str[] = "bitstring_overflow, absolute_offset 0"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && (ad->flags & ASN1_BITSTRING_OVF) - && ad->absolute_offset == 0 - && (ad->flags & ASN1_ABSOLUTE_OFFSET)) - { - /* Let's say tagnum bitstring, primitive, and as universal tag, - * and then length = 1 octet, but the next octet specify to ignore - * the last 256 bits... (let's match!) */ - uint8_t *buf = (uint8_t*) "\x03\x01\xFF"; - - Asn1Ctx *ac = SCAsn1CtxNew(); - if (ac == NULL) - return 0; - - uint16_t len = 3; - - SCAsn1CtxInit(ac, buf, len); - - SCAsn1Decode(ac, ac->cur_frame); - - if (ac->cur_frame > 0 || ac->asn1_stack[0]->id.ptr != NULL) { - /* We spect at least one node */ - uint16_t n_iter = 0; - - for (; n_iter <= ac->cur_frame; n_iter++) { - Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter); - - if (node == NULL || node->id.ptr == NULL) - continue; /* Should not happen */ - - result = DetectAsn1Checks(node, ad); - /* Got a match? */ - if (result == 1) - break; - } - } - - SCAsn1CtxDestroy(ac); - DetectAsn1Free(NULL, ad); - - } - - if (result == 0) { - printf("Error, bitstring_overflow should match the first node: "); - } - - return result; -} - -/** - * \test DetectAsn1Test04 Ensure that the checks work when they should - */ -static int DetectAsn1Test04(void) -{ - int result = 0; - /* Match if any of the nodes after offset 0 has a bitstring overflow */ - char str[] = "bitstring_overflow, absolute_offset 0"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && (ad->flags & ASN1_BITSTRING_OVF) - && ad->absolute_offset == 0 - && (ad->flags & ASN1_ABSOLUTE_OFFSET)) - { - /* Let's say tagnum bitstring, primitive, and as universal tag, - * and then length = 1 octet, but the next octet specify to ignore - * the last 7 bits... (should not match) */ - uint8_t *buf = (uint8_t*) "\x03\x01\x07"; - - Asn1Ctx *ac = SCAsn1CtxNew(); - if (ac == NULL) - return 0; - - uint16_t len = 3; - - SCAsn1CtxInit(ac, buf, len); - - SCAsn1Decode(ac, ac->cur_frame); - - if (ac->cur_frame > 0 || ac->asn1_stack[0]->id.ptr != NULL) { - /* We spect at least one node */ - uint16_t n_iter = 0; - - for (; n_iter <= ac->cur_frame; n_iter++) { - Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter); - - if (node == NULL || node->id.ptr == NULL) - continue; /* Should not happen */ - - result = DetectAsn1Checks(node, ad); - /* Got a match? */ - if (result == 1) - break; - } - } - - SCAsn1CtxDestroy(ac); - DetectAsn1Free(NULL, ad); - - } - - if (result == 1) { - printf("Error, bitstring_overflog should not match any node: "); - result = 0; - } else { - result = 1; - } - - return result; -} - -/** - * \test DetectAsn1Test05 Ensure that the checks work when they should - */ -static int DetectAsn1Test05(void) -{ - int result = 0; - /* Match if any of the nodes after offset 0 has a double overflow */ - char str[] = "double_overflow, absolute_offset 0"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && (ad->flags & ASN1_DOUBLE_OVF) - && ad->absolute_offset == 0 - && (ad->flags & ASN1_ABSOLUTE_OFFSET)) - { - /* Let's say tag num 9 (type Real), and encoded as ASCII, with length - * 257, then we must match */ - uint8_t buf[261]; - /* universal class, primitive type, tag_num = 9 (Data type Real) */ - buf[0] = '\x09'; - /* length, definite form, 2 octets */ - buf[1] = '\x82'; - /* length is the sum of the following octets (257): */ - buf[2] = '\xFE'; - buf[3] = '\x03'; - - /* Fill the content of the number */ - uint16_t i = 4; - for (; i < 257;i++) - buf[i] = '\x05'; - - Asn1Ctx *ac = SCAsn1CtxNew(); - if (ac == NULL) - return 0; - - uint16_t len = 261; - - SCAsn1CtxInit(ac, buf, len); - - SCAsn1Decode(ac, ac->cur_frame); - - if (ac->cur_frame > 0 || ac->asn1_stack[0]->id.ptr != NULL) { - /* We spect at least one node */ - uint16_t n_iter = 0; - - for (; n_iter <= ac->cur_frame; n_iter++) { - Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter); - - if (node == NULL || node->id.ptr == NULL) - continue; /* Should not happen */ - - result = DetectAsn1Checks(node, ad); - /* Got a match? */ - if (result == 1) - break; - } - } - - SCAsn1CtxDestroy(ac); - DetectAsn1Free(NULL, ad); - - } - - if (result == 0) { - printf("Error, double_overflow should match the first node: "); - } - - return result; -} - -/** - * \test DetectAsn1Test06 Ensure that the checks work when they should - */ -static int DetectAsn1Test06(void) -{ - int result = 0; - /* Match if any of the nodes after offset 0 has a double overflow */ - char str[] = "double_overflow, absolute_offset 0"; - DetectAsn1Data *ad = NULL; - - ad = DetectAsn1Parse(str); - if (ad != NULL && (ad->flags & ASN1_DOUBLE_OVF) - && ad->absolute_offset == 0 - && (ad->flags & ASN1_ABSOLUTE_OFFSET)) - { - /* Let's say tag num 9 (type Real), and encoded as ASCII, with length - * 256, which fit in the buffer, so it should not match */ - uint8_t buf[260]; - /* universal class, primitive type, tag_num = 9 (Data type Real) */ - buf[0] = '\x09'; - /* length, definite form, 2 octets */ - buf[1] = '\x82'; - /* length is the sum of the following octets (256): */ - buf[2] = '\xFE'; - buf[3] = '\x02'; - - /* Fill the content of the number */ - uint16_t i = 4; - for (; i < 256;i++) - buf[i] = '\x05'; - - Asn1Ctx *ac = SCAsn1CtxNew(); - if (ac == NULL) - return 0; - - uint16_t len = 260; - - SCAsn1CtxInit(ac, buf, len); - - SCAsn1Decode(ac, ac->cur_frame); - - if (ac->cur_frame > 0 || ac->asn1_stack[0]->id.ptr != NULL) { - /* We spect at least one node */ - uint16_t n_iter = 0; - - for (; n_iter <= ac->cur_frame; n_iter++) { - Asn1Node *node = ASN1CTX_GET_NODE(ac, n_iter); - - if (node == NULL || node->id.ptr == NULL) - continue; /* Should not happen */ - - result = DetectAsn1Checks(node, ad); - /* Got a match? */ - if (result == 1) - break; - } - } - - SCAsn1CtxDestroy(ac); - DetectAsn1Free(NULL, ad); - - } - - if (result == 1) { - printf("Error, double_overflow should not match any node: "); - result = 0 ; - } else { - result = 1; - } - - return result; -} - /** * \test DetectAsn1TestReal01 Ensure that all works together */ @@ -1108,7 +223,7 @@ static int DetectAsn1TestReal01(void) "content:\"Pablo\"; asn1:absolute_offset 0, " "oversize_length 130; sid:1;)"; sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; " - "content:\"AA\"; asn1:relative_offset 0, " + "content:\"AA\"; asn1:relative_offset 2, " "oversize_length 130; sid:2;)"; sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; " "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)"; @@ -1187,7 +302,7 @@ static int DetectAsn1TestReal02(void) "content:\"Pablo\"; asn1:absolute_offset 0, " "oversize_length 140; sid:1;)"; sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; " - "content:\"AA\"; asn1:relative_offset 0, " + "content:\"AA\"; asn1:relative_offset 2, " "oversize_length 140; sid:2;)"; sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; " "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)"; @@ -1218,8 +333,8 @@ static int DetectAsn1TestReal03(void) /* length, definite form, 2 octets */ buf[1] = '\x82'; /* length is the sum of the following octets (257): */ - buf[2] = '\xFE'; - buf[3] = '\x03'; + buf[2] = '\x01'; + buf[3] = '\x01'; /* Fill the content of the number */ uint16_t i = 4; @@ -1268,7 +383,7 @@ end: /** * \test DetectAsn1TestReal04 like the real test 02, but modified the * relative offset to check negative offset values, in this case - * start decoding from -11 bytes respect the content match "John" + * start decoding from -7 bytes respect the content match "John" */ static int DetectAsn1TestReal04(void) { @@ -1325,7 +440,7 @@ static int DetectAsn1TestReal04(void) "content:\"Pablo\"; asn1:absolute_offset 0, " "oversize_length 140; sid:1;)"; sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; " - "content:\"John\"; asn1:relative_offset -11, " + "content:\"John\"; asn1:relative_offset -7, " "oversize_length 140; sid:2;)"; sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; " "content:\"lalala\"; asn1: oversize_length 2000; sid:3;)"; @@ -1344,64 +459,6 @@ end: return result; } -/** - * \test DetectAsn1TestReal05 like the real test 04, but modified the - * relative offset to check offset values which could read past the bounds - * of the buffer - */ -static int DetectAsn1TestReal05(void) -{ - int result = 0; - /* Check the start with AA (this is to test the relative_offset keyword) */ - uint8_t *buf = (uint8_t *) "AA\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01" - "P""\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" - "\x42\x01\x33\xA1\x0A\x43\x08""19710917" - "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" - "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" - "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111" - "\x31\x1F\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05" - "Jones""\xA0\x0A\x43\x08""19590717" - "\x60\x81\x85\x61\x10\x1A\x04""John""\x1A\x01""P" - "\x1A\x05""Smith""\xA0\x0A\x1A\x08""Director" - "\x42\x01\x33\xA1\x0A\x43\x08""19710917" - "\xA2\x12\x61\x10\x1A\x04""Mary""\x1A\x01""T""\x1A\x05" - "Smith""\xA3\x42\x31\x1F\x61\x11\x1A\x05""Ralph""\x1A\x01" - "T""\x1A\x05""Smith""\xA0\x0A\x43\x08""19571111""\x31\x1F" - "\x61\x11\x1A\x05""Susan""\x1A\x01""B""\x1A\x05""Jones" - "\xA0\x0A\x43\x08""19590717"; - - uint16_t buflen = strlen((char *)buf) - 1; - - Packet *p[1]; - - p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); - - if (p[0] == NULL) - goto end; - - const char *sigs[3]; - sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; " - "content:\"John\"; asn1:relative_offset -100, " - "oversize_length 132; sid:1;)"; - sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; " - "content:\"John\"; asn1:relative_offset -11, " - "oversize_length 132; sid:2;)"; - sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; " - "content:\"John\"; asn1: relative_offset 5000, " - "oversize_length 132; sid:3;)"; - - uint32_t sid[3] = {1, 2, 3}; - - // The second signature should match - uint32_t results[1][3] = {{0, 1, 0}}; - - result = UTHGenericTest(p, 1, sigs, sid, (uint32_t *) results, 3); - - UTHFreePackets(p, 1); -end: - return result; -} - #endif /* UNITTESTS */ /** @@ -1410,35 +467,10 @@ end: static void DetectAsn1RegisterTests(void) { #ifdef UNITTESTS - UtRegisterTest("DetectAsn1TestParse01", DetectAsn1TestParse01); - UtRegisterTest("DetectAsn1TestParse02", DetectAsn1TestParse02); - UtRegisterTest("DetectAsn1TestParse03", DetectAsn1TestParse03); - - UtRegisterTest("DetectAsn1TestParse04", DetectAsn1TestParse04); - UtRegisterTest("DetectAsn1TestParse05", DetectAsn1TestParse05); - UtRegisterTest("DetectAsn1TestParse06", DetectAsn1TestParse06); - - UtRegisterTest("DetectAsn1TestParse07", DetectAsn1TestParse07); - UtRegisterTest("DetectAsn1TestParse08", DetectAsn1TestParse08); - UtRegisterTest("DetectAsn1TestParse09", DetectAsn1TestParse09); - - UtRegisterTest("DetectAsn1TestParse10", DetectAsn1TestParse10); - UtRegisterTest("DetectAsn1TestParse11", DetectAsn1TestParse11); - UtRegisterTest("DetectAsn1TestParse12", DetectAsn1TestParse12); - UtRegisterTest("DetectAsn1TestParse13", DetectAsn1TestParse13); - UtRegisterTest("DetectAsn1TestParse14", DetectAsn1TestParse14); - UtRegisterTest("DetectAsn1TestParse15", DetectAsn1TestParse15); - UtRegisterTest("DetectAsn1Test01 - oversize_len", DetectAsn1Test01); - UtRegisterTest("DetectAsn1Test02 - oversize_len", DetectAsn1Test02); - UtRegisterTest("DetectAsn1Test03 - bitstring_ovf", DetectAsn1Test03); - UtRegisterTest("DetectAsn1Test04 - bitstring_ovf", DetectAsn1Test04); - UtRegisterTest("DetectAsn1Test05 - double_ovf", DetectAsn1Test05); - UtRegisterTest("DetectAsn1Test06 - double_ovf", DetectAsn1Test06); UtRegisterTest("DetectAsn1TestReal01", DetectAsn1TestReal01); UtRegisterTest("DetectAsn1TestReal02", DetectAsn1TestReal02); UtRegisterTest("DetectAsn1TestReal03", DetectAsn1TestReal03); UtRegisterTest("DetectAsn1TestReal04", DetectAsn1TestReal04); - UtRegisterTest("DetectAsn1TestReal05", DetectAsn1TestReal05); #endif /* UNITTESTS */ } diff --git a/src/detect-asn1.h b/src/detect-asn1.h index 9f350e25b1..eda36d4c2a 100644 --- a/src/detect-asn1.h +++ b/src/detect-asn1.h @@ -1,4 +1,4 @@ -/* Copyright (C) 2007-2010 Open Information Security Foundation +/* Copyright (C) 2020 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 @@ -18,28 +18,11 @@ /** * \file detect-asn1.h * - * \author Pablo Rincon Crespo - * * Implements "asn1" keyword */ #ifndef __DETECT_ASN1_H__ #define __DETECT_ASN1_H__ - -/* Function check flags */ -#define ASN1_BITSTRING_OVF 0x01 -#define ASN1_DOUBLE_OVF 0x02 -#define ASN1_OVERSIZE_LEN 0x04 -#define ASN1_ABSOLUTE_OFFSET 0x10 -#define ASN1_RELATIVE_OFFSET 0x20 - -typedef struct DetectAsn1Data_ { - uint8_t flags; /* flags indicating the checks loaded */ - uint32_t oversize_length; /* Length argument if needed */ - uint16_t absolute_offset; /* Length argument if needed */ - int32_t relative_offset; /* Length argument if needed */ -} DetectAsn1Data; - /* prototypes */ void DetectAsn1Register (void);