mirror of https://github.com/OISF/suricata
Add ASN.1 parser for X509 certificates (in DER format)
Signed-off-by: Pierre Chifflier <pierre.chifflier@ssi.gouv.fr>remotes/origin/HEAD
parent
0b3f6c464a
commit
f77fcdb3e8
@ -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 <pierre.chifflier@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 || 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->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: */
|
||||
@ -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 <pierre.chifflier@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__ */
|
||||
@ -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 <pierre.chifflier@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->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);
|
||||
|
||||
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;
|
||||
|
||||
/* 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));
|
||||
SCFree(it);
|
||||
it = n;
|
||||
}
|
||||
}
|
||||
@ -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 <pierre.chifflier@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__ */
|
||||
Loading…
Reference in New Issue