From 81db488ebe397ddba4e51e04c0719023ef18cf70 Mon Sep 17 00:00:00 2001 From: Michał Cichoń Date: Sun, 6 May 2012 01:46:56 +0200 Subject: Replace axTLS with PolarSSL as it does not handle secure connections well enough. --- polarssl/src/library/x509parse.c | 3425 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 3425 insertions(+) create mode 100644 polarssl/src/library/x509parse.c (limited to 'polarssl/src/library/x509parse.c') diff --git a/polarssl/src/library/x509parse.c b/polarssl/src/library/x509parse.c new file mode 100644 index 0000000..9d01a65 --- /dev/null +++ b/polarssl/src/library/x509parse.c @@ -0,0 +1,3425 @@ +/* + * X.509 certificate and private key decoding + * + * Copyright (C) 2006-2011, Brainspark B.V. + * + * This file is part of PolarSSL (http://www.polarssl.org) + * Lead Maintainer: Paul Bakker + * + * All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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 along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + */ +/* + * The ITU-T X.509 standard defines a certificate format for PKI. + * + * http://www.ietf.org/rfc/rfc3279.txt + * http://www.ietf.org/rfc/rfc3280.txt + * + * ftp://ftp.rsasecurity.com/pub/pkcs/ascii/pkcs-1v2.asc + * + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.680-0207.pdf + * http://www.itu.int/ITU-T/studygroups/com17/languages/X.690-0207.pdf + */ + +#include "polarssl/config.h" + +#if defined(POLARSSL_X509_PARSE_C) + +#include "polarssl/x509.h" +#include "polarssl/asn1.h" +#include "polarssl/pem.h" +#include "polarssl/des.h" +#include "polarssl/md2.h" +#include "polarssl/md4.h" +#include "polarssl/md5.h" +#include "polarssl/sha1.h" +#include "polarssl/sha2.h" +#include "polarssl/sha4.h" +#include "polarssl/dhm.h" + +#include +#include +#if defined(_WIN32) +#include +#else +#include +#endif + +#if defined(POLARSSL_FS_IO) +#include +#endif + +/* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + */ +static int x509_get_version( unsigned char **p, + const unsigned char *end, + int *ver ) +{ + int ret; + size_t len; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | 0 ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + { + *ver = 0; + return( 0 ); + } + + return( ret ); + } + + end = *p + len; + + if( ( ret = asn1_get_int( p, end, ver ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_VERSION + ret ); + + if( *p != end ) + return( POLARSSL_ERR_X509_CERT_INVALID_VERSION + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * Version ::= INTEGER { v1(0), v2(1) } + */ +static int x509_crl_get_version( unsigned char **p, + const unsigned char *end, + int *ver ) +{ + int ret; + + if( ( ret = asn1_get_int( p, end, ver ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + { + *ver = 0; + return( 0 ); + } + + return( POLARSSL_ERR_X509_CERT_INVALID_VERSION + ret ); + } + + return( 0 ); +} + +/* + * CertificateSerialNumber ::= INTEGER + */ +static int x509_get_serial( unsigned char **p, + const unsigned char *end, + x509_buf *serial ) +{ + int ret; + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_CERT_INVALID_SERIAL + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( **p != ( ASN1_CONTEXT_SPECIFIC | ASN1_PRIMITIVE | 2 ) && + **p != ASN1_INTEGER ) + return( POLARSSL_ERR_X509_CERT_INVALID_SERIAL + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + serial->tag = *(*p)++; + + if( ( ret = asn1_get_len( p, end, &serial->len ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_SERIAL + ret ); + + serial->p = *p; + *p += serial->len; + + return( 0 ); +} + +/* + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL } + */ +static int x509_get_alg( unsigned char **p, + const unsigned char *end, + x509_buf *alg ) +{ + int ret; + size_t len; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_ALG + ret ); + + end = *p + len; + alg->tag = **p; + + if( ( ret = asn1_get_tag( p, end, &alg->len, ASN1_OID ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_ALG + ret ); + + alg->p = *p; + *p += alg->len; + + if( *p == end ) + return( 0 ); + + /* + * assume the algorithm parameters must be NULL + */ + if( ( ret = asn1_get_tag( p, end, &len, ASN1_NULL ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_ALG + ret ); + + if( *p != end ) + return( POLARSSL_ERR_X509_CERT_INVALID_ALG + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + */ +static int x509_get_attr_type_value( unsigned char **p, + const unsigned char *end, + x509_name *cur ) +{ + int ret; + size_t len; + x509_buf *oid; + x509_buf *val; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_NAME + ret ); + + oid = &cur->oid; + oid->tag = **p; + + if( ( ret = asn1_get_tag( p, end, &oid->len, ASN1_OID ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_NAME + ret ); + + oid->p = *p; + *p += oid->len; + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_CERT_INVALID_NAME + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + if( **p != ASN1_BMP_STRING && **p != ASN1_UTF8_STRING && + **p != ASN1_T61_STRING && **p != ASN1_PRINTABLE_STRING && + **p != ASN1_IA5_STRING && **p != ASN1_UNIVERSAL_STRING ) + return( POLARSSL_ERR_X509_CERT_INVALID_NAME + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + val = &cur->val; + val->tag = *(*p)++; + + if( ( ret = asn1_get_len( p, end, &val->len ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_NAME + ret ); + + val->p = *p; + *p += val->len; + + cur->next = NULL; + + return( 0 ); +} + +/* + * RelativeDistinguishedName ::= + * SET OF AttributeTypeAndValue + * + * AttributeTypeAndValue ::= SEQUENCE { + * type AttributeType, + * value AttributeValue } + * + * AttributeType ::= OBJECT IDENTIFIER + * + * AttributeValue ::= ANY DEFINED BY AttributeType + */ +static int x509_get_name( unsigned char **p, + const unsigned char *end, + x509_name *cur ) +{ + int ret; + size_t len; + const unsigned char *end2; + x509_name *use; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SET ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_NAME + ret ); + + end2 = end; + end = *p + len; + use = cur; + + do + { + if( ( ret = x509_get_attr_type_value( p, end, use ) ) != 0 ) + return( ret ); + + if( *p != end ) + { + use->next = (x509_name *) malloc( + sizeof( x509_name ) ); + + if( use->next == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + memset( use->next, 0, sizeof( x509_name ) ); + + use = use->next; + } + } + while( *p != end ); + + /* + * recurse until end of SEQUENCE is reached + */ + if( *p == end2 ) + return( 0 ); + + cur->next = (x509_name *) malloc( + sizeof( x509_name ) ); + + if( cur->next == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + return( x509_get_name( p, end2, cur->next ) ); +} + +/* + * Time ::= CHOICE { + * utcTime UTCTime, + * generalTime GeneralizedTime } + */ +static int x509_get_time( unsigned char **p, + const unsigned char *end, + x509_time *time ) +{ + int ret; + size_t len; + char date[64]; + unsigned char tag; + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_CERT_INVALID_DATE + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + tag = **p; + + if ( tag == ASN1_UTC_TIME ) + { + (*p)++; + ret = asn1_get_len( p, end, &len ); + + if( ret != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_DATE + ret ); + + memset( date, 0, sizeof( date ) ); + memcpy( date, *p, ( len < sizeof( date ) - 1 ) ? + len : sizeof( date ) - 1 ); + + if( sscanf( date, "%2d%2d%2d%2d%2d%2d", + &time->year, &time->mon, &time->day, + &time->hour, &time->min, &time->sec ) < 5 ) + return( POLARSSL_ERR_X509_CERT_INVALID_DATE ); + + time->year += 100 * ( time->year < 50 ); + time->year += 1900; + + *p += len; + + return( 0 ); + } + else if ( tag == ASN1_GENERALIZED_TIME ) + { + (*p)++; + ret = asn1_get_len( p, end, &len ); + + if( ret != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_DATE + ret ); + + memset( date, 0, sizeof( date ) ); + memcpy( date, *p, ( len < sizeof( date ) - 1 ) ? + len : sizeof( date ) - 1 ); + + if( sscanf( date, "%4d%2d%2d%2d%2d%2d", + &time->year, &time->mon, &time->day, + &time->hour, &time->min, &time->sec ) < 5 ) + return( POLARSSL_ERR_X509_CERT_INVALID_DATE ); + + *p += len; + + return( 0 ); + } + else + return( POLARSSL_ERR_X509_CERT_INVALID_DATE + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); +} + + +/* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + */ +static int x509_get_dates( unsigned char **p, + const unsigned char *end, + x509_time *from, + x509_time *to ) +{ + int ret; + size_t len; + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_DATE + ret ); + + end = *p + len; + + if( ( ret = x509_get_time( p, end, from ) ) != 0 ) + return( ret ); + + if( ( ret = x509_get_time( p, end, to ) ) != 0 ) + return( ret ); + + if( *p != end ) + return( POLARSSL_ERR_X509_CERT_INVALID_DATE + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * SubjectPublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + */ +static int x509_get_pubkey( unsigned char **p, + const unsigned char *end, + x509_buf *pk_alg_oid, + mpi *N, mpi *E ) +{ + int ret, can_handle; + size_t len; + unsigned char *end2; + + if( ( ret = x509_get_alg( p, end, pk_alg_oid ) ) != 0 ) + return( ret ); + + /* + * only RSA public keys handled at this time + */ + can_handle = 0; + + if( pk_alg_oid->len == 9 && + memcmp( pk_alg_oid->p, OID_PKCS1_RSA, 9 ) == 0 ) + can_handle = 1; + + if( pk_alg_oid->len == 9 && + memcmp( pk_alg_oid->p, OID_PKCS1, 8 ) == 0 ) + { + if( pk_alg_oid->p[8] >= 2 && pk_alg_oid->p[8] <= 5 ) + can_handle = 1; + + if ( pk_alg_oid->p[8] >= 11 && pk_alg_oid->p[8] <= 14 ) + can_handle = 1; + } + + if( pk_alg_oid->len == 5 && + memcmp( pk_alg_oid->p, OID_RSA_SHA_OBS, 5 ) == 0 ) + can_handle = 1; + + if( can_handle == 0 ) + return( POLARSSL_ERR_X509_UNKNOWN_PK_ALG ); + + if( ( ret = asn1_get_tag( p, end, &len, ASN1_BIT_STRING ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + ret ); + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + end2 = *p + len; + + if( *(*p)++ != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY ); + + /* + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + */ + if( ( ret = asn1_get_tag( p, end2, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + ret ); + + if( *p + len != end2 ) + return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + if( ( ret = asn1_get_mpi( p, end2, N ) ) != 0 || + ( ret = asn1_get_mpi( p, end2, E ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + ret ); + + if( *p != end ) + return( POLARSSL_ERR_X509_CERT_INVALID_PUBKEY + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +static int x509_get_sig( unsigned char **p, + const unsigned char *end, + x509_buf *sig ) +{ + int ret; + size_t len; + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + sig->tag = **p; + + if( ( ret = asn1_get_tag( p, end, &len, ASN1_BIT_STRING ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE + ret ); + + + if( --len < 1 || *(*p)++ != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_SIGNATURE ); + + sig->len = len; + sig->p = *p; + + *p += len; + + return( 0 ); +} + +/* + * X.509 v2/v3 unique identifier (not parsed) + */ +static int x509_get_uid( unsigned char **p, + const unsigned char *end, + x509_buf *uid, int n ) +{ + int ret; + + if( *p == end ) + return( 0 ); + + uid->tag = **p; + + if( ( ret = asn1_get_tag( p, end, &uid->len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | n ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( 0 ); + + return( ret ); + } + + uid->p = *p; + *p += uid->len; + + return( 0 ); +} + +/* + * X.509 Extensions (No parsing of extensions, pointer should + * be either manually updated or extensions should be parsed! + */ +static int x509_get_ext( unsigned char **p, + const unsigned char *end, + x509_buf *ext, int tag ) +{ + int ret; + size_t len; + + if( *p == end ) + return( 0 ); + + ext->tag = **p; + + if( ( ret = asn1_get_tag( p, end, &ext->len, + ASN1_CONTEXT_SPECIFIC | ASN1_CONSTRUCTED | tag ) ) != 0 ) + return( ret ); + + ext->p = *p; + end = *p + ext->len; + + /* + * Extensions ::= SEQUENCE SIZE (1..MAX) OF Extension + * + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + if( end != *p + len ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * X.509 CRL v2 extensions (no extensions parsed yet.) + */ +static int x509_get_crl_ext( unsigned char **p, + const unsigned char *end, + x509_buf *ext ) +{ + int ret; + size_t len = 0; + + /* Get explicit tag */ + if( ( ret = x509_get_ext( p, end, ext, 0) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( 0 ); + + return( ret ); + } + + while( *p < end ) + { + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + *p += len; + } + + if( *p != end ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * X.509 CRL v2 entry extensions (no extensions parsed yet.) + */ +static int x509_get_crl_entry_ext( unsigned char **p, + const unsigned char *end, + x509_buf *ext ) +{ + int ret; + size_t len = 0; + + /* OPTIONAL */ + if (end <= *p) + return( 0 ); + + ext->tag = **p; + ext->p = *p; + + /* + * Get CRL-entry extension sequence header + * crlEntryExtensions Extensions OPTIONAL -- if present, MUST be v2 + */ + if( ( ret = asn1_get_tag( p, end, &ext->len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + { + ext->p = NULL; + return( 0 ); + } + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + } + + end = *p + ext->len; + + if( end != *p + ext->len ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + while( *p < end ) + { + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + *p += len; + } + + if( *p != end ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +static int x509_get_basic_constraints( unsigned char **p, + const unsigned char *end, + int *ca_istrue, + int *max_pathlen ) +{ + int ret; + size_t len; + + /* + * BasicConstraints ::= SEQUENCE { + * cA BOOLEAN DEFAULT FALSE, + * pathLenConstraint INTEGER (0..MAX) OPTIONAL } + */ + *ca_istrue = 0; /* DEFAULT FALSE */ + *max_pathlen = 0; /* endless */ + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + if( *p == end ) + return 0; + + if( ( ret = asn1_get_bool( p, end, ca_istrue ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + ret = asn1_get_int( p, end, ca_istrue ); + + if( ret != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + if( *ca_istrue != 0 ) + *ca_istrue = 1; + } + + if( *p == end ) + return 0; + + if( ( ret = asn1_get_int( p, end, max_pathlen ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + if( *p != end ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + (*max_pathlen)++; + + return 0; +} + +static int x509_get_ns_cert_type( unsigned char **p, + const unsigned char *end, + unsigned char *ns_cert_type) +{ + int ret; + x509_bitstring bs = { 0, 0, NULL }; + + if( ( ret = asn1_get_bitstring( p, end, &bs ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + if( bs.len != 1 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_INVALID_LENGTH ); + + /* Get actual bitstring */ + *ns_cert_type = *bs.p; + return 0; +} + +static int x509_get_key_usage( unsigned char **p, + const unsigned char *end, + unsigned char *key_usage) +{ + int ret; + x509_bitstring bs = { 0, 0, NULL }; + + if( ( ret = asn1_get_bitstring( p, end, &bs ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + if( bs.len > 1 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_INVALID_LENGTH ); + + /* Get actual bitstring */ + *key_usage = *bs.p; + return 0; +} + +/* + * ExtKeyUsageSyntax ::= SEQUENCE SIZE (1..MAX) OF KeyPurposeId + * + * KeyPurposeId ::= OBJECT IDENTIFIER + */ +static int x509_get_ext_key_usage( unsigned char **p, + const unsigned char *end, + x509_sequence *ext_key_usage) +{ + int ret; + + if( ( ret = asn1_get_sequence_of( p, end, ext_key_usage, ASN1_OID ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + /* Sequence length must be >= 1 */ + if( ext_key_usage->buf.p == NULL ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_INVALID_LENGTH ); + + return 0; +} + +/* + * SubjectAltName ::= GeneralNames + * + * GeneralNames ::= SEQUENCE SIZE (1..MAX) OF GeneralName + * + * GeneralName ::= CHOICE { + * otherName [0] OtherName, + * rfc822Name [1] IA5String, + * dNSName [2] IA5String, + * x400Address [3] ORAddress, + * directoryName [4] Name, + * ediPartyName [5] EDIPartyName, + * uniformResourceIdentifier [6] IA5String, + * iPAddress [7] OCTET STRING, + * registeredID [8] OBJECT IDENTIFIER } + * + * OtherName ::= SEQUENCE { + * type-id OBJECT IDENTIFIER, + * value [0] EXPLICIT ANY DEFINED BY type-id } + * + * EDIPartyName ::= SEQUENCE { + * nameAssigner [0] DirectoryString OPTIONAL, + * partyName [1] DirectoryString } + * + * NOTE: PolarSSL only parses and uses dNSName at this point. + */ +static int x509_get_subject_alt_name( unsigned char **p, + const unsigned char *end, + x509_sequence *subject_alt_name ) +{ + int ret; + size_t len, tag_len; + asn1_buf *buf; + unsigned char tag; + asn1_sequence *cur = subject_alt_name; + + /* Get main sequence tag */ + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + if( *p + len != end ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + while( *p < end ) + { + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + tag = **p; + (*p)++; + if( ( ret = asn1_get_len( p, end, &tag_len ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + if( ( tag & ASN1_CONTEXT_SPECIFIC ) != ASN1_CONTEXT_SPECIFIC ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + + if( tag != ( ASN1_CONTEXT_SPECIFIC | 2 ) ) + { + *p += tag_len; + continue; + } + + buf = &(cur->buf); + buf->tag = tag; + buf->p = *p; + buf->len = tag_len; + *p += buf->len; + + /* Allocate and assign next pointer */ + if (*p < end) + { + cur->next = (asn1_sequence *) malloc( + sizeof( asn1_sequence ) ); + + if( cur->next == NULL ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_MALLOC_FAILED ); + + cur = cur->next; + } + } + + /* Set final sequence entry's next pointer to NULL */ + cur->next = NULL; + + if( *p != end ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * X.509 v3 extensions + * + * TODO: Perform all of the basic constraints tests required by the RFC + * TODO: Set values for undetected extensions to a sane default? + * + */ +static int x509_get_crt_ext( unsigned char **p, + const unsigned char *end, + x509_cert *crt ) +{ + int ret; + size_t len; + unsigned char *end_ext_data, *end_ext_octet; + + if( ( ret = x509_get_ext( p, end, &crt->v3_ext, 3 ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( 0 ); + + return( ret ); + } + + while( *p < end ) + { + /* + * Extension ::= SEQUENCE { + * extnID OBJECT IDENTIFIER, + * critical BOOLEAN DEFAULT FALSE, + * extnValue OCTET STRING } + */ + x509_buf extn_oid = {0, 0, NULL}; + int is_critical = 0; /* DEFAULT FALSE */ + + if( ( ret = asn1_get_tag( p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + end_ext_data = *p + len; + + /* Get extension ID */ + extn_oid.tag = **p; + + if( ( ret = asn1_get_tag( p, end, &extn_oid.len, ASN1_OID ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + extn_oid.p = *p; + *p += extn_oid.len; + + if( ( end - *p ) < 1 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + + /* Get optional critical */ + if( ( ret = asn1_get_bool( p, end_ext_data, &is_critical ) ) != 0 && + ( ret != POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + /* Data should be octet string type */ + if( ( ret = asn1_get_tag( p, end_ext_data, &len, + ASN1_OCTET_STRING ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + ret ); + + end_ext_octet = *p + len; + + if( end_ext_octet != end_ext_data ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + /* + * Detect supported extensions + */ + if( ( OID_SIZE( OID_BASIC_CONSTRAINTS ) == extn_oid.len ) && + memcmp( extn_oid.p, OID_BASIC_CONSTRAINTS, extn_oid.len ) == 0 ) + { + /* Parse basic constraints */ + if( ( ret = x509_get_basic_constraints( p, end_ext_octet, + &crt->ca_istrue, &crt->max_pathlen ) ) != 0 ) + return ( ret ); + crt->ext_types |= EXT_BASIC_CONSTRAINTS; + } + else if( ( OID_SIZE( OID_NS_CERT_TYPE ) == extn_oid.len ) && + memcmp( extn_oid.p, OID_NS_CERT_TYPE, extn_oid.len ) == 0 ) + { + /* Parse netscape certificate type */ + if( ( ret = x509_get_ns_cert_type( p, end_ext_octet, + &crt->ns_cert_type ) ) != 0 ) + return ( ret ); + crt->ext_types |= EXT_NS_CERT_TYPE; + } + else if( ( OID_SIZE( OID_KEY_USAGE ) == extn_oid.len ) && + memcmp( extn_oid.p, OID_KEY_USAGE, extn_oid.len ) == 0 ) + { + /* Parse key usage */ + if( ( ret = x509_get_key_usage( p, end_ext_octet, + &crt->key_usage ) ) != 0 ) + return ( ret ); + crt->ext_types |= EXT_KEY_USAGE; + } + else if( ( OID_SIZE( OID_EXTENDED_KEY_USAGE ) == extn_oid.len ) && + memcmp( extn_oid.p, OID_EXTENDED_KEY_USAGE, extn_oid.len ) == 0 ) + { + /* Parse extended key usage */ + if( ( ret = x509_get_ext_key_usage( p, end_ext_octet, + &crt->ext_key_usage ) ) != 0 ) + return ( ret ); + crt->ext_types |= EXT_EXTENDED_KEY_USAGE; + } + else if( ( OID_SIZE( OID_SUBJECT_ALT_NAME ) == extn_oid.len ) && + memcmp( extn_oid.p, OID_SUBJECT_ALT_NAME, extn_oid.len ) == 0 ) + { + /* Parse extended key usage */ + if( ( ret = x509_get_subject_alt_name( p, end_ext_octet, + &crt->subject_alt_names ) ) != 0 ) + return ( ret ); + crt->ext_types |= EXT_SUBJECT_ALT_NAME; + } + else + { + /* No parser found, skip extension */ + *p = end_ext_octet; + +#if !defined(POLARSSL_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION) + if( is_critical ) + { + /* Data is marked as critical: fail */ + return ( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ); + } +#endif + } + } + + if( *p != end ) + return( POLARSSL_ERR_X509_CERT_INVALID_EXTENSIONS + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + + return( 0 ); +} + +/* + * X.509 CRL Entries + */ +static int x509_get_entries( unsigned char **p, + const unsigned char *end, + x509_crl_entry *entry ) +{ + int ret; + size_t entry_len; + x509_crl_entry *cur_entry = entry; + + if( *p == end ) + return( 0 ); + + if( ( ret = asn1_get_tag( p, end, &entry_len, + ASN1_SEQUENCE | ASN1_CONSTRUCTED ) ) != 0 ) + { + if( ret == POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + return( 0 ); + + return( ret ); + } + + end = *p + entry_len; + + while( *p < end ) + { + size_t len2; + const unsigned char *end2; + + if( ( ret = asn1_get_tag( p, end, &len2, + ASN1_SEQUENCE | ASN1_CONSTRUCTED ) ) != 0 ) + { + return( ret ); + } + + cur_entry->raw.tag = **p; + cur_entry->raw.p = *p; + cur_entry->raw.len = len2; + end2 = *p + len2; + + if( ( ret = x509_get_serial( p, end2, &cur_entry->serial ) ) != 0 ) + return( ret ); + + if( ( ret = x509_get_time( p, end2, &cur_entry->revocation_date ) ) != 0 ) + return( ret ); + + if( ( ret = x509_get_crl_entry_ext( p, end2, &cur_entry->entry_ext ) ) != 0 ) + return( ret ); + + if ( *p < end ) + { + cur_entry->next = malloc( sizeof( x509_crl_entry ) ); + + if( cur_entry->next == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + cur_entry = cur_entry->next; + memset( cur_entry, 0, sizeof( x509_crl_entry ) ); + } + } + + return( 0 ); +} + +static int x509_get_sig_alg( const x509_buf *sig_oid, int *sig_alg ) +{ + if( sig_oid->len == 9 && + memcmp( sig_oid->p, OID_PKCS1, 8 ) == 0 ) + { + if( sig_oid->p[8] >= 2 && sig_oid->p[8] <= 5 ) + { + *sig_alg = sig_oid->p[8]; + return( 0 ); + } + + if ( sig_oid->p[8] >= 11 && sig_oid->p[8] <= 14 ) + { + *sig_alg = sig_oid->p[8]; + return( 0 ); + } + + return( POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG ); + } + if( sig_oid->len == 5 && + memcmp( sig_oid->p, OID_RSA_SHA_OBS, 5 ) == 0 ) + { + *sig_alg = SIG_RSA_SHA1; + return( 0 ); + } + + return( POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG ); +} + +/* + * Parse and fill a single X.509 certificate in DER format + */ +int x509parse_crt_der( x509_cert *crt, const unsigned char *buf, size_t buflen ) +{ + int ret; + size_t len; + unsigned char *p, *end; + + /* + * Check for valid input + */ + if( crt == NULL || buf == NULL ) + return( POLARSSL_ERR_X509_INVALID_INPUT ); + + p = (unsigned char *) malloc( len = buflen ); + + if( p == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + memcpy( p, buf, buflen ); + + buflen = 0; + + crt->raw.p = p; + crt->raw.len = len; + end = p + len; + + /* + * Certificate ::= SEQUENCE { + * tbsCertificate TBSCertificate, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_free( crt ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT ); + } + + if( len != (size_t) ( end - p ) ) + { + x509_free( crt ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + /* + * TBSCertificate ::= SEQUENCE { + */ + crt->tbs.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_free( crt ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); + } + + end = p + len; + crt->tbs.len = end - crt->tbs.p; + + /* + * Version ::= INTEGER { v1(0), v2(1), v3(2) } + * + * CertificateSerialNumber ::= INTEGER + * + * signature AlgorithmIdentifier + */ + if( ( ret = x509_get_version( &p, end, &crt->version ) ) != 0 || + ( ret = x509_get_serial( &p, end, &crt->serial ) ) != 0 || + ( ret = x509_get_alg( &p, end, &crt->sig_oid1 ) ) != 0 ) + { + x509_free( crt ); + return( ret ); + } + + crt->version++; + + if( crt->version > 3 ) + { + x509_free( crt ); + return( POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION ); + } + + if( ( ret = x509_get_sig_alg( &crt->sig_oid1, &crt->sig_alg ) ) != 0 ) + { + x509_free( crt ); + return( ret ); + } + + /* + * issuer Name + */ + crt->issuer_raw.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_free( crt ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); + } + + if( ( ret = x509_get_name( &p, p + len, &crt->issuer ) ) != 0 ) + { + x509_free( crt ); + return( ret ); + } + + crt->issuer_raw.len = p - crt->issuer_raw.p; + + /* + * Validity ::= SEQUENCE { + * notBefore Time, + * notAfter Time } + * + */ + if( ( ret = x509_get_dates( &p, end, &crt->valid_from, + &crt->valid_to ) ) != 0 ) + { + x509_free( crt ); + return( ret ); + } + + /* + * subject Name + */ + crt->subject_raw.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_free( crt ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); + } + + if( ( ret = x509_get_name( &p, p + len, &crt->subject ) ) != 0 ) + { + x509_free( crt ); + return( ret ); + } + + crt->subject_raw.len = p - crt->subject_raw.p; + + /* + * SubjectPublicKeyInfo ::= SEQUENCE + * algorithm AlgorithmIdentifier, + * subjectPublicKey BIT STRING } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_free( crt ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); + } + + if( ( ret = x509_get_pubkey( &p, p + len, &crt->pk_oid, + &crt->rsa.N, &crt->rsa.E ) ) != 0 ) + { + x509_free( crt ); + return( ret ); + } + + if( ( ret = rsa_check_pubkey( &crt->rsa ) ) != 0 ) + { + x509_free( crt ); + return( ret ); + } + + crt->rsa.len = mpi_size( &crt->rsa.N ); + + /* + * issuerUniqueID [1] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + * subjectUniqueID [2] IMPLICIT UniqueIdentifier OPTIONAL, + * -- If present, version shall be v2 or v3 + * extensions [3] EXPLICIT Extensions OPTIONAL + * -- If present, version shall be v3 + */ + if( crt->version == 2 || crt->version == 3 ) + { + ret = x509_get_uid( &p, end, &crt->issuer_id, 1 ); + if( ret != 0 ) + { + x509_free( crt ); + return( ret ); + } + } + + if( crt->version == 2 || crt->version == 3 ) + { + ret = x509_get_uid( &p, end, &crt->subject_id, 2 ); + if( ret != 0 ) + { + x509_free( crt ); + return( ret ); + } + } + + if( crt->version == 3 ) + { + ret = x509_get_crt_ext( &p, end, crt); + if( ret != 0 ) + { + x509_free( crt ); + return( ret ); + } + } + + if( p != end ) + { + x509_free( crt ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + end = crt->raw.p + crt->raw.len; + + /* + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + */ + if( ( ret = x509_get_alg( &p, end, &crt->sig_oid2 ) ) != 0 ) + { + x509_free( crt ); + return( ret ); + } + + if( memcmp( crt->sig_oid1.p, crt->sig_oid2.p, crt->sig_oid1.len ) != 0 ) + { + x509_free( crt ); + return( POLARSSL_ERR_X509_CERT_SIG_MISMATCH ); + } + + if( ( ret = x509_get_sig( &p, end, &crt->sig ) ) != 0 ) + { + x509_free( crt ); + return( ret ); + } + + if( p != end ) + { + x509_free( crt ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + return( 0 ); +} + +/* + * Parse one or more PEM certificates from a buffer and add them to the chained list + */ +int x509parse_crt( x509_cert *chain, const unsigned char *buf, size_t buflen ) +{ + int ret, success = 0, first_error = 0, total_failed = 0; + x509_cert *crt, *prev = NULL; + int buf_format = X509_FORMAT_DER; + + crt = chain; + + /* + * Check for valid input + */ + if( crt == NULL || buf == NULL ) + return( POLARSSL_ERR_X509_INVALID_INPUT ); + + while( crt->version != 0 && crt->next != NULL ) + { + prev = crt; + crt = crt->next; + } + + /* + * Add new certificate on the end of the chain if needed. + */ + if ( crt->version != 0 && crt->next == NULL) + { + crt->next = (x509_cert *) malloc( sizeof( x509_cert ) ); + + if( crt->next == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + prev = crt; + crt = crt->next; + memset( crt, 0, sizeof( x509_cert ) ); + } + + /* + * Determine buffer content. Buffer contains either one DER certificate or + * one or more PEM certificates. + */ +#if defined(POLARSSL_PEM_C) + if( strstr( (char *) buf, "-----BEGIN CERTIFICATE-----" ) != NULL ) + buf_format = X509_FORMAT_PEM; +#endif + + if( buf_format == X509_FORMAT_DER ) + return x509parse_crt_der( crt, buf, buflen ); + +#if defined(POLARSSL_PEM_C) + if( buf_format == X509_FORMAT_PEM ) + { + pem_context pem; + + while( buflen > 0 ) + { + size_t use_len; + pem_init( &pem ); + + ret = pem_read_buffer( &pem, + "-----BEGIN CERTIFICATE-----", + "-----END CERTIFICATE-----", + buf, NULL, 0, &use_len ); + + if( ret == 0 ) + { + /* + * Was PEM encoded + */ + buflen -= use_len; + buf += use_len; + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_PRESENT ) + { + pem_free( &pem ); + + if( first_error == 0 ) + first_error = ret; + + continue; + } + else + break; + + ret = x509parse_crt_der( crt, pem.buf, pem.buflen ); + + pem_free( &pem ); + + if( ret != 0 ) + { + /* + * quit parsing on a memory error + */ + if( ret == POLARSSL_ERR_X509_MALLOC_FAILED ) + { + if( prev ) + prev->next = NULL; + + if( crt != chain ) + free( crt ); + + return( ret ); + } + + if( first_error == 0 ) + first_error = ret; + + total_failed++; + + memset( crt, 0, sizeof( x509_cert ) ); + continue; + } + + success = 1; + + /* + * Add new certificate to the list + */ + crt->next = (x509_cert *) malloc( sizeof( x509_cert ) ); + + if( crt->next == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + prev = crt; + crt = crt->next; + memset( crt, 0, sizeof( x509_cert ) ); + } + } +#endif + + if( crt->version == 0 ) + { + if( prev ) + prev->next = NULL; + + if( crt != chain ) + free( crt ); + } + + if( success ) + return( total_failed ); + else if( first_error ) + return( first_error ); + else + return( POLARSSL_ERR_X509_CERT_UNKNOWN_FORMAT ); +} + +/* + * Parse one or more CRLs and add them to the chained list + */ +int x509parse_crl( x509_crl *chain, const unsigned char *buf, size_t buflen ) +{ + int ret; + size_t len; + unsigned char *p, *end; + x509_crl *crl; +#if defined(POLARSSL_PEM_C) + size_t use_len; + pem_context pem; +#endif + + crl = chain; + + /* + * Check for valid input + */ + if( crl == NULL || buf == NULL ) + return( POLARSSL_ERR_X509_INVALID_INPUT ); + + while( crl->version != 0 && crl->next != NULL ) + crl = crl->next; + + /* + * Add new CRL on the end of the chain if needed. + */ + if ( crl->version != 0 && crl->next == NULL) + { + crl->next = (x509_crl *) malloc( sizeof( x509_crl ) ); + + if( crl->next == NULL ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + } + + crl = crl->next; + memset( crl, 0, sizeof( x509_crl ) ); + } + +#if defined(POLARSSL_PEM_C) + pem_init( &pem ); + ret = pem_read_buffer( &pem, + "-----BEGIN X509 CRL-----", + "-----END X509 CRL-----", + buf, NULL, 0, &use_len ); + + if( ret == 0 ) + { + /* + * Was PEM encoded + */ + buflen -= use_len; + buf += use_len; + + /* + * Steal PEM buffer + */ + p = pem.buf; + pem.buf = NULL; + len = pem.buflen; + pem_free( &pem ); + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_PRESENT ) + { + pem_free( &pem ); + return( ret ); + } + else + { + /* + * nope, copy the raw DER data + */ + p = (unsigned char *) malloc( len = buflen ); + + if( p == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + memcpy( p, buf, buflen ); + + buflen = 0; + } +#else + p = (unsigned char *) malloc( len = buflen ); + + if( p == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + memcpy( p, buf, buflen ); + + buflen = 0; +#endif + + crl->raw.p = p; + crl->raw.len = len; + end = p + len; + + /* + * CertificateList ::= SEQUENCE { + * tbsCertList TBSCertList, + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT ); + } + + if( len != (size_t) ( end - p ) ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + /* + * TBSCertList ::= SEQUENCE { + */ + crl->tbs.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); + } + + end = p + len; + crl->tbs.len = end - crl->tbs.p; + + /* + * Version ::= INTEGER OPTIONAL { v1(0), v2(1) } + * -- if present, MUST be v2 + * + * signature AlgorithmIdentifier + */ + if( ( ret = x509_crl_get_version( &p, end, &crl->version ) ) != 0 || + ( ret = x509_get_alg( &p, end, &crl->sig_oid1 ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + crl->version++; + + if( crl->version > 2 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_CERT_UNKNOWN_VERSION ); + } + + if( ( ret = x509_get_sig_alg( &crl->sig_oid1, &crl->sig_alg ) ) != 0 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_CERT_UNKNOWN_SIG_ALG ); + } + + /* + * issuer Name + */ + crl->issuer_raw.p = p; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); + } + + if( ( ret = x509_get_name( &p, p + len, &crl->issuer ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + crl->issuer_raw.len = p - crl->issuer_raw.p; + + /* + * thisUpdate Time + * nextUpdate Time OPTIONAL + */ + if( ( ret = x509_get_time( &p, end, &crl->this_update ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + if( ( ret = x509_get_time( &p, end, &crl->next_update ) ) != 0 ) + { + if ( ret != ( POLARSSL_ERR_X509_CERT_INVALID_DATE + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) && + ret != ( POLARSSL_ERR_X509_CERT_INVALID_DATE + + POLARSSL_ERR_ASN1_OUT_OF_DATA ) ) + { + x509_crl_free( crl ); + return( ret ); + } + } + + /* + * revokedCertificates SEQUENCE OF SEQUENCE { + * userCertificate CertificateSerialNumber, + * revocationDate Time, + * crlEntryExtensions Extensions OPTIONAL + * -- if present, MUST be v2 + * } OPTIONAL + */ + if( ( ret = x509_get_entries( &p, end, &crl->entry ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + /* + * crlExtensions EXPLICIT Extensions OPTIONAL + * -- if present, MUST be v2 + */ + if( crl->version == 2 ) + { + ret = x509_get_crl_ext( &p, end, &crl->crl_ext ); + + if( ret != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + } + + if( p != end ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + end = crl->raw.p + crl->raw.len; + + /* + * signatureAlgorithm AlgorithmIdentifier, + * signatureValue BIT STRING + */ + if( ( ret = x509_get_alg( &p, end, &crl->sig_oid2 ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + if( memcmp( crl->sig_oid1.p, crl->sig_oid2.p, crl->sig_oid1.len ) != 0 ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_CERT_SIG_MISMATCH ); + } + + if( ( ret = x509_get_sig( &p, end, &crl->sig ) ) != 0 ) + { + x509_crl_free( crl ); + return( ret ); + } + + if( p != end ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + if( buflen > 0 ) + { + crl->next = (x509_crl *) malloc( sizeof( x509_crl ) ); + + if( crl->next == NULL ) + { + x509_crl_free( crl ); + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + } + + crl = crl->next; + memset( crl, 0, sizeof( x509_crl ) ); + + return( x509parse_crl( crl, buf, buflen ) ); + } + + return( 0 ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Load all data from a file into a given buffer. + */ +int load_file( const char *path, unsigned char **buf, size_t *n ) +{ + FILE *f; + + if( ( f = fopen( path, "rb" ) ) == NULL ) + return( POLARSSL_ERR_X509_FILE_IO_ERROR ); + + fseek( f, 0, SEEK_END ); + *n = (size_t) ftell( f ); + fseek( f, 0, SEEK_SET ); + + if( ( *buf = (unsigned char *) malloc( *n + 1 ) ) == NULL ) + return( POLARSSL_ERR_X509_MALLOC_FAILED ); + + if( fread( *buf, 1, *n, f ) != *n ) + { + fclose( f ); + free( *buf ); + return( POLARSSL_ERR_X509_FILE_IO_ERROR ); + } + + fclose( f ); + + (*buf)[*n] = '\0'; + + return( 0 ); +} + +/* + * Load one or more certificates and add them to the chained list + */ +int x509parse_crtfile( x509_cert *chain, const char *path ) +{ + int ret; + size_t n; + unsigned char *buf; + + if ( (ret = load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + ret = x509parse_crt( chain, buf, n ); + + memset( buf, 0, n + 1 ); + free( buf ); + + return( ret ); +} + +/* + * Load one or more CRLs and add them to the chained list + */ +int x509parse_crlfile( x509_crl *chain, const char *path ) +{ + int ret; + size_t n; + unsigned char *buf; + + if ( (ret = load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + ret = x509parse_crl( chain, buf, n ); + + memset( buf, 0, n + 1 ); + free( buf ); + + return( ret ); +} + +/* + * Load and parse a private RSA key + */ +int x509parse_keyfile( rsa_context *rsa, const char *path, const char *pwd ) +{ + int ret; + size_t n; + unsigned char *buf; + + if ( (ret = load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + if( pwd == NULL ) + ret = x509parse_key( rsa, buf, n, NULL, 0 ); + else + ret = x509parse_key( rsa, buf, n, + (unsigned char *) pwd, strlen( pwd ) ); + + memset( buf, 0, n + 1 ); + free( buf ); + + return( ret ); +} + +/* + * Load and parse a public RSA key + */ +int x509parse_public_keyfile( rsa_context *rsa, const char *path ) +{ + int ret; + size_t n; + unsigned char *buf; + + if ( (ret = load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + ret = x509parse_public_key( rsa, buf, n ); + + memset( buf, 0, n + 1 ); + free( buf ); + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ + +/* + * Parse a private RSA key + */ +int x509parse_key( rsa_context *rsa, const unsigned char *key, size_t keylen, + const unsigned char *pwd, size_t pwdlen ) +{ + int ret; + size_t len; + unsigned char *p, *end; + unsigned char *p_alt; + x509_buf pk_alg_oid; + +#if defined(POLARSSL_PEM_C) + pem_context pem; + + pem_init( &pem ); + ret = pem_read_buffer( &pem, + "-----BEGIN RSA PRIVATE KEY-----", + "-----END RSA PRIVATE KEY-----", + key, pwd, pwdlen, &len ); + + if( ret == POLARSSL_ERR_PEM_NO_HEADER_PRESENT ) + { + ret = pem_read_buffer( &pem, + "-----BEGIN PRIVATE KEY-----", + "-----END PRIVATE KEY-----", + key, pwd, pwdlen, &len ); + } + + if( ret == 0 ) + { + /* + * Was PEM encoded + */ + keylen = pem.buflen; + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_PRESENT ) + { + pem_free( &pem ); + return( ret ); + } + + p = ( ret == 0 ) ? pem.buf : (unsigned char *) key; +#else + ((void) pwd); + ((void) pwdlen); + p = (unsigned char *) key; +#endif + end = p + keylen; + + /* + * Note: Depending on the type of private key file one can expect either a + * PrivatKeyInfo object (PKCS#8) or a RSAPrivateKey (PKCS#1) directly. + * + * PrivateKeyInfo ::= SEQUENCE { + * version Version, + * algorithm AlgorithmIdentifier, + * PrivateKey BIT STRING + * } + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + * + * RSAPrivateKey ::= SEQUENCE { + * version Version, + * modulus INTEGER, -- n + * publicExponent INTEGER, -- e + * privateExponent INTEGER, -- d + * prime1 INTEGER, -- p + * prime2 INTEGER, -- q + * exponent1 INTEGER, -- d mod (p-1) + * exponent2 INTEGER, -- d mod (q-1) + * coefficient INTEGER, -- (inverse of q) mod p + * otherPrimeInfos OtherPrimeInfos OPTIONAL + * } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); + } + + end = p + len; + + if( ( ret = asn1_get_int( &p, end, &rsa->ver ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); + } + + if( rsa->ver != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_VERSION + ret ); + } + + p_alt = p; + + if( ( ret = x509_get_alg( &p_alt, end, &pk_alg_oid ) ) != 0 ) + { + // Assume that we have the PKCS#1 format if wrong + // tag was encountered + // + if( ret != POLARSSL_ERR_X509_CERT_INVALID_ALG + + POLARSSL_ERR_ASN1_UNEXPECTED_TAG ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT ); + } + } + else + { + int can_handle; + + /* + * only RSA keys handled at this time + */ + can_handle = 0; + + if( pk_alg_oid.len == 9 && + memcmp( pk_alg_oid.p, OID_PKCS1_RSA, 9 ) == 0 ) + can_handle = 1; + + if( pk_alg_oid.len == 9 && + memcmp( pk_alg_oid.p, OID_PKCS1, 8 ) == 0 ) + { + if( pk_alg_oid.p[8] >= 2 && pk_alg_oid.p[8] <= 5 ) + can_handle = 1; + + if ( pk_alg_oid.p[8] >= 11 && pk_alg_oid.p[8] <= 14 ) + can_handle = 1; + } + + if( pk_alg_oid.len == 5 && + memcmp( pk_alg_oid.p, OID_RSA_SHA_OBS, 5 ) == 0 ) + can_handle = 1; + + if( can_handle == 0 ) + return( POLARSSL_ERR_X509_UNKNOWN_PK_ALG ); + + /* + * Parse the PKCS#8 format + */ + + p = p_alt; + if( ( ret = asn1_get_tag( &p, end, &len, ASN1_OCTET_STRING ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); + } + + if( ( end - p ) < 1 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + + POLARSSL_ERR_ASN1_OUT_OF_DATA ); + } + + end = p + len; + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); + } + + end = p + len; + + if( ( ret = asn1_get_int( &p, end, &rsa->ver ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); + } + + if( rsa->ver != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_VERSION + ret ); + } + } + + if( ( ret = asn1_get_mpi( &p, end, &rsa->N ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->E ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->D ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->P ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->Q ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->DP ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->DQ ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &rsa->QP ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); + } + + rsa->len = mpi_size( &rsa->N ); + + if( p != end ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + + if( ( ret = rsa_check_privkey( rsa ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( ret ); + } + +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + + return( 0 ); +} + +/* + * Parse a public RSA key + */ +int x509parse_public_key( rsa_context *rsa, const unsigned char *key, size_t keylen ) +{ + int ret; + size_t len; + unsigned char *p, *end; + x509_buf alg_oid; +#if defined(POLARSSL_PEM_C) + pem_context pem; + + pem_init( &pem ); + ret = pem_read_buffer( &pem, + "-----BEGIN PUBLIC KEY-----", + "-----END PUBLIC KEY-----", + key, NULL, 0, &len ); + + if( ret == 0 ) + { + /* + * Was PEM encoded + */ + keylen = pem.buflen; + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_PRESENT ) + { + pem_free( &pem ); + return( ret ); + } + + p = ( ret == 0 ) ? pem.buf : (unsigned char *) key; +#else + p = (unsigned char *) key; +#endif + end = p + keylen; + + /* + * PublicKeyInfo ::= SEQUENCE { + * algorithm AlgorithmIdentifier, + * PublicKey BIT STRING + * } + * + * AlgorithmIdentifier ::= SEQUENCE { + * algorithm OBJECT IDENTIFIER, + * parameters ANY DEFINED BY algorithm OPTIONAL + * } + * + * RSAPublicKey ::= SEQUENCE { + * modulus INTEGER, -- n + * publicExponent INTEGER -- e + * } + */ + + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_CERT_INVALID_FORMAT + ret ); + } + + if( ( ret = x509_get_pubkey( &p, end, &alg_oid, &rsa->N, &rsa->E ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); + } + + if( ( ret = rsa_check_pubkey( rsa ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + rsa_free( rsa ); + return( ret ); + } + + rsa->len = mpi_size( &rsa->N ); + +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + + return( 0 ); +} + +#if defined(POLARSSL_DHM_C) +/* + * Parse DHM parameters + */ +int x509parse_dhm( dhm_context *dhm, const unsigned char *dhmin, size_t dhminlen ) +{ + int ret; + size_t len; + unsigned char *p, *end; +#if defined(POLARSSL_PEM_C) + pem_context pem; + + pem_init( &pem ); + + ret = pem_read_buffer( &pem, + "-----BEGIN DH PARAMETERS-----", + "-----END DH PARAMETERS-----", + dhmin, NULL, 0, &dhminlen ); + + if( ret == 0 ) + { + /* + * Was PEM encoded + */ + dhminlen = pem.buflen; + } + else if( ret != POLARSSL_ERR_PEM_NO_HEADER_PRESENT ) + { + pem_free( &pem ); + return( ret ); + } + + p = ( ret == 0 ) ? pem.buf : (unsigned char *) dhmin; +#else + p = (unsigned char *) dhmin; +#endif + end = p + dhminlen; + + memset( dhm, 0, sizeof( dhm_context ) ); + + /* + * DHParams ::= SEQUENCE { + * prime INTEGER, -- P + * generator INTEGER, -- g + * } + */ + if( ( ret = asn1_get_tag( &p, end, &len, + ASN1_CONSTRUCTED | ASN1_SEQUENCE ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); + } + + end = p + len; + + if( ( ret = asn1_get_mpi( &p, end, &dhm->P ) ) != 0 || + ( ret = asn1_get_mpi( &p, end, &dhm->G ) ) != 0 ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + dhm_free( dhm ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + ret ); + } + + if( p != end ) + { +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + dhm_free( dhm ); + return( POLARSSL_ERR_X509_KEY_INVALID_FORMAT + + POLARSSL_ERR_ASN1_LENGTH_MISMATCH ); + } + +#if defined(POLARSSL_PEM_C) + pem_free( &pem ); +#endif + + return( 0 ); +} + +#if defined(POLARSSL_FS_IO) +/* + * Load and parse a private RSA key + */ +int x509parse_dhmfile( dhm_context *dhm, const char *path ) +{ + int ret; + size_t n; + unsigned char *buf; + + if ( ( ret = load_file( path, &buf, &n ) ) != 0 ) + return( ret ); + + ret = x509parse_dhm( dhm, buf, n ); + + memset( buf, 0, n + 1 ); + free( buf ); + + return( ret ); +} +#endif /* POLARSSL_FS_IO */ +#endif /* POLARSSL_DHM_C */ + +#if defined _MSC_VER && !defined snprintf +#include + +#if !defined vsnprintf +#define vsnprintf _vsnprintf +#endif // vsnprintf + +/* + * Windows _snprintf and _vsnprintf are not compatible to linux versions. + * Result value is not size of buffer needed, but -1 if no fit is possible. + * + * This fuction tries to 'fix' this by at least suggesting enlarging the + * size by 20. + */ +int compat_snprintf(char *str, size_t size, const char *format, ...) +{ + va_list ap; + int res = -1; + + va_start( ap, format ); + + res = vsnprintf( str, size, format, ap ); + + va_end( ap ); + + // No quick fix possible + if ( res < 0 ) + return( (int) size + 20 ); + + return res; +} + +#define snprintf compat_snprintf +#endif + +#define POLARSSL_ERR_DEBUG_BUF_TOO_SMALL -2 + +#define SAFE_SNPRINTF() \ +{ \ + if( ret == -1 ) \ + return( -1 ); \ + \ + if ( (unsigned int) ret > n ) { \ + p[n - 1] = '\0'; \ + return POLARSSL_ERR_DEBUG_BUF_TOO_SMALL;\ + } \ + \ + n -= (unsigned int) ret; \ + p += (unsigned int) ret; \ +} + +/* + * Store the name in printable form into buf; no more + * than size characters will be written + */ +int x509parse_dn_gets( char *buf, size_t size, const x509_name *dn ) +{ + int ret; + size_t i, n; + unsigned char c; + const x509_name *name; + char s[128], *p; + + memset( s, 0, sizeof( s ) ); + + name = dn; + p = buf; + n = size; + + while( name != NULL ) + { + if( name != dn ) + { + ret = snprintf( p, n, ", " ); + SAFE_SNPRINTF(); + } + + if( memcmp( name->oid.p, OID_X520, 2 ) == 0 ) + { + switch( name->oid.p[2] ) + { + case X520_COMMON_NAME: + ret = snprintf( p, n, "CN=" ); break; + + case X520_COUNTRY: + ret = snprintf( p, n, "C=" ); break; + + case X520_LOCALITY: + ret = snprintf( p, n, "L=" ); break; + + case X520_STATE: + ret = snprintf( p, n, "ST=" ); break; + + case X520_ORGANIZATION: + ret = snprintf( p, n, "O=" ); break; + + case X520_ORG_UNIT: + ret = snprintf( p, n, "OU=" ); break; + + default: + ret = snprintf( p, n, "0x%02X=", + name->oid.p[2] ); + break; + } + SAFE_SNPRINTF(); + } + else if( memcmp( name->oid.p, OID_PKCS9, 8 ) == 0 ) + { + switch( name->oid.p[8] ) + { + case PKCS9_EMAIL: + ret = snprintf( p, n, "emailAddress=" ); break; + + default: + ret = snprintf( p, n, "0x%02X=", + name->oid.p[8] ); + break; + } + SAFE_SNPRINTF(); + } + else + { + ret = snprintf( p, n, "\?\?=" ); + SAFE_SNPRINTF(); + } + + for( i = 0; i < name->val.len; i++ ) + { + if( i >= sizeof( s ) - 1 ) + break; + + c = name->val.p[i]; + if( c < 32 || c == 127 || ( c > 128 && c < 160 ) ) + s[i] = '?'; + else s[i] = c; + } + s[i] = '\0'; + ret = snprintf( p, n, "%s", s ); + SAFE_SNPRINTF(); + name = name->next; + } + + return( (int) ( size - n ) ); +} + +/* + * Store the serial in printable form into buf; no more + * than size characters will be written + */ +int x509parse_serial_gets( char *buf, size_t size, const x509_buf *serial ) +{ + int ret; + size_t i, n, nr; + char *p; + + p = buf; + n = size; + + nr = ( serial->len <= 32 ) + ? serial->len : 28; + + for( i = 0; i < nr; i++ ) + { + if( i == 0 && nr > 1 && serial->p[i] == 0x0 ) + continue; + + ret = snprintf( p, n, "%02X%s", + serial->p[i], ( i < nr - 1 ) ? ":" : "" ); + SAFE_SNPRINTF(); + } + + if( nr != serial->len ) + { + ret = snprintf( p, n, "...." ); + SAFE_SNPRINTF(); + } + + return( (int) ( size - n ) ); +} + +/* + * Return an informational string about the certificate. + */ +int x509parse_cert_info( char *buf, size_t size, const char *prefix, + const x509_cert *crt ) +{ + int ret; + size_t n; + char *p; + + p = buf; + n = size; + + ret = snprintf( p, n, "%scert. version : %d\n", + prefix, crt->version ); + SAFE_SNPRINTF(); + ret = snprintf( p, n, "%sserial number : ", + prefix ); + SAFE_SNPRINTF(); + + ret = x509parse_serial_gets( p, n, &crt->serial); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sissuer name : ", prefix ); + SAFE_SNPRINTF(); + ret = x509parse_dn_gets( p, n, &crt->issuer ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%ssubject name : ", prefix ); + SAFE_SNPRINTF(); + ret = x509parse_dn_gets( p, n, &crt->subject ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sissued on : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crt->valid_from.year, crt->valid_from.mon, + crt->valid_from.day, crt->valid_from.hour, + crt->valid_from.min, crt->valid_from.sec ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sexpires on : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crt->valid_to.year, crt->valid_to.mon, + crt->valid_to.day, crt->valid_to.hour, + crt->valid_to.min, crt->valid_to.sec ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%ssigned using : RSA+", prefix ); + SAFE_SNPRINTF(); + + switch( crt->sig_alg ) + { + case SIG_RSA_MD2 : ret = snprintf( p, n, "MD2" ); break; + case SIG_RSA_MD4 : ret = snprintf( p, n, "MD4" ); break; + case SIG_RSA_MD5 : ret = snprintf( p, n, "MD5" ); break; + case SIG_RSA_SHA1 : ret = snprintf( p, n, "SHA1" ); break; + case SIG_RSA_SHA224 : ret = snprintf( p, n, "SHA224" ); break; + case SIG_RSA_SHA256 : ret = snprintf( p, n, "SHA256" ); break; + case SIG_RSA_SHA384 : ret = snprintf( p, n, "SHA384" ); break; + case SIG_RSA_SHA512 : ret = snprintf( p, n, "SHA512" ); break; + default: ret = snprintf( p, n, "???" ); break; + } + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sRSA key size : %d bits\n", prefix, + (int) crt->rsa.N.n * (int) sizeof( unsigned long ) * 8 ); + SAFE_SNPRINTF(); + + return( (int) ( size - n ) ); +} + +/* Compare a given OID string with an OID x509_buf * */ +#define OID_CMP(oid_str, oid_buf) \ + ( ( OID_SIZE(oid_str) == (oid_buf)->len ) && \ + memcmp( (oid_str), (oid_buf)->p, (oid_buf)->len) == 0) + +/* + * Return an informational string describing the given OID + */ +const char *x509_oid_get_description( x509_buf *oid ) +{ + if ( oid == NULL ) + return ( NULL ); + + else if( OID_CMP( OID_SERVER_AUTH, oid ) ) + return( STRING_SERVER_AUTH ); + + else if( OID_CMP( OID_CLIENT_AUTH, oid ) ) + return( STRING_CLIENT_AUTH ); + + else if( OID_CMP( OID_CODE_SIGNING, oid ) ) + return( STRING_CODE_SIGNING ); + + else if( OID_CMP( OID_EMAIL_PROTECTION, oid ) ) + return( STRING_EMAIL_PROTECTION ); + + else if( OID_CMP( OID_TIME_STAMPING, oid ) ) + return( STRING_TIME_STAMPING ); + + else if( OID_CMP( OID_OCSP_SIGNING, oid ) ) + return( STRING_OCSP_SIGNING ); + + return( NULL ); +} + +/* Return the x.y.z.... style numeric string for the given OID */ +int x509_oid_get_numeric_string( char *buf, size_t size, x509_buf *oid ) +{ + int ret; + size_t i, n; + unsigned int value; + char *p; + + p = buf; + n = size; + + /* First byte contains first two dots */ + if( oid->len > 0 ) + { + ret = snprintf( p, n, "%d.%d", oid->p[0]/40, oid->p[0]%40 ); + SAFE_SNPRINTF(); + } + + /* TODO: value can overflow in value. */ + value = 0; + for( i = 1; i < oid->len; i++ ) + { + value <<= 7; + value += oid->p[i] & 0x7F; + + if( !( oid->p[i] & 0x80 ) ) + { + /* Last byte */ + ret = snprintf( p, n, ".%d", value ); + SAFE_SNPRINTF(); + value = 0; + } + } + + return( (int) ( size - n ) ); +} + +/* + * Return an informational string about the CRL. + */ +int x509parse_crl_info( char *buf, size_t size, const char *prefix, + const x509_crl *crl ) +{ + int ret; + size_t n; + char *p; + const x509_crl_entry *entry; + + p = buf; + n = size; + + ret = snprintf( p, n, "%sCRL version : %d", + prefix, crl->version ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sissuer name : ", prefix ); + SAFE_SNPRINTF(); + ret = x509parse_dn_gets( p, n, &crl->issuer ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%sthis update : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crl->this_update.year, crl->this_update.mon, + crl->this_update.day, crl->this_update.hour, + crl->this_update.min, crl->this_update.sec ); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n%snext update : " \ + "%04d-%02d-%02d %02d:%02d:%02d", prefix, + crl->next_update.year, crl->next_update.mon, + crl->next_update.day, crl->next_update.hour, + crl->next_update.min, crl->next_update.sec ); + SAFE_SNPRINTF(); + + entry = &crl->entry; + + ret = snprintf( p, n, "\n%sRevoked certificates:", + prefix ); + SAFE_SNPRINTF(); + + while( entry != NULL && entry->raw.len != 0 ) + { + ret = snprintf( p, n, "\n%sserial number: ", + prefix ); + SAFE_SNPRINTF(); + + ret = x509parse_serial_gets( p, n, &entry->serial); + SAFE_SNPRINTF(); + + ret = snprintf( p, n, " revocation date: " \ + "%04d-%02d-%02d %02d:%02d:%02d", + entry->revocation_date.year, entry->revocation_date.mon, + entry->revocation_date.day, entry->revocation_date.hour, + entry->revocation_date.min, entry->revocation_date.sec ); + SAFE_SNPRINTF(); + + entry = entry->next; + } + + ret = snprintf( p, n, "\n%ssigned using : RSA+", prefix ); + SAFE_SNPRINTF(); + + switch( crl->sig_alg ) + { + case SIG_RSA_MD2 : ret = snprintf( p, n, "MD2" ); break; + case SIG_RSA_MD4 : ret = snprintf( p, n, "MD4" ); break; + case SIG_RSA_MD5 : ret = snprintf( p, n, "MD5" ); break; + case SIG_RSA_SHA1 : ret = snprintf( p, n, "SHA1" ); break; + case SIG_RSA_SHA224 : ret = snprintf( p, n, "SHA224" ); break; + case SIG_RSA_SHA256 : ret = snprintf( p, n, "SHA256" ); break; + case SIG_RSA_SHA384 : ret = snprintf( p, n, "SHA384" ); break; + case SIG_RSA_SHA512 : ret = snprintf( p, n, "SHA512" ); break; + default: ret = snprintf( p, n, "???" ); break; + } + SAFE_SNPRINTF(); + + ret = snprintf( p, n, "\n" ); + SAFE_SNPRINTF(); + + return( (int) ( size - n ) ); +} + +/* + * Return 0 if the x509_time is still valid, or 1 otherwise. + */ +int x509parse_time_expired( const x509_time *to ) +{ + int year, mon, day; + int hour, min, sec; + +#if defined(_WIN32) + SYSTEMTIME st; + + GetLocalTime(&st); + + year = st.wYear; + mon = st.wMonth; + day = st.wDay; + hour = st.wHour; + min = st.wMinute; + sec = st.wSecond; +#else + struct tm *lt; + time_t tt; + + tt = time( NULL ); + lt = localtime( &tt ); + + year = lt->tm_year + 1900; + mon = lt->tm_mon + 1; + day = lt->tm_mday; + hour = lt->tm_hour; + min = lt->tm_min; + sec = lt->tm_sec; +#endif + + if( year > to->year ) + return( 1 ); + + if( year == to->year && + mon > to->mon ) + return( 1 ); + + if( year == to->year && + mon == to->mon && + day > to->day ) + return( 1 ); + + if( year == to->year && + mon == to->mon && + day == to->day && + hour > to->hour ) + return( 1 ); + + if( year == to->year && + mon == to->mon && + day == to->day && + hour == to->hour && + min > to->min ) + return( 1 ); + + if( year == to->year && + mon == to->mon && + day == to->day && + hour == to->hour && + min == to->min && + sec > to->sec ) + return( 1 ); + + return( 0 ); +} + +/* + * Return 1 if the certificate is revoked, or 0 otherwise. + */ +int x509parse_revoked( const x509_cert *crt, const x509_crl *crl ) +{ + const x509_crl_entry *cur = &crl->entry; + + while( cur != NULL && cur->serial.len != 0 ) + { + if( crt->serial.len == cur->serial.len && + memcmp( crt->serial.p, cur->serial.p, crt->serial.len ) == 0 ) + { + if( x509parse_time_expired( &cur->revocation_date ) ) + return( 1 ); + } + + cur = cur->next; + } + + return( 0 ); +} + +/* + * Wrapper for x509 hashes. + * + * \param out Buffer to receive the hash (Should be at least 64 bytes) + */ +static void x509_hash( const unsigned char *in, size_t len, int alg, + unsigned char *out ) +{ + switch( alg ) + { +#if defined(POLARSSL_MD2_C) + case SIG_RSA_MD2 : md2( in, len, out ); break; +#endif +#if defined(POLARSSL_MD4_C) + case SIG_RSA_MD4 : md4( in, len, out ); break; +#endif +#if defined(POLARSSL_MD5_C) + case SIG_RSA_MD5 : md5( in, len, out ); break; +#endif +#if defined(POLARSSL_SHA1_C) + case SIG_RSA_SHA1 : sha1( in, len, out ); break; +#endif +#if defined(POLARSSL_SHA2_C) + case SIG_RSA_SHA224 : sha2( in, len, out, 1 ); break; + case SIG_RSA_SHA256 : sha2( in, len, out, 0 ); break; +#endif +#if defined(POLARSSL_SHA4_C) + case SIG_RSA_SHA384 : sha4( in, len, out, 1 ); break; + case SIG_RSA_SHA512 : sha4( in, len, out, 0 ); break; +#endif + default: + memset( out, '\xFF', 64 ); + break; + } +} + +/* + * Check that the given certificate is valid accoring to the CRL. + */ +static int x509parse_verifycrl(x509_cert *crt, x509_cert *ca, + x509_crl *crl_list) +{ + int flags = 0; + int hash_id; + unsigned char hash[64]; + + /* + * TODO: What happens if no CRL is present? + * Suggestion: Revocation state should be unknown if no CRL is present. + * For backwards compatibility this is not yet implemented. + */ + + while( ca != NULL && crl_list != NULL && crl_list->version != 0 ) + { + if( crl_list->issuer_raw.len != ca->subject_raw.len || + memcmp( crl_list->issuer_raw.p, ca->subject_raw.p, + crl_list->issuer_raw.len ) != 0 ) + { + crl_list = crl_list->next; + continue; + } + + /* + * Check if CRL is correctly signed by the trusted CA + */ + hash_id = crl_list->sig_alg; + + x509_hash( crl_list->tbs.p, crl_list->tbs.len, hash_id, hash ); + + if( !rsa_pkcs1_verify( &ca->rsa, RSA_PUBLIC, hash_id, + 0, hash, crl_list->sig.p ) == 0 ) + { + /* + * CRL is not trusted + */ + flags |= BADCRL_NOT_TRUSTED; + break; + } + + /* + * Check for validity of CRL (Do not drop out) + */ + if( x509parse_time_expired( &crl_list->next_update ) ) + flags |= BADCRL_EXPIRED; + + /* + * Check if certificate is revoked + */ + if( x509parse_revoked(crt, crl_list) ) + { + flags |= BADCERT_REVOKED; + break; + } + + crl_list = crl_list->next; + } + return flags; +} + +int x509_wildcard_verify( const char *cn, x509_buf *name ) +{ + size_t i; + size_t cn_idx = 0; + + if( name->len < 3 || name->p[0] != '*' || name->p[1] != '.' ) + return( 0 ); + + for( i = 0; i < strlen( cn ); ++i ) + { + if( cn[i] == '.' ) + { + cn_idx = i; + break; + } + } + + if( cn_idx == 0 ) + return( 0 ); + + if( memcmp( name->p + 1, cn + cn_idx, name->len - 1 ) == 0 && + strlen( cn ) - cn_idx == name->len - 1 ) + { + return( 1 ); + } + + return( 0 ); +} + +/* + * Verify the certificate validity + */ +int x509parse_verify( x509_cert *crt, + x509_cert *trust_ca, + x509_crl *ca_crl, + const char *cn, int *flags, + int (*f_vrfy)(void *, x509_cert *, int, int), + void *p_vrfy ) +{ + size_t cn_len; + int hash_id; + int pathlen; + x509_cert *parent; + x509_name *name; + unsigned char hash[64]; + x509_sequence *cur = NULL; + + *flags = 0; + + if( x509parse_time_expired( &crt->valid_to ) ) + *flags = BADCERT_EXPIRED; + + if( cn != NULL ) + { + name = &crt->subject; + cn_len = strlen( cn ); + + while( name != NULL ) + { + if( memcmp( name->oid.p, OID_CN, 3 ) == 0 ) + { + if( memcmp( name->val.p, cn, cn_len ) == 0 && + name->val.len == cn_len ) + break; + + if( memcmp( name->val.p, "*.", 2 ) == 0 && + x509_wildcard_verify( cn, &name->val ) ) + break; + } + + name = name->next; + } + + if( name == NULL ) + { + if( crt->ext_types & EXT_SUBJECT_ALT_NAME ) + { + cur = &crt->subject_alt_names; + + while( cur != NULL ) + { + if( memcmp( cn, cur->buf.p, cn_len ) == 0 && + cur->buf.len == cn_len ) + break; + + if( memcmp( cur->buf.p, "*.", 2 ) == 0 && + x509_wildcard_verify( cn, &cur->buf ) ) + break; + + cur = cur->next; + } + } + + if( cur == NULL ) + *flags |= BADCERT_CN_MISMATCH; + } + } + + /* + * Iterate upwards in the given cert chain, + * ignoring any upper cert with CA != TRUE. + */ + parent = crt->next; + + pathlen = 1; + + while( parent != NULL && parent->version != 0 ) + { + if( parent->ca_istrue == 0 || + crt->issuer_raw.len != parent->subject_raw.len || + memcmp( crt->issuer_raw.p, parent->subject_raw.p, + crt->issuer_raw.len ) != 0 ) + { + parent = parent->next; + continue; + } + + hash_id = crt->sig_alg; + + x509_hash( crt->tbs.p, crt->tbs.len, hash_id, hash ); + + if( rsa_pkcs1_verify( &parent->rsa, RSA_PUBLIC, hash_id, 0, hash, + crt->sig.p ) != 0 ) + *flags |= BADCERT_NOT_TRUSTED; + + /* Check trusted CA's CRL for the given crt */ + *flags |= x509parse_verifycrl(crt, parent, ca_crl); + + /* crt is verified to be a child of the parent cur, call verify callback */ + if( NULL != f_vrfy ) + { + if( f_vrfy( p_vrfy, crt, pathlen - 1, ( *flags == 0 ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); + else + *flags = 0; + } + else if( *flags != 0 ) + return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); + + pathlen++; + + crt = parent; + parent = crt->next; + } + + /* + * Attempt to validate topmost cert with our CA chain. + */ + *flags |= BADCERT_NOT_TRUSTED; + + while( trust_ca != NULL && trust_ca->version != 0 ) + { + if( crt->issuer_raw.len != trust_ca->subject_raw.len || + memcmp( crt->issuer_raw.p, trust_ca->subject_raw.p, + crt->issuer_raw.len ) != 0 ) + { + trust_ca = trust_ca->next; + continue; + } + + if( trust_ca->max_pathlen > 0 && + trust_ca->max_pathlen < pathlen ) + break; + + hash_id = crt->sig_alg; + + x509_hash( crt->tbs.p, crt->tbs.len, hash_id, hash ); + + if( rsa_pkcs1_verify( &trust_ca->rsa, RSA_PUBLIC, hash_id, + 0, hash, crt->sig.p ) == 0 ) + { + /* + * cert. is signed by a trusted CA + */ + *flags &= ~BADCERT_NOT_TRUSTED; + break; + } + + trust_ca = trust_ca->next; + } + + /* Check trusted CA's CRL for the given crt */ + *flags |= x509parse_verifycrl( crt, trust_ca, ca_crl ); + + /* Verification succeeded, call callback on top cert */ + if( NULL != f_vrfy ) + { + if( f_vrfy(p_vrfy, crt, pathlen-1, ( *flags == 0 ) ) != 0 ) + return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); + else + *flags = 0; + } + else if( *flags != 0 ) + return( POLARSSL_ERR_X509_CERT_VERIFY_FAILED ); + + return( 0 ); +} + +/* + * Unallocate all certificate data + */ +void x509_free( x509_cert *crt ) +{ + x509_cert *cert_cur = crt; + x509_cert *cert_prv; + x509_name *name_cur; + x509_name *name_prv; + x509_sequence *seq_cur; + x509_sequence *seq_prv; + + if( crt == NULL ) + return; + + do + { + rsa_free( &cert_cur->rsa ); + + name_cur = cert_cur->issuer.next; + while( name_cur != NULL ) + { + name_prv = name_cur; + name_cur = name_cur->next; + memset( name_prv, 0, sizeof( x509_name ) ); + free( name_prv ); + } + + name_cur = cert_cur->subject.next; + while( name_cur != NULL ) + { + name_prv = name_cur; + name_cur = name_cur->next; + memset( name_prv, 0, sizeof( x509_name ) ); + free( name_prv ); + } + + seq_cur = cert_cur->ext_key_usage.next; + while( seq_cur != NULL ) + { + seq_prv = seq_cur; + seq_cur = seq_cur->next; + memset( seq_prv, 0, sizeof( x509_sequence ) ); + free( seq_prv ); + } + + seq_cur = cert_cur->subject_alt_names.next; + while( seq_cur != NULL ) + { + seq_prv = seq_cur; + seq_cur = seq_cur->next; + memset( seq_prv, 0, sizeof( x509_sequence ) ); + free( seq_prv ); + } + + if( cert_cur->raw.p != NULL ) + { + memset( cert_cur->raw.p, 0, cert_cur->raw.len ); + free( cert_cur->raw.p ); + } + + cert_cur = cert_cur->next; + } + while( cert_cur != NULL ); + + cert_cur = crt; + do + { + cert_prv = cert_cur; + cert_cur = cert_cur->next; + + memset( cert_prv, 0, sizeof( x509_cert ) ); + if( cert_prv != crt ) + free( cert_prv ); + } + while( cert_cur != NULL ); +} + +/* + * Unallocate all CRL data + */ +void x509_crl_free( x509_crl *crl ) +{ + x509_crl *crl_cur = crl; + x509_crl *crl_prv; + x509_name *name_cur; + x509_name *name_prv; + x509_crl_entry *entry_cur; + x509_crl_entry *entry_prv; + + if( crl == NULL ) + return; + + do + { + name_cur = crl_cur->issuer.next; + while( name_cur != NULL ) + { + name_prv = name_cur; + name_cur = name_cur->next; + memset( name_prv, 0, sizeof( x509_name ) ); + free( name_prv ); + } + + entry_cur = crl_cur->entry.next; + while( entry_cur != NULL ) + { + entry_prv = entry_cur; + entry_cur = entry_cur->next; + memset( entry_prv, 0, sizeof( x509_crl_entry ) ); + free( entry_prv ); + } + + if( crl_cur->raw.p != NULL ) + { + memset( crl_cur->raw.p, 0, crl_cur->raw.len ); + free( crl_cur->raw.p ); + } + + crl_cur = crl_cur->next; + } + while( crl_cur != NULL ); + + crl_cur = crl; + do + { + crl_prv = crl_cur; + crl_cur = crl_cur->next; + + memset( crl_prv, 0, sizeof( x509_crl ) ); + if( crl_prv != crl ) + free( crl_prv ); + } + while( crl_cur != NULL ); +} + +#if defined(POLARSSL_SELF_TEST) + +#include "polarssl/certs.h" + +/* + * Checkup routine + */ +int x509_self_test( int verbose ) +{ +#if defined(POLARSSL_CERTS_C) && defined(POLARSSL_MD5_C) + int ret; + int flags; + size_t i, j; + x509_cert cacert; + x509_cert clicert; + rsa_context rsa; +#if defined(POLARSSL_DHM_C) + dhm_context dhm; +#endif + + if( verbose != 0 ) + printf( " X.509 certificate load: " ); + + memset( &clicert, 0, sizeof( x509_cert ) ); + + ret = x509parse_crt( &clicert, (unsigned char *) test_cli_crt, + strlen( test_cli_crt ) ); + if( ret != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( ret ); + } + + memset( &cacert, 0, sizeof( x509_cert ) ); + + ret = x509parse_crt( &cacert, (unsigned char *) test_ca_crt, + strlen( test_ca_crt ) ); + if( ret != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( ret ); + } + + if( verbose != 0 ) + printf( "passed\n X.509 private key load: " ); + + i = strlen( test_ca_key ); + j = strlen( test_ca_pwd ); + + rsa_init( &rsa, RSA_PKCS_V15, 0 ); + + if( ( ret = x509parse_key( &rsa, + (unsigned char *) test_ca_key, i, + (unsigned char *) test_ca_pwd, j ) ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( ret ); + } + + if( verbose != 0 ) + printf( "passed\n X.509 signature verify: "); + + ret = x509parse_verify( &clicert, &cacert, NULL, "PolarSSL Client 2", &flags, NULL, NULL ); + if( ret != 0 ) + { + printf("%02x", flags); + if( verbose != 0 ) + printf( "failed\n" ); + + return( ret ); + } + +#if defined(POLARSSL_DHM_C) + if( verbose != 0 ) + printf( "passed\n X.509 DHM parameter load: " ); + + i = strlen( test_dhm_params ); + j = strlen( test_ca_pwd ); + + if( ( ret = x509parse_dhm( &dhm, (unsigned char *) test_dhm_params, i ) ) != 0 ) + { + if( verbose != 0 ) + printf( "failed\n" ); + + return( ret ); + } + + if( verbose != 0 ) + printf( "passed\n\n" ); +#endif + + x509_free( &cacert ); + x509_free( &clicert ); + rsa_free( &rsa ); +#if defined(POLARSSL_DHM_C) + dhm_free( &dhm ); +#endif + + return( 0 ); +#else + ((void) verbose); + return( POLARSSL_ERR_X509_FEATURE_UNAVAILABLE ); +#endif +} + +#endif + +#endif -- cgit v1.2.3