[Oisf-devel] [PATCH 1/3] Add ASN.1 parser for X509 certificates (in DER format)
Pierre Chifflier
pierre.chifflier at ssi.gouv.fr
Fri Nov 4 17:18:44 UTC 2011
Signed-off-by: Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
---
src/Makefile.am | 2 +
src/util-decode-der-get.c | 245 +++++++++++++++
src/util-decode-der-get.h | 43 +++
src/util-decode-der.c | 731 +++++++++++++++++++++++++++++++++++++++++++++
src/util-decode-der.h | 82 +++++
5 files changed, 1103 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 b2270d7..e2fa6ca 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..02470e9
--- /dev/null
+++ b/src/util-decode-der-get.c
@@ -0,0 +1,245 @@
+/*
+ * Copyright (C) 2011 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ * 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 <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 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));
+
+ 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 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));
+
+ 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..e658483
--- /dev/null
+++ b/src/util-decode-der-get.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2011 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ * 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 <pierre.chifflier at ssi.gouv.fr>
+ *
+ */
+
+#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 0000000..03a04dd
--- /dev/null
+++ b/src/util-decode-der.c
@@ -0,0 +1,731 @@
+/*
+ * Copyright (C) 2011 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ * 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 <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
+
+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)
+ 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; 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)
+ 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;
+
+ 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)
+ 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)
+ 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)
+ 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);
+ 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)
+ 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)
+ 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;
+ 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) {
+ 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; 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);
+
+ 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; i<numbytes; i++) {
+ d_length = d_length<<8 | d_ptr[0];
+ d_ptr++;
+ }
+ if (d_length+(d_ptr-buffer) != size)
+ 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..30f410a
--- /dev/null
+++ b/src/util-decode-der.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (C) 2011 Pierre Chifflier <pierre.chifflier at ssi.gouv.fr>
+ * 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 <pierre.chifflier at ssi.gouv.fr>
+ *
+ */
+
+#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__ */
--
1.7.7
More information about the Oisf-devel
mailing list