From 3fa3229e0104ed0553dcc6ba4b3579665a349660 Mon Sep 17 00:00:00 2001 From: Pablo Rincon Date: Thu, 20 May 2010 12:34:22 +0200 Subject: [PATCH] ASN1 decoder and keyword implementation --- src/Makefile.am | 2 + src/decode-asn1.c | 855 ++++++++++++++++++++++++++++++ src/decode-asn1.h | 218 ++++++++ src/decode.h | 1 + src/detect-asn1.c | 1255 +++++++++++++++++++++++++++++++++++++++++++++ src/detect-asn1.h | 47 ++ src/detect.c | 2 + src/detect.h | 2 + src/suricata.c | 1 + 9 files changed, 2383 insertions(+) create mode 100644 src/decode-asn1.c create mode 100644 src/decode-asn1.h create mode 100644 src/detect-asn1.c create mode 100644 src/detect-asn1.h diff --git a/src/Makefile.am b/src/Makefile.am index f340b944ce..2f1b7c7fd0 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -27,6 +27,7 @@ decode-icmpv4.c decode-icmpv4.h \ decode-icmpv6.c decode-icmpv6.h \ decode-tcp.c decode-tcp.h \ decode-udp.c decode-udp.h \ +decode-asn1.c decode-asn1.h \ flow.c flow.h \ flow-queue.c flow-queue.h \ flow-hash.c flow-hash.h \ @@ -116,6 +117,7 @@ detect-dce-stub-data.c detect-dce-stub-data.h \ detect-urilen.c detect-urilen.h \ detect-detection-filter.c detect-detection-filter.h \ detect-http-client-body.c detect-http-client-body.h \ +detect-asn1.c detect-asn1.h \ util-print.c util-print.h \ util-fmemopen.c util-fmemopen.h \ util-cpu.c util-cpu.h \ diff --git a/src/decode-asn1.c b/src/decode-asn1.c new file mode 100644 index 0000000000..94b8cc06e8 --- /dev/null +++ b/src/decode-asn1.c @@ -0,0 +1,855 @@ +/* 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 decode-asn1.c + * + * \author Pablo Rincon Crespo + * + * Implements ASN1 decoding (needed for the asn1 keyword, BER, CER & DER) + */ + +#include "suricata.h" +#include "suricata-common.h" +#include "decode.h" +#include "util-debug.h" +#include "util-unittest.h" +#include "util-print.h" + +#include "decode-asn1.h" + + +/** + * \brief Decode and check the identifier information of the + * current node that is in extended format + * + * \param ac pointer to the ASN1 Context data + * + * \retval byte of the status of the parser + */ +uint8_t SCAsn1GetHighTagNumber(Asn1Ctx *ac) { + uint8_t ret = 0; + uint32_t tag_num = 0; + + /* If we have a high tag num, skip the id octet */ + ac->iter++; + + Asn1Node *node = ASN1CTX_CUR_NODE(ac); + + ret = SCAsn1CheckBounds(ac); + if (ret == ASN1_PARSER_ERR) { + ac->parser_status |= ASN1_STATUS_INVALID | ASN1_STATUS_OOB; + return ret; + } + + uint8_t raw_id = *ac->iter; + + tag_num += ASN1_BER_GET_HIGH_TAG_NUM(raw_id); + + if (ASN1_BER_GET_HIGH_TAG_NUM(raw_id) == 0) { + /* Set event, invalid id */ + node->flags |= ASN1_BER_EVENT_INVALID_ID; + ac->parser_status |= ASN1_STATUS_INVALID; + return ASN1_PARSER_ERR; + } + + ac->iter++; + if (!ASN1_BER_IS_HIGH_TAG_END(raw_id)) { + do { + ret = SCAsn1CheckBounds(ac); + if (ret == ASN1_PARSER_ERR) { + ac->parser_status |= ASN1_STATUS_INVALID | ASN1_STATUS_OOB; + return ret; + } + + raw_id = *ac->iter; + + if ((uint64_t) ((uint64_t)tag_num + + (uint64_t)ASN1_BER_GET_HIGH_TAG_NUM(raw_id)) > UINT32_MAX) + { + node->flags |= ASN1_BER_EVENT_ID_TOO_LONG; + ac->parser_status |= ASN1_STATUS_INVALID; + return ASN1_PARSER_ERR; + } + + tag_num += ASN1_BER_GET_HIGH_TAG_NUM(raw_id); + ac->iter++; + } while (!ASN1_BER_IS_HIGH_TAG_END(raw_id)); + } + node->id.tag_num = tag_num; + + return ASN1_PARSER_OK; +} + +/** + * \brief Decode and check the length, of the current node + * in definite but extended format, that we are parsing, + * checking invalid opts + * + * \param ac pointer to the ASN1 Context data + * + * \retval byte of the status of the parser + */ +uint32_t SCAsn1GetLengthLongForm(Asn1Ctx *ac) { + uint8_t raw_len = *ac->iter; + uint8_t ret = 0; + uint32_t content_len = 0; + uint8_t oct_len = ASN1_BER_GET_LONG_LEN_OCTETS(raw_len); + uint8_t i = 0; + + Asn1Node *node = ASN1CTX_CUR_NODE(ac); + + for (; i < oct_len; i++) { + ac->iter++; + + ret = SCAsn1CheckBounds(ac); + if (ret == ASN1_PARSER_ERR) { + ac->parser_status |= ASN1_STATUS_INVALID | ASN1_STATUS_OOB; + return ASN1_PARSER_ERR; + } + + raw_len = *ac->iter; + if (raw_len == 0xFF && ac->iter == node->len.ptr + 1) { + /* 8.1.3.5, 0xFF shall not be used */ + node->flags |= ASN1_BER_EVENT_INVALID_LEN; + ac->parser_status = ASN1_STATUS_INVALID; + return ASN1_PARSER_ERR; + } + + if ((uint64_t) ((uint64_t)content_len + + (uint64_t) ASN1_BER_GET_HIGH_TAG_NUM(raw_len)) > UINT32_MAX) + { + node->flags |= ASN1_BER_EVENT_LEN_TOO_LONG; + ac->parser_status = ASN1_STATUS_INVALID; + return ASN1_PARSER_ERR; + } + + content_len += raw_len; + } + + ac->iter++; + + node->len.len = content_len; + return ASN1_PARSER_OK; +} + + +/** + * \brief Check the content length and perform other inspections + * and decodings if necessary + * + * \param ac pointer to the ASN1 Context data + * + * \retval byte of the status of the parser + */ +uint8_t SCAsn1DecodeContent(Asn1Ctx *ac) { + + Asn1Node *node = ASN1CTX_CUR_NODE(ac); + + /* Uops, if we are done, we break here */ + if (node->flags & ASN1_NODE_IS_EOC) + return ASN1_PARSER_OK; + + /* First check the form of length (BER, DER, CER) + * and if we are on a zero length */ + if (node->len.form != ASN1_BER_LEN_INDEFINITE && + node->len.len == 0) + { + node->data.len = 0; + return ASN1_PARSER_OK; + } + + node->data.ptr = ac->iter; + /* If we have a complete length, check that + * it is in bounds */ + if (ac->iter + node->len.len > ac->end) { + /* We do not have all the content octets! */ + node->data.len = ac->end - ac->iter; + } else { + /* We have all the content octets */ + node->data.len = node->len.len; + } + + return ASN1_PARSER_OK; +} + +/** + * \brief Decode and check the length, of the current node + * that we are parsing, also check invalid opts + * + * \param ac pointer to the ASN1 Context data + * + * \retval byte of the status of the parser + */ +uint8_t SCAsn1DecodeLength(Asn1Ctx *ac) { + uint8_t ret = 0; + ret = SCAsn1CheckBounds(ac); + if (ret == ASN1_PARSER_ERR) { + ac->parser_status |= ASN1_STATUS_INVALID | ASN1_STATUS_OOB; + return ASN1_PARSER_ERR; + } + + Asn1Node *node = ASN1CTX_CUR_NODE(ac); + /* Store the position */ + node->len.ptr = ac->iter; + + uint8_t len_byte = *ac->iter; + + //SCPrintByteBin(len_byte); + + if (*node->id.ptr == 0 && len_byte == 0) { + node->flags |= ASN1_NODE_IS_EOC; + ac->iter++; + return ASN1_PARSER_OK; + } + + if (ASN1_BER_IS_INDEFINITE_LEN(len_byte)) { + node->len.form = ASN1_BER_LEN_INDEFINITE; + node->len.len = 0; + ac->iter++; + + uint8_t *tmp_iter = ac->iter; + + /* Check that e-o-c is in bounds */ + for (; tmp_iter < ac->end - 1; tmp_iter++) { + if (ASN1_BER_IS_EOC(tmp_iter)) { + node->data.len = tmp_iter - ac->iter; + node->len.len = tmp_iter - ac->iter; + return ASN1_PARSER_OK; + } + } + + /* EOC Not found */ + ac->parser_status |= ASN1_STATUS_INVALID; + node->flags |= ASN1_BER_EVENT_EOC_NOT_FOUND; + + return ASN1_PARSER_ERR; + + } else { + /* Look which form we get (and if it apply to the id type) */ + if (ASN1_BER_IS_SHORT_LEN(len_byte)) { + node->len.form = ASN1_BER_LEN_SHORT; + node->len.len = ASN1_BER_GET_SHORT_LEN(len_byte); + ac->iter++; + } else { + node->len.form = ASN1_BER_LEN_LONG; + + /* Ok, let's parse the long form */ + return SCAsn1GetLengthLongForm(ac); + } + + } + return ASN1_PARSER_OK; +} + +/** + * \brief Decode and check the identifier information of the + * current node that we are parsing, also check invalid opts + * + * \param ac pointer to the ASN1 Context data + * + * \retval byte of the status of the parser + */ +uint8_t SCAsn1DecodeIdentifier(Asn1Ctx *ac) { + uint8_t ret = 0; + ret = SCAsn1CheckBounds(ac); + if (ret == ASN1_PARSER_ERR) { + ac->parser_status |= ASN1_STATUS_INVALID | ASN1_STATUS_OOB; + return ret; + } + + Asn1Node *node = ASN1CTX_CUR_NODE(ac); + /* Store the position */ + node->id.ptr = ac->iter; + + //SCPrintByteBin(*ac->iter); + + node->id.class_tag = ASN1_BER_GET_CLASS_TAG(*ac->iter); + node->id.tag_type = ASN1_BER_IS_CONSTRUCTED(*ac->iter); + + if (ASN1_BER_IS_HIGH_TAG(*ac->iter)) { + return SCAsn1GetHighTagNumber(ac); + } else { + node->id.tag_num = ASN1_BER_GET_LOW_TAG_NUM(*ac->iter); + ac->iter++; + } + + return ASN1_PARSER_OK; +} + +/** + * \brief Helper function that print the bits of a byte + * to check encoding internals + * \param byte value of the byte + */ +void SCPrintByteBin(uint8_t byte) { + uint8_t i = 0; + for (i = 8; i > 0; i--) { + printf("%"PRIu8, ((byte >> (i - 1)) & 0x01)); + if (i == 5) + printf(" "); + } + printf("\n"); +} + +/** + * \brief check if we have remaining data available, + * otherwise the parser should stop + * \param ac Asn1Ctx pointer initialized + * \retval 1 if we are out of bounds, 0 if not + */ +uint8_t SCAsn1CheckBounds(Asn1Ctx *ac) { + return (ac->iter < ac->end && ac->iter >= ac->data)? ASN1_PARSER_OK : ASN1_PARSER_ERR; +} + + +/** + * \brief Create a new ASN1 Parsing context + * + * \retval Asn1Ctx pointer to the new ctx + */ +Asn1Ctx *SCAsn1CtxNew(void) { + Asn1Ctx *ac = SCMalloc(sizeof(Asn1Ctx)); + + if (ac == NULL) + return NULL; + + memset(ac, 0, sizeof(Asn1Ctx)); + return ac; +} + +/** + * \brief Destroy an ASN1 Parsing context + * + * \param Asn1Ctx pointer to the new ctx + */ +void SCAsn1CtxDestroy(Asn1Ctx *ac) { + if (ac == NULL) + return; + + uint16_t i = 0; + for (; i < ac->cur_frame; i++) { + Asn1Node *node = ASN1CTX_GET_NODE(ac, i); + if (node != NULL) { + SCFree(node); + } + } + SCFree(ac); +} + +/** + * \brief Create a new node at the array stack of frames in the ctx + * + * \param ac pointer to the ASN1 ctx + * \param node index of the frame that we are going to allocate + * at the asn1 stack in the parser + * + * \retval Asn1Node pointer to the new node allocated + */ +Asn1Node *SCAsn1CtxNewFrame(Asn1Ctx *ac, uint16_t node) { + if (ac->asn1_stack[node] == NULL) + ac->asn1_stack[node] = SCMalloc(sizeof(Asn1Node)); + + if (ac->asn1_stack[node] == NULL) + return NULL; + + memset(ac->asn1_stack[node], 0, sizeof(Asn1Node)); + return ac->asn1_stack[node]; +} + +/** + * \brief Initialize the data of the ASN1 parser ctx with the asn1 raw buffer + * + * \param ac pointer to the ASN1 ctx + * \param data pointer to the data to process (binary raw of asn1) + * \param length length of the asn1 raw buffer + * + * \retval void + */ +void SCAsn1CtxInit(Asn1Ctx *ac, uint8_t *data, uint16_t length) { + + ac->data = data; + ac->iter = data; + ac->len = length; + ac->end = data + length; + ac->parser_status = ASN1_STATUS_OK; +} + +/** + * \brief Decode the nodes/frames located at certain position/level + * + * \param ac pointer to the ASN1 ctx + * \param node_id node index at the asn1 stack of the ctx + * + * \retval byte of parser status + */ +uint8_t SCAsn1Decode(Asn1Ctx *ac, uint16_t node_id) { + Asn1Node *node = NULL; + uint8_t ret = 0; + + /* while remaining data, and no fatal error, or end, or max stack frames */ + while (ac->iter < ac->end + && !(ac->parser_status & ASN1_STATUS_DONE) + && ac->cur_frame < ASN1_MAX_FRAMES) + { + /* Prepare a new frame */ + if (SCAsn1CtxNewFrame(ac, node_id) == NULL) + break; + + ac->cur_frame = node_id; + node = ASN1CTX_GET_NODE(ac, node_id); + + SCLogDebug("ASN1 Getting ID, cur:%x remaining %"PRIu32, (uint8_t)*ac->iter, (uint32_t)(ac->end - ac->iter)); + + /* Get identifier/tag */ + ret = SCAsn1DecodeIdentifier(ac); + if (ret == ASN1_PARSER_ERR) { + SCLogDebug("Error parsing identifier"); + + node->flags |= ASN1_BER_EVENT_INVALID_ID; + ac->ctx_flags |= node->flags; + + break; + } + + SCLogDebug("ASN1 Getting LEN"); + + /* Get length of content */ + ret = SCAsn1DecodeLength(ac); + if (ret == ASN1_PARSER_ERR) { + SCLogDebug("Error parsing length"); + + node->flags |= ASN1_BER_EVENT_INVALID_LEN; + ac->ctx_flags |= node->flags; + + break; + } + + if ( !(node->flags & ASN1_NODE_IS_EOC)) { + SCLogDebug("ASN1 Getting CONTENT"); + + /* Inspect content */ + ret = SCAsn1DecodeContent(ac); + if (ret == ASN1_PARSER_ERR) { + SCLogDebug("Error parsing content"); + + break; + } + + /* Skip to the next record (if any) */ + if (node->id.tag_type != ASN1_TAG_TYPE_CONSTRUCTED) + /* Is primitive, skip it all (no need to decode it)*/ + ac->iter += node->data.len; + } + + /* Check if we are done with data */ + ret = SCAsn1CheckBounds(ac); + if (ret == ASN1_PARSER_ERR) { + + ac->parser_status |= ASN1_STATUS_DONE; + /* There's no more data available */ + ret = ASN1_PARSER_OK; + + break; + } +#if 0 + printf("Tag Num: %"PRIu32", Tag Type: %"PRIu8", Class:%"PRIu8", Length: %"PRIu32"\n", node->id.tag_num, node->id.tag_type, node->id.class_tag, node->len.len); + printf("Data: \n"); + PrintRawDataFp(stdout, node->data.ptr, node->len.len); + printf(" -- EOD --\n"); +#endif + + /* Stack flags/events here, so we have the resume at the ctx flags */ + ac->ctx_flags |= node->flags; + + /* Check if it's not a primitive type, + * then we need to decode contents */ + if (node->id.tag_type == ASN1_TAG_TYPE_CONSTRUCTED) { + ret = SCAsn1Decode(ac, node_id + 1); + } /* Else we have reached a primitive type and stop the recursion, + * look if we have other branches at the same level */ + + /* But first check if it's a constructed node, and the sum of child + * lengths was more than the length of this frame + * this would mean that we have an overflow at the attributes */ + if (ac->iter > node->data.ptr + node->data.len + 1) { + /* We decoded more length on this frame */ + } + + node_id = ac->cur_frame + 1; + } + + return ret; +} + +/* ----------------------- Unit tests ------------------------ */ +#ifdef UNITTESTS + +/** + * \test Check we handle extended identifiers correctly + */ +int DecodeAsn1Test01(void) { + uint8_t *str = (uint8_t *) "\x3F\x84\x06"; + + Asn1Ctx *ac = SCAsn1CtxNew(); + if (ac == NULL) + return 0; + uint8_t ret = 1; + + uint16_t len = 3; + + SCAsn1CtxInit(ac, str, len); + + SCAsn1Decode(ac, ac->cur_frame); + Asn1Node *node = ASN1CTX_GET_NODE(ac, 0); + if (node->id.tag_num != 10) { + ret = 0; + printf("Error, expected tag_num 10, got %"PRIu32" :", node->id.tag_num); + goto end; + } + +end: + SCAsn1CtxDestroy(ac); + return ret; +} + +/** + * \test Check we handle extended identifiers correctly + */ +int DecodeAsn1Test02(void) { + uint8_t *str = (uint8_t *) "\x3F\x81\x81\x81\x81\x06"; + + Asn1Ctx *ac = SCAsn1CtxNew(); + if (ac == NULL) + return 0; + uint8_t ret = 1; + + uint16_t len = 6; + + SCAsn1CtxInit(ac, str, len); + + SCAsn1Decode(ac, ac->cur_frame); + Asn1Node *node = ASN1CTX_GET_NODE(ac, 0); + if (node->id.tag_num != 10) { + ret = 0; + printf("Error, expected tag_num 10, got %"PRIu32": ", node->id.tag_num); + goto end; + } + +end: + SCAsn1CtxDestroy(ac); + return ret; +} + +/** + * \test Check we handle short identifiers correctly + */ +int DecodeAsn1Test03(void) { + uint8_t *str = (uint8_t *) "\x28"; + + Asn1Ctx *ac = SCAsn1CtxNew(); + if (ac == NULL) + return 0; + uint8_t ret = 1; + + uint16_t len = 1; + + SCAsn1CtxInit(ac, str, len); + + SCAsn1Decode(ac, ac->cur_frame); + Asn1Node *node = ASN1CTX_GET_NODE(ac, 0); + if (node->id.tag_num != 8) { + ret = 0; + printf("Error, expected tag_num 10, got %"PRIu32": ", node->id.tag_num); + goto end; + } + +end: + SCAsn1CtxDestroy(ac); + return ret; +} + +/** + * \test Check we handle extended lengths correctly with indefinite form + */ +int DecodeAsn1Test04(void) { + uint8_t *str = (uint8_t *) "\x3F\x84\x06\x80\x12\x12\x12\x00\x00"; + + Asn1Ctx *ac = SCAsn1CtxNew(); + if (ac == NULL) + return 0; + uint8_t ret = 1; + + uint16_t len = 9; + + SCAsn1CtxInit(ac, str, len); + + SCAsn1Decode(ac, ac->cur_frame); + Asn1Node *node = ASN1CTX_GET_NODE(ac, 0); + if (node->len.len != 3) { + ret = 0; + printf("Error, expected length 3, got %"PRIu32": ", node->len.len); + goto end; + } + +end: + SCAsn1CtxDestroy(ac); + return ret; +} + +/** + * \test Check we handle extended lengths correctly + * in the definite form + */ +int DecodeAsn1Test05(void) { + uint8_t *str = (uint8_t *) "\x3F\x84\x06\x82\x10\x10"; + + Asn1Ctx *ac = SCAsn1CtxNew(); + if (ac == NULL) + return 0; + uint8_t ret = 1; + + uint16_t len = 6; + + SCAsn1CtxInit(ac, str, len); + + SCAsn1Decode(ac, ac->cur_frame); + Asn1Node *node = ASN1CTX_GET_NODE(ac, 0); + if (node->len.len!= 32) { + ret = 0; + printf("Error, expected length 10, got %"PRIu32": ", node->len.len); + goto end; + } + +end: + SCAsn1CtxDestroy(ac); + return ret; +} + +/** + * \test Check we handle short lengths correctly + */ +int DecodeAsn1Test06(void) { + uint8_t *str = (uint8_t *) "\x3F\x84\x06\x26"; + + Asn1Ctx *ac = SCAsn1CtxNew(); + if (ac == NULL) + return 0; + uint8_t ret = 1; + + uint16_t len = 4; + + SCAsn1CtxInit(ac, str, len); + + SCAsn1Decode(ac, ac->cur_frame); + Asn1Node *node = ASN1CTX_GET_NODE(ac, 0); + if (node->len.len != 38) { + ret = 0; + printf("Error, expected length 10, got %"PRIu32": ", node->len.len); + goto end; + } + +end: + SCAsn1CtxDestroy(ac); + return ret; +} + +/** + * \test Check we handle events correctly + */ +int DecodeAsn1Test07(void) { + uint8_t *str = (uint8_t *) "\x3F\x00\x84\x06"; + + Asn1Ctx *ac = SCAsn1CtxNew(); + if (ac == NULL) + return 0; + uint8_t ret = 1; + + uint16_t len = 4; + + SCAsn1CtxInit(ac, str, len); + + SCAsn1Decode(ac, ac->cur_frame); + Asn1Node *node = ASN1CTX_GET_NODE(ac, 0); + if ( !(ac->ctx_flags & ASN1_BER_EVENT_INVALID_ID) + || !(node->flags & ASN1_BER_EVENT_INVALID_ID)) + { + ret = 0; + printf("Error, expected invalid id, got flags %"PRIu8": ", ac->ctx_flags); + goto end; + } + +end: + SCAsn1CtxDestroy(ac); + return ret; +} + +/** + * \test Check we handle events correctly + */ +int DecodeAsn1Test08(void) { + uint8_t *str = (uint8_t *) "\x3F\x84\x06\x81\xFF"; + + Asn1Ctx *ac = SCAsn1CtxNew(); + if (ac == NULL) + return 0; + uint8_t ret = 1; + + uint16_t len = 5; + + SCAsn1CtxInit(ac, str, len); + + SCAsn1Decode(ac, ac->cur_frame); + Asn1Node *node = ASN1CTX_GET_NODE(ac, 0); + if ( !(ac->ctx_flags & ASN1_BER_EVENT_INVALID_LEN) + || !(node->flags & ASN1_BER_EVENT_INVALID_LEN)) + { + ret = 0; + printf("Error, expected invalid length, got flags %"PRIu8": ", ac->ctx_flags); + goto end; + } + +end: + SCAsn1CtxDestroy(ac); + return ret; +} + +/** + * \test Check we handle events correctly + */ +int DecodeAsn1Test09(void) { + uint8_t *str = (uint8_t *) "\x3F\x84\x06\x80\xAB\xCD\xEF"; + + Asn1Ctx *ac = SCAsn1CtxNew(); + if (ac == NULL) + return 0; + uint8_t ret = 1; + + uint16_t len = 7; + + SCAsn1CtxInit(ac, str, len); + + SCAsn1Decode(ac, ac->cur_frame); + Asn1Node *node = ASN1CTX_GET_NODE(ac, 0); + if ( !(ac->ctx_flags & ASN1_BER_EVENT_EOC_NOT_FOUND) + || !(node->flags & ASN1_BER_EVENT_EOC_NOT_FOUND)) + { + ret = 0; + printf("Error, expected eoc not found, got flags %"PRIu8": ", ac->ctx_flags); + goto end; + } + +end: + SCAsn1CtxDestroy(ac); + return ret; +} + +/** + * \test Decode a big chunk of data + */ +int DecodeAsn1Test10(void) { + // 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" + "\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; + uint8_t ret = 1; + + uint16_t len = strlen((char *)str)-1; + + SCAsn1CtxInit(ac, str, len); + + ret = SCAsn1Decode(ac, ac->cur_frame); + + /* General checks */ + if (ret != ASN1_PARSER_OK) { + printf("Error decoding asn1 data: "); + ret = 0; + goto end; + } + + if (ac->cur_frame != 59) { + printf("Error decoding asn1 data, not all the nodes" + "were correctly decoded: "); + ret = 0; + goto end; + } + + if (ac->iter != ac->end) { + printf("Error decoding asn1 data, not all the nodes" + "were correctly decoded: "); + ret = 0; + goto end; + } + + Asn1Node *node = ASN1CTX_GET_NODE(ac, 0); + if (node->len.len != 133) { + printf("Error decoding asn1 data, not all the nodes" + "were correctly decoded: "); + ret = 0; + goto end; + } + + node = ASN1CTX_GET_NODE(ac, 30); + if (node->len.len != 133) { + printf("Error decoding asn1 data, not all the nodes" + "were correctly decoded: "); + ret = 0; + goto end; + } + +end: + SCAsn1CtxDestroy(ac); + return ret; +} + +#endif + +void DecodeAsn1RegisterTests(void) { +#ifdef UNITTESTS + UtRegisterTest("DecodeAsn1Test01", DecodeAsn1Test01, 1); + UtRegisterTest("DecodeAsn1Test02", DecodeAsn1Test02, 1); + UtRegisterTest("DecodeAsn1Test03", DecodeAsn1Test03, 1); + + UtRegisterTest("DecodeAsn1Test04", DecodeAsn1Test04, 1); + UtRegisterTest("DecodeAsn1Test05", DecodeAsn1Test05, 1); + UtRegisterTest("DecodeAsn1Test06", DecodeAsn1Test06, 1); + + UtRegisterTest("DecodeAsn1Test07", DecodeAsn1Test07, 1); + UtRegisterTest("DecodeAsn1Test08", DecodeAsn1Test08, 1); + UtRegisterTest("DecodeAsn1Test09", DecodeAsn1Test09, 1); + + UtRegisterTest("DecodeAsn1Test10", DecodeAsn1Test10, 1); +#endif +} + diff --git a/src/decode-asn1.h b/src/decode-asn1.h new file mode 100644 index 0000000000..b7b8fcdba6 --- /dev/null +++ b/src/decode-asn1.h @@ -0,0 +1,218 @@ +/* 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 decode-asn1.h + * + * \author Pablo Rincon Crespo + * + * Implements ASN1 decoding (needed for the asn1 keyword) + */ + +#ifndef __DECODE_ASN1_H__ +#define __DECODE_ASN1_H__ +#include +#include +#include +#include +#include +#include +#include +#include + +#define ASN1_MAX_FRAMES 128 + +/* For future enconding type implementations */ +enum { + ASN1_BER_ENC, + ASN1_ENC_UNKNOWN +}; + +/* Class of tag */ +#define ASN1_BER_CLASS_UNIV 0 +#define ASN1_BER_CLASS_APP 1 +#define ASN1_BER_CLASS_CTX_SPECIFIC 2 +#define ASN1_BER_CLASS_PRIV 3 + +/* For low tag numbers */ +#define ASN1_BER_GET_CLASS_TAG(id_octet) \ + ((id_octet >> 6) & 0x03) /* (8.1.2.2a) */ +#define ASN1_BER_IS_CONSTRUCTED(id_octet) \ + ((id_octet >> 5) & 0x01) /* (8.1.2.5) Constructed Tag */ +#define ASN1_BER_IS_PRIMITIVE(id_octet) \ + (((id_octet >> 5) & 0x01)?0:1) /* (8.1.2.5) Primitive Tag */ +#define ASN1_BER_IS_LOW_TAG(id_octet) \ + ASN1_BER_IS_PRIMITIVE(id_octet) /* (8.1.2.5) Is Low Tag + Number */ +#define ASN1_BER_GET_LOW_TAG_NUM(id_octet) \ + (id_octet & 0x1F) /* (8.1.2.2c) Get LowTag Number */ + +/* For high tag numbers */ +#define ASN1_BER_IS_HIGH_TAG(id_octet) \ + ((ASN1_BER_GET_LOW_TAG_NUM(id_octet) == 0x1F) && \ + ASN1_BER_IS_CONSTRUCTED(id_octet)) /* (8.1.2.4) High Tag Number */ +#define ASN1_BER_IS_HIGH_TAG_END(id_octet) \ + ( !((id_octet >> 7) & 0x01)) /* (8.1.2.4) Is End of Tag Num */ +#define ASN1_BER_GET_HIGH_TAG_NUM(id_octet) \ + (id_octet & 0x7F) /* (8.1.2.4) Part of High Tag + Number */ + + +#define ASN1_BER_IS_SHORT_LEN(id_octet) \ + ( !((id_octet >> 7) & 0x01)) /* (8.1.3.3) Is short form */ +#define ASN1_BER_GET_SHORT_LEN(id_octet) \ + (id_octet & 0x7F) /* (8.1.3.3) length value */ +#define ASN1_BER_GET_LONG_LEN_OCTETS(id_octet) \ + (id_octet & 0x7F) /* (8.1.3.5) the number of + bytes */ +#define ASN1_BER_GET_LONG_LEN(id_octet) \ + (id_octet) /* (8.1.3.5) the byte itself*/ +#define ASN1_BER_LONG_LEN_HAS_NEXT(id_octet) \ + ( !((id_octet >> 7) & 0x01)) /* (8.1.3.5) Has next octets + lenght */ +#define ASN1_BER_IS_INDEFINITE_LEN(id_octet) \ + (id_octet == 0x80) /* (8.1.3.6) Need end-of-ccontent */ +#define ASN1_BER_IS_EOC(tmp_iter) (*tmp_iter == 0 && *(tmp_iter + 1) == 0) + +/* Return the current node/frame that we are filling */ +#define ASN1CTX_CUR_NODE(ac) (ac->asn1_stack[ac->cur_frame]) +#define ASN1CTX_GET_NODE(ac, node) (ac->asn1_stack[node]) + +/* BER Universal tags */ +#define ASN1_UNITAG_EOC 0 /* EOC */ +#define ASN1_UNITAG_BOOLEAN 1 +#define ASN1_UNITAG_INTEGER 2 +#define ASN1_UNITAG_BIT_STRING 3 +#define ASN1_UNITAG_OCTET_STRING 4 +#define ASN1_UNITAG_NULL 5 +#define ASN1_UNITAG_OID 6 +#define ASN1_UNITAG_OBJECT_DESCRIPTOR 7 +#define ASN1_UNITAG_EXTERNAL 8 +#define ASN1_UNITAG_REAL 9 +#define ASN1_UNITAG_ENUMERATED 10 +#define ASN1_UNITAG_EMBEDDED_PDV 11 +#define ASN1_UNITAG_UTF8_STRING 12 +#define ASN1_UNITAG_RELATIVE_OID 13 +#define ASN1_UNITAG_SEQUENCE 16 +#define ASN1_UNITAG_SET 17 +#define ASN1_UNITAG_NUMERIC_STRING 18 +#define ASN1_UNITAG_PRINTABLE_STRING 19 +#define ASN1_UNITAG_TELETEX_STRING 20 +#define ASN1_UNITAG_VIDEOTEX_STRING 21 +#define ASN1_UNITAG_IA5_STRING 22 +#define ASN1_UNITAG_UTCTIME 23 +#define ASN1_UNITAG_GENERALIZED_TIME 24 +#define ASN1_UNITAG_GRAPHIC_STRING 25 +#define ASN1_UNITAG_VISIBLE_STRING 26 +#define ASN1_UNITAG_GENERAL_STRING 27 +#define ASN1_UNITAG_UNIVERSAL_STRING 28 +#define ASN1_UNITAG_CHARACTER_STRING 29 +#define ASN1_UNITAG_BMP_STRING 30 + +/* Length form */ +#define ASN1_BER_LEN_SHORT 0 +#define ASN1_BER_LEN_LONG 1 +#define ASN1_BER_LEN_INDEFINITE 2 + + +/* Error events/flags */ +#define ASN1_BER_EVENT_ID_TOO_LONG 0x01 +#define ASN1_BER_EVENT_INVALID_ID 0x02 /* (8.1.2.4.2c) First subsequent + id val (from bit 7 to 0) Shall + not be 0 */ +#define ASN1_BER_EVENT_INVALID_LEN 0x04 /* (8.1.3.2a) we expect a simple + form, or (8.1.3.5c) we got + 0xFF, or not enough data */ +#define ASN1_BER_EVENT_LEN_TOO_LONG 0x08 +#define ASN1_BER_EVENT_EOC_NOT_FOUND 0x10 /* EOC not found */ + + +/* Helper flags */ +#define ASN1_NODE_IS_EOC 1 +#define ASN1_TAG_TYPE_PRIMITIVE 0 +#define ASN1_TAG_TYPE_CONSTRUCTED 1 + +typedef struct Asn1Len_ { + uint8_t form; + uint32_t len; + uint8_t *ptr; +} Asn1Len; + +typedef struct Asn1Id_ { + uint8_t *ptr; + uint8_t class_tag; + uint8_t tag_type; + uint32_t tag_num; +} Asn1Id; + +typedef struct Asn1Data_ { + uint8_t *ptr; + uint32_t len; + uint8_t type; +} Asn1Data; + +typedef struct Asn1Node_ { + uint8_t *raw_str; + uint8_t data_len; + Asn1Len len; + Asn1Id id; + Asn1Data data; + uint8_t flags; +} Asn1Node; + +typedef struct Asn1Ctx_ { + uint8_t *data; + uint8_t *end; + uint16_t len; + + uint8_t *iter; + + uint8_t cur_frame; + Asn1Node *asn1_stack[ASN1_MAX_FRAMES]; + + uint8_t parser_status; + + uint8_t ctx_flags; +} Asn1Ctx; + +/* Return codes of the decoder */ +#define ASN1_PARSER_OK 0x01 /* Everything ok */ +#define ASN1_PARSER_ERR 0x02 /* Internal error, fatal error, we can't continue decoding */ + +/* Status of the parser */ +#define ASN1_STATUS_OK 0x00 /* On the road */ +#define ASN1_STATUS_INVALID 0x01 /* We found something weird/invalid by the specification, but we can try to continue parsing */ +#define ASN1_STATUS_OOB 0x02 /* We don't have enough data or ran out of bounds */ +#define ASN1_STATUS_DONE 0x04 /* We have finished cleanly */ + +void SCPrintByteBin(uint8_t); + +Asn1Ctx *SCAsn1CtxNew(void); +void SCAsn1CtxInit(Asn1Ctx *, uint8_t *, uint16_t); +void SCAsn1CtxDestroy(Asn1Ctx *); + +uint8_t SCAsn1Decode(Asn1Ctx *, uint16_t); +uint8_t SCAsn1DecodeIdentifier(Asn1Ctx *); +uint8_t SCAsn1DecodeLength(Asn1Ctx *); +uint8_t SCAsn1DecodeContent(Asn1Ctx *); + +uint8_t SCAsn1CheckBounds(Asn1Ctx *); + +void DecodeAsn1RegisterTests(void); + +#endif /* __DECODE_ASN1_H__ */ + diff --git a/src/decode.h b/src/decode.h index 7f81408e5d..efce18bc43 100644 --- a/src/decode.h +++ b/src/decode.h @@ -54,6 +54,7 @@ #include "decode-udp.h" #include "decode-raw.h" #include "decode-vlan.h" +#include "decode-asn1.h" #include "detect-reference.h" diff --git a/src/detect-asn1.c b/src/detect-asn1.c new file mode 100644 index 0000000000..c883e5fbcf --- /dev/null +++ b/src/detect-asn1.c @@ -0,0 +1,1255 @@ +/* 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 "decode-asn1.h" + +/* delimiters for functions/arguments */ +const char *ASN_DELIM = " \t,\n"; + +int DetectAsn1Match(ThreadVars *, DetectEngineThreadCtx *, Packet *, + Signature *, SigMatch *); +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 + */ +uint8_t DetectAsn1Checks(Asn1Node *node, 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, SigMatch *m) +{ + uint8_t ret = 0; + + if (p->payload_len == 0) { + /* No error, parser done, no data in bounds to decode */ + return 0; + } + + DetectAsn1Data *ad = (DetectAsn1Data *)m->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]->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; + uint32_t rel_off = 0; + uint8_t flags = 0; + + tok = strtok(asn1str, ASN_DELIM); + 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(NULL, ASN_DELIM); + 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(NULL, ASN_DELIM); + 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(NULL, ASN_DELIM); + if (tok == NULL || + ByteExtractStringUint32(&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(NULL, ASN_DELIM); + } + + fd = SCMalloc(sizeof(fd)); + if (fd == NULL) { + SCLogError(SC_ERR_MEM_ALLOC, "Error allocating DetectAsn1Data"); + exit(EXIT_FAILURE); + } + + 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 = (void *)ad; + + SigMatchAppendPacket(s, sm); + + 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 && ad->oversize_length == 1024 + && (ad->flags & ASN1_OVERSIZE_LEN)) { + DetectAsn1Free(ad); + result = 1; + } + + 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 = 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 == 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]; + + 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; +} + +#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); + +#endif /* UNITTESTS */ +} diff --git a/src/detect-asn1.h b/src/detect-asn1.h new file mode 100644 index 0000000000..c38d6439e7 --- /dev/null +++ b/src/detect-asn1.h @@ -0,0 +1,47 @@ +/* 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.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 */ + int32_t absolute_offset; /* Length argument if needed */ + int32_t relative_offset; /* Length argument if needed */ +} DetectAsn1Data; + +/* prototypes */ +void DetectAsn1Register (void); + +#endif /* __DETECT_ASN1_H__ */ + diff --git a/src/detect.c b/src/detect.c index 14ceb7ecc7..293b7492b3 100644 --- a/src/detect.c +++ b/src/detect.c @@ -87,6 +87,7 @@ #include "detect-isdataat.h" #include "detect-id.h" #include "detect-rpc.h" +#include "detect-asn1.h" #include "detect-dsize.h" #include "detect-flowvar.h" #include "detect-flowint.h" @@ -3070,6 +3071,7 @@ void SigTableSetup(void) { DetectDetectionFilterRegister(); DetectHttpHeaderRegister(); DetectHttpClientBodyRegister(); + DetectAsn1Register(); uint8_t i = 0; for (i = 0; i < DETECT_TBLSIZE; i++) { diff --git a/src/detect.h b/src/detect.h index 70419049e6..731449d103 100644 --- a/src/detect.h +++ b/src/detect.h @@ -689,6 +689,8 @@ enum { DETECT_DCE_OPNUM, DETECT_DCE_STUB_DATA, + DETECT_ASN1, + /* make sure this stays last */ DETECT_TBLSIZE, }; diff --git a/src/suricata.c b/src/suricata.c index f2097e65ac..43887509c1 100644 --- a/src/suricata.c +++ b/src/suricata.c @@ -742,6 +742,7 @@ int main(int argc, char **argv) DecodeTCPRegisterTests(); DecodeUDPV4RegisterTests(); DecodeGRERegisterTests(); + DecodeAsn1RegisterTests(); AlpDetectRegisterTests(); ConfRegisterTests(); ConfYamlRegisterTests();