You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
suricata/src/detect-asn1.c

1336 lines
42 KiB
C

/* 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 <pablo.rincon.crespo@gmail.com>
*
* 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 *, 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
*/
static 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;
int32_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 ||
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(NULL, ASN_DELIM);
}
fd = SCMalloc(sizeof(DetectAsn1Data));
if (fd == NULL) {
SCLogError(SC_ERR_MEM_ALLOC, "Error allocating DetectAsn1Data");
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 = (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) {
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 */
}