[Oisf-devel] [PATCH 1/3] Add ASN.1 parser for X509 certificates (in DER format)

Pierre Chifflier pierre.chifflier at ssi.gouv.fr
Tue Oct 25 12:10:58 UTC 2011


Signed-off-by: Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
---
 src/Makefile.am           |    2 +
 src/util-decode-der-get.c |  239 +++++++++++++++
 src/util-decode-der-get.h |   33 ++
 src/util-decode-der.c     |  746 +++++++++++++++++++++++++++++++++++++++++++++
 src/util-decode-der.h     |   72 +++++
 5 files changed, 1092 insertions(+), 0 deletions(-)
 create mode 100644 src/util-decode-der-get.c
 create mode 100644 src/util-decode-der-get.h
 create mode 100644 src/util-decode-der.c
 create mode 100644 src/util-decode-der.h

diff --git a/src/Makefile.am b/src/Makefile.am
index 1145471..a59443d 100644
--- a/src/Makefile.am
+++ b/src/Makefile.am
@@ -203,6 +203,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 0000000..85d5b48
--- /dev/null
+++ b/src/util-decode-der-get.c
@@ -0,0 +1,239 @@
+/* Copyright (C) 2011 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ * 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
+ *
+ * \author Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <inttypes.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#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; offset<seqsz; offset++) {
+
+        idx = seq_index[offset];
+        for (i=0; i<idx; i++) {
+            if (Node == 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 */
+        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 Asn1Generic *Node;
+    const char *shortname;
+    int rc = -1;
+    int current_length;
+    const char *separator = ", ";
+
+    if (length < 10)
+        goto issuer_dn_error;
+    buffer[0] = '\0';
+
+    Node = Asn1DerGet(cert, SEQ_IDX_ISSUER, sizeof(SEQ_IDX_ISSUER));
+
+    node = Node;
+    if (node->type != ASN1_SEQUENCE)
+        goto issuer_dn_error;
+
+    it = node;
+    while (it != NULL) {
+        if (it == NULL || 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;
+
+        current_length = strlen(buffer);
+        snprintf(buffer+current_length, length-current_length, "%s=%s", shortname, node_str->str);
+        if (strcmp(shortname,"CN")==0)
+            separator = "/";
+        if (it->next != NULL) {
+            current_length = strlen(buffer);
+            snprintf(buffer+current_length, length-current_length, "%s", separator);
+        }
+        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 Asn1Generic *Node;
+    const char *shortname;
+    int rc = -1;
+    int current_length;
+    const char *separator = ", ";
+
+    if (length < 10)
+        goto subject_dn_error;
+    buffer[0] = '\0';
+
+    Node = Asn1DerGet(cert, SEQ_IDX_SUBJECT, sizeof(SEQ_IDX_SUBJECT));
+
+    node = Node;
+    if (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;
+
+        current_length = strlen(buffer);
+        snprintf(buffer+current_length, length-current_length, "%s=%s", shortname, node_str->str);
+        if (strcmp(shortname,"CN")==0)
+            separator = "/";
+        if (it->next != NULL) {
+            current_length = strlen(buffer);
+            snprintf(buffer+current_length, length-current_length, "%s", separator);
+        }
+        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 0000000..d7a8c40
--- /dev/null
+++ b/src/util-decode-der-get.h
@@ -0,0 +1,33 @@
+/* Copyright (C) 2011 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ * 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
+ *
+ * \author Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ */
+
+#ifndef __DECODE_DER_GET_H__
+#define __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 /* __DECODE_DER_GET_H__ */
diff --git a/src/util-decode-der.c b/src/util-decode-der.c
new file mode 100644
index 0000000..f00078e
--- /dev/null
+++ b/src/util-decode-der.c
@@ -0,0 +1,746 @@
+/* Copyright (C) 2011 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ * 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
+ *
+ * \author Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ */
+
+/*
+ * 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <inttypes.h>
+
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "suricata-common.h"
+
+#include "util-decode-der.h"
+
+#define MAX_OID_LENGTH 256
+
+/* XXX see also http://luca.ntop.org/Teaching/Appunti/asn1.html */
+
+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->next = NULL;
+    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) {
+        SCLogWarning(SC_ERR_ALPARSER, "Unhandled tag %x %x %x\n", el.cls, el.pc, el.tag);
+        return NULL;
+    }
+
+    switch (el.cls) {
+        case ASN1_CLASS_CONTEXTSPEC:
+            /* XXX 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;
+            }
+            SCLogWarning(SC_ERR_ALPARSER, "Unhandled context specific value type %x %x %x\n", el.cls, el.pc, el.tag);
+            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:
+            SCLogWarning(SC_ERR_ALPARSER, "unknown ASN.1 type %x %x %x\n", el.cls, el.pc, el.tag);
+            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; i<numbytes; i++) {
+                    child->length = 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) {
+        SCLogWarning(SC_ERR_ALPARSER, "ASN.1 decoding returned NULL for depth %d idx %d, type %x %x %x\n", depth, seq_index, el.cls, el.pc, el.tag);
+        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; i<numbytes; i++) {
+        value = value<<8 | d_ptr[i];
+    }
+
+    a = Asn1GenericNew();
+    if (a == NULL)
+        return NULL;
+    a->type = 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; i<numbytes; i++) {
+        snprintf(a->str + 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; i<numbytes; i++) {
+        value = value<<8 | d_ptr[0];
+        d_ptr++;
+    }
+
+    a = Asn1GenericNew();
+    if (a == NULL)
+        return NULL;
+    a->type = ASN1_BOOLEAN;
+    a->length = (d_ptr - buffer);
+    a->value = value;
+    /* XXX we can check that value is either 0xff or 0 (See 11.1) */
+
+    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; i<numbytes; i++) {
+        value = value<<8 | d_ptr[0];
+        d_ptr++;
+    }
+
+    a = Asn1GenericNew();
+    a->type = 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<numbytes; i++) {
+            length = length<<8 | d_ptr[0];
+            d_ptr++;
+        }
+    }
+    if (length > max_size) {
+        SCLogWarning(SC_ERR_ALPARSER, "bitstring length too big for data at depth %d\n", depth);
+        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<numbytes; i++) {
+            oid_length = oid_length<<8 | d_ptr[0];
+            d_ptr++;
+        }
+    }
+    if (oid_length > max_size) {
+        SCLogWarning(SC_ERR_ALPARSER, "oid length too big for data at depth %d\n", depth);
+        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; i<oid_length; ) {
+        int s = strlen(a->str);
+        c = d_ptr[0];
+        oid_value = 0;
+        while ( i<oid_length && (c & (1<<7)) == 1<<7 ) {
+            oid_value = oid_value<<7 | (c & ~(1<<7));
+            d_ptr++;
+            c = d_ptr[0];
+            i++;
+        }
+        oid_value = oid_value<<7 | c;
+        d_ptr++;
+        i++;
+        snprintf(a->str + 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<numbytes; i++) {
+            length = length<<8 | d_ptr[0];
+            d_ptr++;
+        }
+    }
+    if (length > max_size) {
+        SCLogWarning(SC_ERR_ALPARSER, "ia5string length too big for data at depth %d\n", depth);
+        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;
+    }
+    strncpy(a->str, (const char*)d_ptr, length);
+    a->str[length] = '\0';
+
+    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<numbytes; i++) {
+            length = length<<8 | d_ptr[0];
+            d_ptr++;
+        }
+    }
+    if (length > max_size) {
+        SCLogWarning(SC_ERR_ALPARSER, "octet-string length too big for data at depth %d\n", depth);
+        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<numbytes; i++) {
+            length = length<<8 | d_ptr[0];
+            d_ptr++;
+        }
+    }
+    if (length > max_size) {
+        SCLogWarning(SC_ERR_ALPARSER, "printable-string length too big for data at depth %d\n", depth);
+        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;
+    }
+    strncpy(a->str, (const char*)d_ptr, length);
+    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;
+    Asn1ElementType el;
+    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;
+    node->data = NULL;
+    node->next = 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; i<numbytes; i++) {
+            d_length = d_length<<8 | d_ptr[0];
+            d_ptr++;
+        }
+    }
+    node->length = d_length + (d_ptr - buffer);
+    if (node->length > max_size) {
+        SCLogWarning(SC_ERR_ALPARSER, "sequence length too big for data at depth %d\n", depth);
+        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) {
+            SCLogWarning(SC_ERR_ALPARSER, "ASN.1 decoding returned NULL for depth %d idx %d, type %x %x %x\n", depth, seq_index, el.cls, el.pc, el.tag);
+            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;
+    Asn1ElementType el;
+    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; i<numbytes; i++) {
+            d_length = d_length<<8 | d_ptr[0];
+            d_ptr++;
+        }
+    }
+    node->length = 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);
+
+    if (child == NULL) {
+        SCLogWarning(SC_ERR_ALPARSER, "ASN.1 decoding returned NULL for depth %d idx %d, type %x %x %x\n", depth, seq_index, el.cls, el.pc, el.tag);
+    }
+    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 */
+        SCLogWarning(SC_ERR_ALPARSER, "Invalid ASN.1 structure: not a sequence\n");
+        return NULL;
+    }
+    c = d_ptr[1];
+    if ((c & (1<<7))>>7 != 1) {
+        SCLogWarning(SC_ERR_ALPARSER, "Invalid ASN.1 structure: first byte of length: bit 8 is not 1 (See 8.1.3.5)\n");
+        return NULL;
+    }
+    numbytes = c & 0x7f;
+    d_length = 0;
+    d_ptr += 2;
+    for (i=0; i<numbytes; i++) {
+        d_length = d_length<<8 | d_ptr[0];
+        d_ptr++;
+    }
+    if (d_length+(d_ptr-buffer) != size) {
+        SCLogWarning(SC_ERR_ALPARSER, "Invalid ASN.1 structure: size of top-level sequence does not match length\n");
+        return NULL;
+    }
+
+    cert = DecodeAsn1DerGeneric(buffer, size, 0 /* depth */, 0);
+
+    return cert;
+}
+
+void DerFree(Asn1Generic *a)
+{
+    Asn1Generic *it, *n;
+
+    if (a == NULL)
+        return;
+
+    it = a;
+    while (it) {
+        n = it->next;
+        if (it->data) {
+            DerFree(it->data);
+        }
+        if (it->str)
+            free(it->str);
+        memset(it, 0xff, sizeof(Asn1Generic));
+        free(it);
+        it = n;
+    }
+}
+
diff --git a/src/util-decode-der.h b/src/util-decode-der.h
new file mode 100644
index 0000000..0120808
--- /dev/null
+++ b/src/util-decode-der.h
@@ -0,0 +1,72 @@
+/* Copyright (C) 2011 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ * 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
+ *
+ * \author Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ *
+ */
+
+#ifndef __DECODE_DER_H__
+#define __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 /* __DECODE_DER_H__ */
-- 
1.7.7




More information about the Oisf-devel mailing list