/* 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 detect-asn1.c * * \author Pablo Rincon Crespo * * Implements "asn1" keyword */ #include "suricata-common.h" #include "debug.h" #include "decode.h" #include "detect.h" #include "detect-parse.h" #include "flow.h" #include "detect-asn1.h" #include "util-unittest.h" #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"; int DetectAsn1Match(ThreadVars *, DetectEngineThreadCtx *, Packet *, Signature *, const SigMatchCtx *); static int DetectAsn1Setup (DetectEngineCtx *, Signature *, char *); void DetectAsn1RegisterTests(void); void DetectAsn1Free(void *); /** * \brief Registration function for asn1 */ void DetectAsn1Register(void) { sigmatch_table[DETECT_ASN1].name = "asn1"; sigmatch_table[DETECT_ASN1].Match = DetectAsn1Match; sigmatch_table[DETECT_ASN1].Setup = DetectAsn1Setup; sigmatch_table[DETECT_ASN1].Free = DetectAsn1Free; sigmatch_table[DETECT_ASN1].RegisterTests = DetectAsn1RegisterTests; 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 p pointer to the current packet * \param m pointer to the sigmatch that we will cast into DetectAsn1Data * * \retval 0 no match * \retval 1 match */ int DetectAsn1Match(ThreadVars *t, DetectEngineThreadCtx *det_ctx, Packet *p, Signature *s, const SigMatchCtx *ctx) { uint8_t ret = 0; if (p->payload_len == 0) { /* No error, parser done, no data in bounds to decode */ return 0; } const DetectAsn1Data *ad = (const DetectAsn1Data *)ctx; Asn1Ctx *ac = SCAsn1CtxNew(); if (ac == NULL) return 0; if (ad->flags & ASN1_ABSOLUTE_OFFSET) { SCAsn1CtxInit(ac, p->payload + ad->absolute_offset, p->payload_len - ad->absolute_offset); } else if (ad->flags & ASN1_RELATIVE_OFFSET) { SCAsn1CtxInit(ac, p->payload + ad->relative_offset, p->payload_len - ad->relative_offset); } else { SCAsn1CtxInit(ac, p->payload, p->payload_len); } 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 * * \retval fd pointer to DetectAsn1Data on success * \retval NULL on failure */ DetectAsn1Data *DetectAsn1Parse(char *asn1str) { DetectAsn1Data *fd = NULL; char *tok = NULL; uint32_t ov_len = 0; uint32_t abs_off = 0; int32_t rel_off = 0; uint8_t flags = 0; char *saveptr = NULL; tok = strtok_r(asn1str, ASN_DELIM, &saveptr); if (tok == NULL) { SCLogError(SC_ERR_INVALID_VALUE, "Malformed asn1 argument: %s", 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 || ByteExtractStringUint32(&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 || ByteExtractStringUint32(&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); if (tok == NULL || ByteExtractStringInt32(&rel_off, 10, 0, tok) <= 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); return NULL; } tok = strtok_r(NULL, ASN_DELIM, &saveptr); } fd = SCMalloc(sizeof(DetectAsn1Data)); if (unlikely(fd == NULL)) { exit(EXIT_FAILURE); } 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 */ return fd; error: return NULL; } /** * \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 asn1str pointer to the user provided asn1 options * * \retval 0 on Success * \retval -1 on Failure */ int DetectAsn1Setup(DetectEngineCtx *de_ctx, Signature *s, char *asn1str) { DetectAsn1Data *ad = NULL; SigMatch *sm = NULL; ad = DetectAsn1Parse(asn1str); if (ad == NULL) goto error; /* Okay so far so good, lets get this into a SigMatch * and put it in the Signature. */ sm = SigMatchAlloc(); if (sm == NULL) goto error; sm->type = DETECT_ASN1; sm->ctx = (SigMatchCtx *)ad; SigMatchAppendSMToList(s, sm, DETECT_SM_LIST_MATCH); return 0; error: if (ad != NULL) DetectAsn1Free(ad); if (sm != NULL) SCFree(sm); return -1; } /** * \brief this function will free memory associated with DetectAsn1Data * * \param ad pointer to DetectAsn1Data */ void DetectAsn1Free(void *ptr) { DetectAsn1Data *ad = (DetectAsn1Data *)ptr; SCFree(ad); } #ifdef UNITTESTS /** * \test DetectAsn1TestParse01 check that we parse oversize_length correctly */ 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(ad); } return result; } /** * \test DetectAsn1TestParse02 check that we parse absolute_offset correctly */ 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(ad); result = 1; } return result; } /** * \test DetectAsn1TestParse03 check that we parse relative_offset correctly */ 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(ad); result = 1; } return result; } /** * \test DetectAsn1TestParse04 check that we parse bitstring_overflow correctly */ 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(ad); result = 1; } return result; } /** * \test DetectAsn1TestParse05 check that we parse double_overflow correctly */ 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(ad); result = 1; } return result; } /** * \test DetectAsn1TestParse06 check that we fail if a needed arg is not given */ int DetectAsn1TestParse06(void) { int result = 1; char str[] = "absolute_offset"; DetectAsn1Data *ad = NULL; ad = DetectAsn1Parse(str); if (ad != NULL) { DetectAsn1Free(ad); result = 0; } return result; } /** * \test DetectAsn1TestParse07 check that we fail if a needed arg is not given */ int DetectAsn1TestParse07(void) { int result = 1; char str[] = "relative_offset"; DetectAsn1Data *ad = NULL; ad = DetectAsn1Parse(str); if (ad != NULL) { DetectAsn1Free(ad); result = 0; } return result; } /** * \test DetectAsn1TestParse08 check that we fail if a needed arg is not given */ int DetectAsn1TestParse08(void) { int result = 1; char str[] = "oversize_length"; DetectAsn1Data *ad = NULL; ad = DetectAsn1Parse(str); if (ad != NULL) { DetectAsn1Free(ad); result = 0; } return result; } /** * \test DetectAsn1TestParse09 test that we break on invalid options */ 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(fd); } return result; } /** * \test DetectAsn1TestParse10 test that we break with a empty string */ int DetectAsn1TestParse10(void) { int result = 1; DetectAsn1Data *fd = NULL; char str[] = ""; fd = DetectAsn1Parse(str); if (fd != NULL) { result = 0; DetectAsn1Free(fd); } return result; } /** * \test DetectAsn1TestParse11 check for combinations of keywords */ 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(ad); result = 1; } return result; } /** * \test DetectAsn1TestParse12 check for combinations of keywords */ 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(ad); result = 1; } return result; } /** * \test DetectAsn1TestParse13 check for combinations of keywords */ 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(ad); result = 1; } return result; } /** * \test DetectAsn1TestParse14 check for combinations of keywords */ 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(ad); result = 1; } return result; } /** * \test DetectAsn1TestParse15 check for combinations of keywords */ 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(ad); result = 1; } return result; } /** * \test DetectAsn1Test01 Ensure that the checks work when they should */ int DetectAsn1Test01(void) { int result = 0; /* Match if any of the nodes after offset 0 has greater length than 10 */ char str[] = "oversize_length 132 absolute_offset 0"; DetectAsn1Data *ad = NULL; ad = DetectAsn1Parse(str); if (ad != NULL && ad->oversize_length == 132 && (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 *str = (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 *)str)-1; SCAsn1CtxInit(ac, str, 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? */ if (result == 1) break; } } SCAsn1CtxDestroy(ac); DetectAsn1Free(ad); } if (result == 0) { printf("Error, oversize_length should match the first node: "); } return result; } /** * \test DetectAsn1Test02 Ensure that the checks work when they should */ 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 *str = (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 *)str)-1; SCAsn1CtxInit(ac, str, 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(ad); } return result; } /** * \test DetectAsn1Test03 Ensure that the checks work when they should */ 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 *str = (uint8_t*) "\x03\x01\xFF"; Asn1Ctx *ac = SCAsn1CtxNew(); if (ac == NULL) return 0; uint16_t len = 3; SCAsn1CtxInit(ac, str, 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(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 */ 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 *str = (uint8_t*) "\x03\x01\x07"; Asn1Ctx *ac = SCAsn1CtxNew(); if (ac == NULL) return 0; uint16_t len = 3; SCAsn1CtxInit(ac, str, 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(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 */ 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 str[261]; /* universal class, primitive type, tag_num = 9 (Data type Real) */ str[0] = '\x09'; /* length, definite form, 2 octets */ str[1] = '\x82'; /* length is the sum of the following octets (257): */ str[2] = '\xFE'; str[3] = '\x03'; /* Fill the content of the number */ uint16_t i = 4; for (; i < 257;i++) str[i] = '\x05'; Asn1Ctx *ac = SCAsn1CtxNew(); if (ac == NULL) return 0; uint16_t len = 261; SCAsn1CtxInit(ac, str, 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(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 */ 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 str[260]; /* universal class, primitive type, tag_num = 9 (Data type Real) */ str[0] = '\x09'; /* length, definite form, 2 octets */ str[1] = '\x82'; /* length is the sum of the following octets (256): */ str[2] = '\xFE'; str[3] = '\x02'; /* Fill the content of the number */ uint16_t i = 4; for (; i < 256;i++) str[i] = '\x05'; Asn1Ctx *ac = SCAsn1CtxNew(); if (ac == NULL) return 0; uint16_t len = 260; SCAsn1CtxInit(ac, str, 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(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 */ int DetectAsn1TestReal01(void) { int result = 0; 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" "\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""Pablo""\x1A\x01""B""\x1A\x05""Jones" "\xA0\x0A\x43\x08""19590717"; uint16_t buflen = strlen((char *)buf) - 1; /* Check the start with AA (this is to test the relative_offset keyword) */ uint8_t *buf2 = (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 buflen2 = strlen((char *)buf2) - 1; Packet *p[2]; p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP); if (p[0] == NULL || p[1] == NULL) goto end; char *sigs[3]; sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; " "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 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;)"; uint32_t sid[3] = {1, 2, 3}; uint32_t results[2][3] = { /* packet 0 match sid 1 */ {1, 0, 0}, /* packet 1 match sid 2 */ {0, 1, 0}}; /* None of the packets should match sid 3 */ result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3); UTHFreePackets(p, 2); end: return result; } /** * \test DetectAsn1TestReal02 Ensure that all works together */ int DetectAsn1TestReal02(void) { int result = 0; 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" "\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""Pablo""\x1A\x01""B""\x1A\x05""Jones" "\xA0\x0A\x43\x08""19590717"; uint16_t buflen = strlen((char *)buf) - 1; /* Check the start with AA (this is to test the relative_offset keyword) */ uint8_t *buf2 = (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 buflen2 = strlen((char *)buf2) - 1; Packet *p[2]; p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP); if (p[0] == NULL || p[1] == NULL) goto end; char *sigs[3]; sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; " "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 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;)"; uint32_t sid[3] = {1, 2, 3}; uint32_t results[2][3] = { {0, 0, 0}, {0, 0, 0}}; /* None of the packets should match */ result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3); UTHFreePackets(p, 2); end: return result; } /** * \test DetectAsn1TestReal03 Ensure that all works together */ int DetectAsn1TestReal03(void) { int result = 0; 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'; uint16_t buflen = 261; /* Check the start with AA (this is to test the relative_offset keyword) */ uint8_t *buf2 = (uint8_t *) "AA\x03\x01\xFF"; uint16_t buflen2 = 5; Packet *p[2] = { NULL, NULL }; p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP); if (p[0] == NULL || p[1] == NULL) goto end; char *sigs[3]; /* This should match the first packet */ sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; " "asn1:absolute_offset 0, double_overflow; sid:1;)"; /* This should match the second packet */ sigs[1]= "alert ip any any -> any any (msg:\"Testing id 2\"; " "asn1:relative_offset 2, bitstring_overflow," "oversize_length 140; sid:2;)"; /* This should match no packet */ sigs[2]= "alert ip any any -> any any (msg:\"Testing id 3\"; " "asn1: oversize_length 2000; sid:3;)"; uint32_t sid[3] = {1, 2, 3}; uint32_t results[2][3] = {{1, 0, 0}, {0, 1, 0}}; result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3); UTHFreePackets(p, 2); end: return result; } /** * \test DetectAsn1TestReal04 like the real test 02, but modified the * relative offset to check negative offset values, in this case * start decoding from -7 bytes respect the content match "John" */ int DetectAsn1TestReal04(void) { int result = 0; 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" "\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""Pablo""\x1A\x01""B""\x1A\x05""Jones" "\xA0\x0A\x43\x08""19590717"; uint16_t buflen = strlen((char *)buf) - 1; /* Check the start with AA (this is to test the relative_offset keyword) */ uint8_t *buf2 = (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 buflen2 = strlen((char *)buf2) - 1; Packet *p[2]; p[0] = UTHBuildPacket((uint8_t *)buf, buflen, IPPROTO_TCP); p[1] = UTHBuildPacket((uint8_t *)buf2, buflen2, IPPROTO_TCP); if (p[0] == NULL || p[1] == NULL) goto end; char *sigs[3]; sigs[0]= "alert ip any any -> any any (msg:\"Testing id 1\"; " "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 -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;)"; uint32_t sid[3] = {1, 2, 3}; uint32_t results[2][3] = { {0, 0, 0}, {0, 0, 0}}; /* None of the packets should match */ result = UTHGenericTest(p, 2, sigs, sid, (uint32_t *) results, 3); UTHFreePackets(p, 2); end: return result; } #endif /* UNITTESTS */ /** * \brief this function registers unit tests for DetectAsn1 */ void DetectAsn1RegisterTests(void) { #ifdef UNITTESTS UtRegisterTest("DetectAsn1TestParse01", DetectAsn1TestParse01, 1); UtRegisterTest("DetectAsn1TestParse02", DetectAsn1TestParse02, 1); UtRegisterTest("DetectAsn1TestParse03", DetectAsn1TestParse03, 1); UtRegisterTest("DetectAsn1TestParse04", DetectAsn1TestParse04, 1); UtRegisterTest("DetectAsn1TestParse05", DetectAsn1TestParse05, 1); UtRegisterTest("DetectAsn1TestParse06", DetectAsn1TestParse06, 1); UtRegisterTest("DetectAsn1TestParse07", DetectAsn1TestParse07, 1); UtRegisterTest("DetectAsn1TestParse08", DetectAsn1TestParse08, 1); UtRegisterTest("DetectAsn1TestParse09", DetectAsn1TestParse09, 1); UtRegisterTest("DetectAsn1TestParse10", DetectAsn1TestParse10, 1); UtRegisterTest("DetectAsn1TestParse11", DetectAsn1TestParse11, 1); UtRegisterTest("DetectAsn1TestParse12", DetectAsn1TestParse12, 1); UtRegisterTest("DetectAsn1TestParse13", DetectAsn1TestParse13, 1); UtRegisterTest("DetectAsn1TestParse14", DetectAsn1TestParse14, 1); UtRegisterTest("DetectAsn1TestParse15", DetectAsn1TestParse15, 1); UtRegisterTest("DetectAsn1Test01 - oversize_len", DetectAsn1Test01, 1); UtRegisterTest("DetectAsn1Test02 - oversize_len", DetectAsn1Test02, 1); UtRegisterTest("DetectAsn1Test03 - bitstring_ovf", DetectAsn1Test03, 1); UtRegisterTest("DetectAsn1Test04 - bitstring_ovf", DetectAsn1Test04, 1); UtRegisterTest("DetectAsn1Test05 - double_ovf", DetectAsn1Test05, 1); UtRegisterTest("DetectAsn1Test06 - double_ovf", DetectAsn1Test06, 1); UtRegisterTest("DetectAsn1TestReal01", DetectAsn1TestReal01, 1); UtRegisterTest("DetectAsn1TestReal02", DetectAsn1TestReal02, 1); UtRegisterTest("DetectAsn1TestReal03", DetectAsn1TestReal03, 1); UtRegisterTest("DetectAsn1TestReal04", DetectAsn1TestReal04, 1); #endif /* UNITTESTS */ }