From: Pierre Chifflier Date: Fri, 4 Nov 2011 17:18:44 +0000 (+0100) Subject: Add ASN.1 parser for X509 certificates (in DER format) X-Git-Tag: suricata-1.3beta1~99 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=f77fcdb3e8df731a4d1d642b1da8eb9f242fd1db;p=thirdparty%2Fsuricata.git Add ASN.1 parser for X509 certificates (in DER format) Signed-off-by: Pierre Chifflier --- diff --git a/src/Makefile.am b/src/Makefile.am index f3c9e7c451..25952fcc98 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -224,6 +224,8 @@ util-cuda-handlers.c util-cuda-handlers.h \ util-optimize.h \ util-privs.c util-privs.h \ util-decode-asn1.c util-decode-asn1.h \ +util-decode-der.c util-decode-der.h \ +util-decode-der-get.c util-decode-der-get.h \ util-ringbuffer.c util-ringbuffer.h \ util-validate.h util-affinity.h util-affinity.c \ util-memcmp.c util-memcmp.h \ diff --git a/src/util-decode-der-get.c b/src/util-decode-der-get.c new file mode 100644 index 0000000000..d1670adbbf --- /dev/null +++ b/src/util-decode-der-get.c @@ -0,0 +1,242 @@ +/* + * Copyright (C) 2011-2012 ANSSI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * \author Pierre Chifflier + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "suricata-common.h" + +#include "util-decode-der.h" +#include "util-decode-der-get.h" + +static const uint8_t SEQ_IDX_ISSUER[] = { 0, 2 }; +static const uint8_t SEQ_IDX_SUBJECT[] = { 0, 4 }; + +static const char *Oid2ShortStr(const char *oid) +{ + if (strcmp(oid, "1.2.840.113549.1.9.1")==0) + return "emailAddress"; + + if (strcmp(oid, "2.5.4.3")==0) + return "CN"; + + if (strcmp(oid, "2.5.4.5")==0) + return "serialNumber"; + + if (strcmp(oid, "2.5.4.6")==0) + return "C"; + + if (strcmp(oid, "2.5.4.7")==0) + return "L"; + + if (strcmp(oid, "2.5.4.8")==0) + return "ST"; + + if (strcmp(oid, "2.5.4.10")==0) + return "O"; + + if (strcmp(oid, "2.5.4.11")==0) + return "OU"; + + return "unknown"; +} + +/** + * \brief Iterate through an ASN.1 structure, following the index sequence. + * Context specific elements are skipped. + * + * \retval The matching node, or NULL + */ +const Asn1Generic * Asn1DerGet(const Asn1Generic *top, const uint8_t *seq_index, const uint32_t seqsz) +{ + const Asn1Generic * node; + uint8_t idx, i; + uint8_t offset = 0; + + node = top; + if (node == NULL || seq_index == NULL) + return NULL; + + for (offset=0; offsetdata == NULL) + return NULL; + + /* skip context-specific elements */ + while (node->data->header.cls == ASN1_CLASS_CONTEXTSPEC) { + node = node->next; + if (node == NULL || node->data == NULL) + return NULL; + } + + node = node->next; + if (node == NULL || node->data == NULL) + return NULL; + } + + /* skip context-specific elements */ + if (node == NULL || node->data == NULL) + return NULL; + while (node->data->header.cls == ASN1_CLASS_CONTEXTSPEC) { + node = node->next; + if (node == NULL || node->data == NULL) + return NULL; + } + + node = node->data; + } + + return node; +} + +int Asn1DerGetIssuerDN(const Asn1Generic *cert, char *buffer, uint32_t length) +{ + const Asn1Generic *node_oid; + const Asn1Generic *node, *it; + const Asn1Generic *node_set; + const Asn1Generic *node_str; + const char *shortname; + int rc = -1; + const char *separator = ", "; + + if (length < 10) + goto issuer_dn_error; + buffer[0] = '\0'; + + node = Asn1DerGet(cert, SEQ_IDX_ISSUER, sizeof(SEQ_IDX_ISSUER)); + if ((node == NULL) || node->type != ASN1_SEQUENCE) + goto issuer_dn_error; + + it = node; + while (it != NULL) { + if (it->data == NULL) + goto issuer_dn_error; + node_set = it->data; + if (node_set->type != ASN1_SET || node_set->data == NULL) + goto issuer_dn_error; + node = node_set->data; + if (node->type != ASN1_SEQUENCE || node->data == NULL) + goto issuer_dn_error; + node_oid = node->data; + if (node_oid->str == NULL || node_oid->type != ASN1_OID) + goto issuer_dn_error; + shortname = Oid2ShortStr(node_oid->str); + if (node->next == NULL) + goto issuer_dn_error; + node = node->next; + node_str = node->data; + if (node_str == NULL || !(node_str->type == ASN1_PRINTSTRING || node_str->type == ASN1_IA5STRING || node_str->type == ASN1_T61STRING)) + goto issuer_dn_error; + + strlcat(buffer, shortname, length); + strlcat(buffer, "=", length); + strlcat(buffer, node_str->str, length); + if (strcmp(shortname,"CN")==0) + separator = "/"; + if (it->next != NULL) + strlcat(buffer, separator, length); + it = it->next; + } + + rc = 0; +issuer_dn_error: + return rc; +} + +int Asn1DerGetSubjectDN(const Asn1Generic *cert, char *buffer, uint32_t length) +{ + const Asn1Generic *node_oid; + const Asn1Generic *node, *it; + const Asn1Generic *node_set; + const Asn1Generic *node_str; + const char *shortname; + int rc = -1; + const char *separator = ", "; + + if (length < 10) + goto subject_dn_error; + buffer[0] = '\0'; + + node = Asn1DerGet(cert, SEQ_IDX_SUBJECT, sizeof(SEQ_IDX_SUBJECT)); + + if ((node == NULL) || node->type != ASN1_SEQUENCE) + goto subject_dn_error; + + it = node; + while (it != NULL) { + if (it == NULL || it->data == NULL) + goto subject_dn_error; + node_set = it->data; + if (node_set->type != ASN1_SET || node_set->data == NULL) + goto subject_dn_error; + node = node_set->data; + if (node->type != ASN1_SEQUENCE || node->data == NULL) + goto subject_dn_error; + node_oid = node->data; + if (node_oid->str == NULL || node_oid->type != ASN1_OID) + goto subject_dn_error; + shortname = Oid2ShortStr(node_oid->str); + if (node->next == NULL) + goto subject_dn_error; + node = node->next; + node_str = node->data; + if (node_str == NULL || !(node_str->type == ASN1_PRINTSTRING || node_str->type == ASN1_IA5STRING || node_str->type == ASN1_T61STRING)) + goto subject_dn_error; + + strlcat(buffer, shortname, length); + strlcat(buffer, "=", length); + strlcat(buffer, node_str->str, length); + if (strcmp(shortname,"CN")==0) + separator = "/"; + if (it->next != NULL) + strlcat(buffer, separator, length); + it = it->next; + } + + rc = 0; +subject_dn_error: + return rc; +} + +/* vim: set et ts=4 sw=4: */ diff --git a/src/util-decode-der-get.h b/src/util-decode-der-get.h new file mode 100644 index 0000000000..9d2d323b8c --- /dev/null +++ b/src/util-decode-der-get.h @@ -0,0 +1,43 @@ +/* + * Copyright (C) 2011-2012 ANSSI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * \author Pierre Chifflier + * + */ + +#ifndef __UTIL_DECODE_DER_GET_H__ +#define __UTIL_DECODE_DER_GET_H__ + +const Asn1Generic * Asn1DerGet(const Asn1Generic *top, const uint8_t *seq_index, const uint32_t seqsz); + +int Asn1DerGetIssuerDN(const Asn1Generic *cert, char *buffer, uint32_t length); +int Asn1DerGetSubjectDN(const Asn1Generic *cert, char *buffer, uint32_t length); + +#endif /* __UTIL_DECODE_DER_GET_H__ */ diff --git a/src/util-decode-der.c b/src/util-decode-der.c new file mode 100644 index 0000000000..8748f1f646 --- /dev/null +++ b/src/util-decode-der.c @@ -0,0 +1,726 @@ +/* + * Copyright (C) 2011-2012 ANSSI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * \author Pierre Chifflier + * + */ + +/* + * An ASN.1 Parser for DER-encoded structures. + * This parser is not written to be complete or fast, but is rather + * focused on stability and security. + * It does not support all ASN.1 structure, only a meaningful subset + * to decode x509v3 certificates (See RFC 3280). + * + * References (like 8.19.4) are relative to the ISO/IEC 8825-1:2003 document + * + */ + +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include "suricata-common.h" + +#include "util-decode-der.h" + +#define MAX_OID_LENGTH 256 + +static Asn1Generic * DecodeAsn1DerBitstring(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerBoolean(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerIA5String(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerInteger(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerNull(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerOctetString(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerOid(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerPrintableString(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerSequence(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerSet(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerT61String(const unsigned char *buffer, uint32_t size, uint8_t depth); +static Asn1Generic * DecodeAsn1DerUTCTime(const unsigned char *buffer, uint32_t size, uint8_t depth); + +static Asn1Generic * Asn1GenericNew(void) +{ + Asn1Generic *obj; + + obj = SCMalloc(sizeof(Asn1Generic)); + if (obj != NULL) + memset(obj, 0, sizeof(Asn1Generic)); + + return obj; +} + +static void Asn1SequenceAppend(Asn1Generic *seq, Asn1Generic *node) +{ + Asn1Generic *it, *new_container; + + if (seq->data == NULL) { + seq->data = node; + return; + } + + new_container = Asn1GenericNew(); + if (new_container == NULL) + return; + new_container->data = node; + + for (it=seq; it->next != NULL; it=it->next) + ; + + it->next = new_container; +} + +static Asn1Generic * DecodeAsn1DerGeneric(const unsigned char *buffer, uint32_t max_size, uint8_t depth, int seq_index) +{ + const unsigned char *d_ptr = buffer; + uint32_t numbytes, el_max_size; + Asn1ElementType el; + uint8_t c; + uint32_t i; + Asn1Generic *child; + uint8_t el_type; + + el.cls = (d_ptr[0] & 0xc0) >> 6; + el.pc = (d_ptr[0] & 0x20) >> 5; + el.tag = (d_ptr[0] & 0x1f); + + el_type = el.tag; + + if (el.tag == 0x1f) + return NULL; + + switch (el.cls) { + case ASN1_CLASS_CONTEXTSPEC: + /* get element type from definition + * see http://www.ietf.org/rfc/rfc3280.txt) + */ + if (depth == 2 && el.tag == 0) { + el_type = ASN1_SEQUENCE; /* TBSCertificate */ + break; + } + if (depth == 2 && el.tag == 1) { + el_type = ASN1_BITSTRING; /* issuerUniqueID */ + break; + } + if (depth == 2 && el.tag == 2) { + el_type = ASN1_BITSTRING; /* subjectUniqueID */ + break; + } + if (depth == 2 && el.tag == 3) { + el_type = ASN1_SEQUENCE; /* extensions */ + break; + } + /* unknown context specific value - do not decode */ + break; + }; + + el_max_size = max_size - (d_ptr-buffer); + switch (el_type) { + case ASN1_INTEGER: + child = DecodeAsn1DerInteger(d_ptr, el_max_size, depth+1); + break; + case ASN1_BOOLEAN: + child = DecodeAsn1DerBoolean(d_ptr, el_max_size, depth+1); + break; + case ASN1_NULL: + child = DecodeAsn1DerNull(d_ptr, el_max_size, depth+1); + break; + case ASN1_BITSTRING: + child = DecodeAsn1DerBitstring(d_ptr, el_max_size, depth+1); + break; + case ASN1_OID: + child = DecodeAsn1DerOid(d_ptr, el_max_size, depth+1); + break; + case ASN1_IA5STRING: + child = DecodeAsn1DerIA5String(d_ptr, el_max_size, depth+1); + break; + case ASN1_OCTETSTRING: + child = DecodeAsn1DerOctetString(d_ptr, el_max_size, depth+1); + break; + case ASN1_PRINTSTRING: + child = DecodeAsn1DerPrintableString(d_ptr, el_max_size, depth+1); + break; + case ASN1_SEQUENCE: + child = DecodeAsn1DerSequence(d_ptr, el_max_size, depth+1); + break; + case ASN1_SET: + child = DecodeAsn1DerSet(d_ptr, el_max_size, depth+1); + break; + case ASN1_T61STRING: + child = DecodeAsn1DerT61String(d_ptr, el_max_size, depth+1); + break; + case ASN1_UTCTIME: + child = DecodeAsn1DerUTCTime(d_ptr, el_max_size, depth+1); + break; + default: + /* unknown ASN.1 type */ + child = NULL; + child = Asn1GenericNew(); + if (child == NULL) + break; + child->type = el.tag; + /* total sequence length */ + const unsigned char * save_d_ptr = d_ptr; + d_ptr++; + c = d_ptr[0]; + if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */ + child->length = c; + d_ptr++; + } else { /* long form 8.1.3.5 */ + numbytes = c & 0x7f; + child->length = 0; + d_ptr++; + for (i=0; ilength = child->length<<8 | d_ptr[0]; + d_ptr++; + } + } + /* fix the length for unknown objects, else + * sequence parsing will fail + */ + child->length += (d_ptr - save_d_ptr); + break; + }; + if (child == NULL) + return NULL; + + child->header = el; + return child; +} + +static Asn1Generic * DecodeAsn1DerInteger(const unsigned char *buffer, uint32_t size, uint8_t depth) +{ + const unsigned char *d_ptr = buffer; + uint8_t numbytes; + uint32_t value; + uint32_t i; + Asn1Generic *a; + + numbytes = d_ptr[1]; + d_ptr += 2; + + value = 0; + for (i=0; itype = ASN1_INTEGER; + a->length = (d_ptr - buffer) + numbytes; + a->value = value; + + a->str = SCMalloc(2*numbytes + 1); + if (a->str == NULL) { + SCFree(a); + return NULL; + } + for (i=0; istr + 2*i, 2*(numbytes-i)+1, "%02X", d_ptr[i]); + } + a->str[2*numbytes]='\0'; + + return a; +} + +static Asn1Generic * DecodeAsn1DerBoolean(const unsigned char *buffer, uint32_t size, uint8_t depth) +{ + const unsigned char *d_ptr = buffer; + uint8_t numbytes; + uint32_t value; + uint32_t i; + Asn1Generic *a; + + numbytes = d_ptr[1]; + d_ptr += 2; + + value = 0; + for (i=0; itype = ASN1_BOOLEAN; + a->length = (d_ptr - buffer); + a->value = value; + + return a; +} + +static Asn1Generic * DecodeAsn1DerNull(const unsigned char *buffer, uint32_t size, uint8_t depth) +{ + const unsigned char *d_ptr = buffer; + uint8_t numbytes; + uint32_t value; + uint32_t i; + Asn1Generic *a; + + numbytes = d_ptr[1]; + d_ptr += 2; + + value = 0; + for (i=0; itype = ASN1_NULL; + a->length = (d_ptr - buffer); + a->value = 0; + + return a; +} + +static Asn1Generic * DecodeAsn1DerBitstring(const unsigned char *buffer, uint32_t max_size, uint8_t depth) +{ + const unsigned char *d_ptr = buffer; + uint32_t length; + uint8_t numbytes, c; + Asn1Generic *a; + uint32_t i; + + d_ptr++; + + /* size */ + c = d_ptr[0]; + if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */ + length = c; + d_ptr++; + } else { /* long form 8.1.3.5 */ + numbytes = c & 0x7f; + length = 0; + d_ptr++; + for (i=0; i max_size) + return NULL; + + a = Asn1GenericNew(); + if (a == NULL) + return NULL; + a->type = ASN1_BITSTRING; + a->strlen = length; + a->str = SCMalloc(length); + if (a->str == NULL) { + SCFree(a); + return NULL; + } + memcpy(a->str, (const char*)d_ptr, length); + + d_ptr += length; + + a->length = (d_ptr - buffer); + return a; +} + +static Asn1Generic * DecodeAsn1DerOid(const unsigned char *buffer, uint32_t max_size, uint8_t depth) +{ + const unsigned char *d_ptr = buffer; + uint32_t oid_length, oid_value; + uint8_t numbytes, c; + Asn1Generic *a; + uint32_t i; + + d_ptr++; + + /* size */ + c = d_ptr[0]; + if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */ + oid_length = c; + d_ptr++; + } else { /* long form 8.1.3.5 */ + numbytes = c & 0x7f; + oid_length = 0; + d_ptr++; + for (i=0; i max_size) + return NULL; + + a = Asn1GenericNew(); + if (a == NULL) + return NULL; + a->type = ASN1_OID; + a->str = SCMalloc(MAX_OID_LENGTH); + if (a->str == NULL) { + SCFree(a); + return NULL; + } + + /* first element = X*40 + Y (See 8.19.4) */ + snprintf(a->str, MAX_OID_LENGTH, "%d.%d", (d_ptr[0]/40), (d_ptr[0]%40)); + d_ptr++; + + for (i=1; istr); + c = d_ptr[0]; + oid_value = 0; + while ( istr + s, MAX_OID_LENGTH - s, ".%d", oid_value); + } + + a->length = (d_ptr - buffer); + return a; +} + +static Asn1Generic * DecodeAsn1DerIA5String(const unsigned char *buffer, uint32_t max_size, uint8_t depth) +{ + const unsigned char *d_ptr = buffer; + uint32_t length, numbytes; + uint32_t i; + Asn1Generic *a; + unsigned char c; + + d_ptr++; + + /* total sequence length */ + c = d_ptr[0]; + if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */ + length = c; + d_ptr++; + } else { /* long form 8.1.3.5 */ + numbytes = c & 0x7f; + length = 0; + d_ptr++; + for (i=0; i max_size) + return NULL; + + a = Asn1GenericNew(); + if (a == NULL) + return NULL; + a->type = ASN1_IA5STRING; + a->strlen = length; + a->str = SCMalloc(length+1); + if (a->str == NULL) { + SCFree(a); + return NULL; + } + strlcpy(a->str, (const char*)d_ptr, length+1); + + d_ptr += length; + + a->length = (d_ptr - buffer); + return a; +} + +static Asn1Generic * DecodeAsn1DerOctetString(const unsigned char *buffer, uint32_t max_size, uint8_t depth) +{ + const unsigned char *d_ptr = buffer; + uint32_t length, numbytes; + uint32_t i; + Asn1Generic *a; + unsigned char c; + + d_ptr++; + + /* total sequence length */ + c = d_ptr[0]; + if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */ + length = c; + d_ptr++; + } else { /* long form 8.1.3.5 */ + numbytes = c & 0x7f; + length = 0; + d_ptr++; + for (i=0; i max_size) + return NULL; + + a = Asn1GenericNew(); + if (a == NULL) + return NULL; + a->type = ASN1_OCTETSTRING; + a->strlen = length; + a->str = SCMalloc(length); + if (a->str == NULL) { + SCFree(a); + return NULL; + } + memcpy(a->str, (const char*)d_ptr, length); + + d_ptr += length; + + a->length = (d_ptr - buffer); + return a; +} + +static Asn1Generic * DecodeAsn1DerPrintableString(const unsigned char *buffer, uint32_t max_size, uint8_t depth) +{ + const unsigned char *d_ptr = buffer; + uint32_t length, numbytes; + uint32_t i; + Asn1Generic *a; + unsigned char c; + + d_ptr++; + + /* total sequence length */ + c = d_ptr[0]; + if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */ + length = c; + d_ptr++; + } else { /* long form 8.1.3.5 */ + numbytes = c & 0x7f; + length = 0; + d_ptr++; + for (i=0; i max_size) + return NULL; + + a = Asn1GenericNew(); + if (a == NULL) + return NULL; + a->type = ASN1_PRINTSTRING; + a->strlen = length; + a->str = SCMalloc(length+1); + if (a->str == NULL) { + SCFree(a); + return NULL; + } + strlcpy(a->str, (const char*)d_ptr, length+1); + a->str[length] = '\0'; + + d_ptr += length; + + a->length = (d_ptr - buffer); + return a; +} + +static Asn1Generic * DecodeAsn1DerSequence(const unsigned char *buffer, uint32_t max_size, uint8_t depth) +{ + const unsigned char *d_ptr = buffer; + uint32_t d_length, parsed_bytes, numbytes, el_max_size; + uint8_t c; + uint32_t i, seq_index; + Asn1Generic *node; + Asn1Generic *child; + + d_ptr++; + + node = Asn1GenericNew(); + if (node == NULL) + return NULL; + node->type = ASN1_SEQUENCE; + + /* total sequence length */ + c = d_ptr[0]; + if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */ + d_length = c; + d_ptr++; + } else { /* long form 8.1.3.5 */ + numbytes = c & 0x7f; + d_length = 0; + d_ptr++; + for (i=0; ilength = d_length + (d_ptr - buffer); + if (node->length > max_size) { + free(node); + return NULL; + } + + parsed_bytes = 0; + seq_index = 0; + + /* decode child elements */ + while (parsed_bytes < d_length) { + el_max_size = max_size - (d_ptr-buffer); + child = DecodeAsn1DerGeneric(d_ptr, el_max_size, depth, seq_index); + + if (child == NULL) { + break; + } + Asn1SequenceAppend(node, child); + parsed_bytes += child->length; + d_ptr += child->length; + seq_index++; + } + + return (Asn1Generic *)node; +} + +static Asn1Generic * DecodeAsn1DerSet(const unsigned char *buffer, uint32_t max_size, uint8_t depth) +{ + const unsigned char *d_ptr = buffer; + uint32_t d_length, numbytes, el_max_size; + uint8_t c; + uint32_t i, seq_index; + Asn1Generic *node; + Asn1Generic *child; + + d_ptr++; + + node = Asn1GenericNew(); + if (node == NULL) + return NULL; + node->type = ASN1_SET; + node->data = NULL; + + /* total sequence length */ + c = d_ptr[0]; + if ((c & (1<<7))>>7 == 0) { /* short form 8.1.3.4 */ + d_length = c; + d_ptr++; + } else { /* long form 8.1.3.5 */ + numbytes = c & 0x7f; + d_length = 0; + d_ptr++; + for (i=0; ilength = d_length + (d_ptr - buffer); + + seq_index = 0; + + el_max_size = max_size - (d_ptr-buffer); + child = DecodeAsn1DerGeneric(d_ptr, el_max_size, depth, seq_index); + + node->data = child; + + return (Asn1Generic *)node; +} + +static Asn1Generic * DecodeAsn1DerT61String(const unsigned char *buffer, uint32_t max_size, uint8_t depth) +{ + Asn1Generic *a; + + a = DecodeAsn1DerIA5String(buffer, max_size, depth); + if (a != NULL) + a->type = ASN1_T61STRING; + + return a; +} + +static Asn1Generic * DecodeAsn1DerUTCTime(const unsigned char *buffer, uint32_t max_size, uint8_t depth) +{ + Asn1Generic *a; + + a = DecodeAsn1DerIA5String(buffer, max_size, depth); + if (a != NULL) + a->type = ASN1_UTCTIME; + + return a; +} + +Asn1Generic * DecodeDer(const unsigned char *buffer, uint32_t size) +{ + const unsigned char *d_ptr = buffer; + uint32_t d_length, numbytes; + Asn1Generic *cert; + uint8_t c; + uint32_t i; + + /* Check that buffer is an ASN.1 structure (basic checks) */ + if (d_ptr[0] != 0x30 && d_ptr[1] != 0x82) /* Sequence */ + return NULL; + + c = d_ptr[1]; + if ((c & (1<<7))>>7 != 1) + return NULL; + + numbytes = c & 0x7f; + d_length = 0; + d_ptr += 2; + for (i=0; inext; + if (it->data) { + DerFree(it->data); + } + if (it->str) + free(it->str); + memset(it, 0xff, sizeof(Asn1Generic)); + SCFree(it); + it = n; + } +} diff --git a/src/util-decode-der.h b/src/util-decode-der.h new file mode 100644 index 0000000000..62bb56ac7c --- /dev/null +++ b/src/util-decode-der.h @@ -0,0 +1,82 @@ +/* + * Copyright (C) 2011-2012 ANSSI + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL + * THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; + * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, + * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR + * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF + * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/** + * \file + * + * \author Pierre Chifflier + * + */ + +#ifndef __UTIL_DECODE_DER_H__ +#define __UTIL_DECODE_DER_H__ + +#define ASN1_CLASS_UNIVERSAL 0 +#define ASN1_CLASS_APPLICATION 1 +#define ASN1_CLASS_CONTEXTSPEC 2 +#define ASN1_CLASS_PRIVATE 3 + +#define ASN1_UNKNOWN 0 +#define ASN1_BOOLEAN 0x01 +#define ASN1_INTEGER 0x02 +#define ASN1_BITSTRING 0x03 +#define ASN1_OCTETSTRING 0x04 +#define ASN1_NULL 0x05 +#define ASN1_OID 0x06 +#define ASN1_SEQUENCE 0x10 +#define ASN1_SET 0x11 +#define ASN1_PRINTSTRING 0x13 +#define ASN1_T61STRING 0x14 +#define ASN1_IA5STRING 0x16 +#define ASN1_UTCTIME 0x17 + +typedef struct Asn1ElementType_ { + uint8_t cls:2; + uint8_t pc:1; + uint8_t tag:5; +} __attribute__((packed)) Asn1ElementType; + +/* Generic ASN.1 element + * Presence and meaning of fields depends on the header and type values. + */ +typedef struct Asn1Generic_ { + Asn1ElementType header; + uint8_t type; + uint32_t length; /* length of node, including header */ + + struct Asn1Generic_ *data; /* only if type is structured */ + + char *str; + uint32_t strlen; + uint64_t value; + struct Asn1Generic_ *next; /* only if type is sequence */ +} Asn1Generic; + +Asn1Generic * DecodeDer(const unsigned char *buffer, uint32_t size); +void DerFree(Asn1Generic *a); + +#endif /* __UTIL_DECODE_DER_H__ */