Implement v3 Extension parsing through ASN.1 SEQUENCE OF traversal

This commit rewrites the v3 ext parsing routine `x509_crt_get_ext_cb()`
in terms of the generic ASN.1 SEQUENCE traversal routine.
This commit is contained in:
Hanno Becker 2019-02-22 11:09:48 +00:00
parent c7c638eddd
commit f1b39bf18c

View file

@ -393,7 +393,7 @@ static int x509_get_basic_constraints( unsigned char **p,
if( ( ret = mbedtls_asn1_get_tag( p, end, &len, if( ( ret = mbedtls_asn1_get_tag( p, end, &len,
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 ) MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); return( ret );
if( *p == end ) if( *p == end )
return( 0 ); return( 0 );
@ -404,7 +404,7 @@ static int x509_get_basic_constraints( unsigned char **p,
ret = mbedtls_asn1_get_int( p, end, ca_istrue ); ret = mbedtls_asn1_get_int( p, end, ca_istrue );
if( ret != 0 ) if( ret != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); return( ret );
if( *ca_istrue != 0 ) if( *ca_istrue != 0 )
*ca_istrue = 1; *ca_istrue = 1;
@ -414,11 +414,10 @@ static int x509_get_basic_constraints( unsigned char **p,
return( 0 ); return( 0 );
if( ( ret = mbedtls_asn1_get_int( p, end, max_pathlen ) ) != 0 ) if( ( ret = mbedtls_asn1_get_int( p, end, max_pathlen ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); return( ret );
if( *p != end ) if( *p != end )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + return( MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
(*max_pathlen)++; (*max_pathlen)++;
@ -433,11 +432,10 @@ static int x509_get_ns_cert_type( unsigned char **p,
mbedtls_x509_bitstring bs = { 0, 0, NULL }; mbedtls_x509_bitstring bs = { 0, 0, NULL };
if( ( ret = mbedtls_asn1_get_bitstring( p, end, &bs ) ) != 0 ) if( ( ret = mbedtls_asn1_get_bitstring( p, end, &bs ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); return( ret );
if( bs.len != 1 ) if( bs.len != 1 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH );
MBEDTLS_ERR_ASN1_INVALID_LENGTH );
/* Get actual bitstring */ /* Get actual bitstring */
*ns_cert_type = *bs.p; *ns_cert_type = *bs.p;
@ -453,11 +451,10 @@ static int x509_get_key_usage( unsigned char **p,
mbedtls_x509_bitstring bs = { 0, 0, NULL }; mbedtls_x509_bitstring bs = { 0, 0, NULL };
if( ( ret = mbedtls_asn1_get_bitstring( p, end, &bs ) ) != 0 ) if( ( ret = mbedtls_asn1_get_bitstring( p, end, &bs ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); return( ret );
if( bs.len < 1 ) if( bs.len < 1 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + return( MBEDTLS_ERR_ASN1_INVALID_LENGTH );
MBEDTLS_ERR_ASN1_INVALID_LENGTH );
/* Get actual bitstring */ /* Get actual bitstring */
*key_usage = 0; *key_usage = 0;
@ -478,17 +475,8 @@ static int x509_get_ext_key_usage( unsigned char **p,
const unsigned char *end, const unsigned char *end,
mbedtls_x509_sequence *ext_key_usage) mbedtls_x509_sequence *ext_key_usage)
{ {
int ret; return( mbedtls_asn1_get_sequence_of( p, end, ext_key_usage,
MBEDTLS_ASN1_OID ) );
if( ( ret = mbedtls_asn1_get_sequence_of( p, end, ext_key_usage, MBEDTLS_ASN1_OID ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
/* Sequence length must be >= 1 */
if( ext_key_usage->buf.p == NULL )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
MBEDTLS_ERR_ASN1_INVALID_LENGTH );
return( 0 );
} }
/* /*
@ -546,100 +534,84 @@ static int x509_get_subject_alt_name( unsigned char *p,
const unsigned char *end, const unsigned char *end,
mbedtls_x509_sequence *subject_alt_name ) mbedtls_x509_sequence *subject_alt_name )
{ {
int ret; return( mbedtls_asn1_traverse_sequence_of( &p, end,
ret = mbedtls_asn1_traverse_sequence_of( &p, end,
MBEDTLS_ASN1_TAG_CLASS_MASK, MBEDTLS_ASN1_TAG_CLASS_MASK,
MBEDTLS_ASN1_CONTEXT_SPECIFIC, MBEDTLS_ASN1_CONTEXT_SPECIFIC,
MBEDTLS_ASN1_TAG_VALUE_MASK, MBEDTLS_ASN1_TAG_VALUE_MASK,
2 /* SubjectAlt DNS */, 2 /* SubjectAlt DNS */,
x509_get_subject_alt_name_cb, x509_get_subject_alt_name_cb,
(void*) &subject_alt_name ); (void*) &subject_alt_name ) );
if( ret != 0 )
ret += MBEDTLS_ERR_X509_INVALID_EXTENSIONS;
return( ret );
} }
/* /*
* X.509 v3 extensions * X.509 v3 extensions
* *
*/ */
static int x509_get_crt_ext( unsigned char **p, static int x509_crt_get_ext_cb( void *ctx,
const unsigned char *end, int tag,
mbedtls_x509_crt *crt ) unsigned char *p,
size_t ext_len )
{ {
int ret; int ret;
mbedtls_x509_crt *crt = (mbedtls_x509_crt *) ctx;
size_t len; size_t len;
unsigned char *end_ext_data, *end_ext_octet; unsigned char *end, *end_ext_octet;
mbedtls_x509_buf extn_oid = { 0, 0, NULL };
int is_critical = 0; /* DEFAULT FALSE */
int ext_type = 0;
if( *p == end ) ((void) tag);
return( 0 );
if( ( ret = mbedtls_x509_get_ext( p, end, &crt->v3_ext, 3 ) ) != 0 )
return( ret );
end = crt->v3_ext.p + crt->v3_ext.len;
while( *p < end )
{
/* /*
* Extension ::= SEQUENCE { * Extension ::= SEQUENCE {
* extnID OBJECT IDENTIFIER, * extnID OBJECT IDENTIFIER,
* critical BOOLEAN DEFAULT FALSE, * critical BOOLEAN DEFAULT FALSE,
* extnValue OCTET STRING } * extnValue OCTET STRING }
*/ */
mbedtls_x509_buf extn_oid = {0, 0, NULL};
int is_critical = 0; /* DEFAULT FALSE */
int ext_type = 0;
if( ( ret = mbedtls_asn1_get_tag( p, end, &len, end = p + ext_len;
MBEDTLS_ASN1_CONSTRUCTED | MBEDTLS_ASN1_SEQUENCE ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret );
end_ext_data = *p + len;
/* Get extension ID */ /* Get extension ID */
if( ( ret = mbedtls_asn1_get_tag( p, end_ext_data, &extn_oid.len, if( ( ret = mbedtls_asn1_get_tag( &p, end, &extn_oid.len,
MBEDTLS_ASN1_OID ) ) != 0 ) MBEDTLS_ASN1_OID ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); goto err;
extn_oid.tag = MBEDTLS_ASN1_OID; extn_oid.tag = MBEDTLS_ASN1_OID;
extn_oid.p = *p; extn_oid.p = p;
*p += extn_oid.len; p += extn_oid.len;
/* Get optional critical */ /* Get optional critical */
if( ( ret = mbedtls_asn1_get_bool( p, end_ext_data, &is_critical ) ) != 0 && if( ( ret = mbedtls_asn1_get_bool( &p, end, &is_critical ) ) != 0 &&
( ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) ) ( ret != MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ) )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); goto err;
/* Data should be octet string type */ /* Data should be octet string type */
if( ( ret = mbedtls_asn1_get_tag( p, end_ext_data, &len, if( ( ret = mbedtls_asn1_get_tag( &p, end, &len,
MBEDTLS_ASN1_OCTET_STRING ) ) != 0 ) MBEDTLS_ASN1_OCTET_STRING ) ) != 0 )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret ); goto err;
end_ext_octet = *p + len; end_ext_octet = p + len;
if( end_ext_octet != end )
if( end_ext_octet != end_ext_data ) {
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret = MBEDTLS_ERR_ASN1_LENGTH_MISMATCH;
MBEDTLS_ERR_ASN1_LENGTH_MISMATCH ); goto err;
}
/* /*
* Detect supported extensions * Detect supported extensions
*/ */
ret = mbedtls_oid_get_x509_ext_type( &extn_oid, &ext_type ); ret = mbedtls_oid_get_x509_ext_type( &extn_oid, &ext_type );
if( ret != 0 ) if( ret != 0 )
{ {
/* No parser found, skip extension */
*p = end_ext_octet;
#if !defined(MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION) #if !defined(MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION)
if( is_critical ) if( is_critical )
{ {
/* Data is marked as critical: fail */ /* Data is marked as critical: fail */
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS + ret = MBEDTLS_ERR_ASN1_UNEXPECTED_TAG;
MBEDTLS_ERR_ASN1_UNEXPECTED_TAG ); goto err;
} }
#endif #endif
continue; return( 0 );
} }
/* Forbid repeated extensions */ /* Forbid repeated extensions */
@ -647,61 +619,117 @@ static int x509_get_crt_ext( unsigned char **p,
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS ); return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS );
crt->ext_types |= ext_type; crt->ext_types |= ext_type;
switch( ext_type ) switch( ext_type )
{ {
case MBEDTLS_X509_EXT_BASIC_CONSTRAINTS: case MBEDTLS_X509_EXT_BASIC_CONSTRAINTS:
{
int ca_istrue;
int max_pathlen;
/* Parse basic constraints */ /* Parse basic constraints */
if( ( ret = x509_get_basic_constraints( p, end_ext_octet, ret = x509_get_basic_constraints( &p, end_ext_octet,
&crt->ca_istrue, &crt->max_pathlen ) ) != 0 ) &ca_istrue,
return( ret ); &max_pathlen );
if( ret != 0 )
goto err;
crt->ca_istrue = ca_istrue;
crt->max_pathlen = max_pathlen;
break; break;
}
case MBEDTLS_X509_EXT_KEY_USAGE: case MBEDTLS_X509_EXT_KEY_USAGE:
/* Parse key usage */ /* Parse key usage */
if( ( ret = x509_get_key_usage( p, end_ext_octet, ret = x509_get_key_usage( &p, end_ext_octet,
&crt->key_usage ) ) != 0 ) &crt->key_usage );
return( ret ); if( ret != 0 )
break; goto err;
case MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE:
/* Parse extended key usage */
crt->ext_key_usage_raw.p = *p;
crt->ext_key_usage_raw.len = end_ext_octet - *p;
if( ( ret = x509_get_ext_key_usage( p, end_ext_octet,
&crt->ext_key_usage ) ) != 0 )
return( ret );
break; break;
case MBEDTLS_X509_EXT_SUBJECT_ALT_NAME: case MBEDTLS_X509_EXT_SUBJECT_ALT_NAME:
/* Parse subject alt name */ /* Copy reference to raw subject alt name data. */
crt->subject_alt_raw.p = *p; crt->subject_alt_raw.p = p;
crt->subject_alt_raw.len = end_ext_octet - *p; crt->subject_alt_raw.len = end_ext_octet - p;
if( ( ret = x509_get_subject_alt_name( *p, end_ext_octet, if( ( ret = x509_get_subject_alt_name( p, end_ext_octet,
&crt->subject_alt_names ) ) != 0 ) &crt->subject_alt_names ) ) != 0 )
{ {
return( ret ); return( ret );
} }
*p = end_ext_octet; break;
case MBEDTLS_X509_EXT_EXTENDED_KEY_USAGE:
/* Parse extended key usage */
crt->ext_key_usage_raw.p = p;
crt->ext_key_usage_raw.len = end_ext_octet - p;
if( ( ret = x509_get_ext_key_usage( &p, end_ext_octet,
&crt->ext_key_usage ) ) != 0 )
{
return( ret );
}
break; break;
case MBEDTLS_X509_EXT_NS_CERT_TYPE: case MBEDTLS_X509_EXT_NS_CERT_TYPE:
/* Parse netscape certificate type */ /* Parse netscape certificate type */
if( ( ret = x509_get_ns_cert_type( p, end_ext_octet, ret = x509_get_ns_cert_type( &p, end_ext_octet,
&crt->ns_cert_type ) ) != 0 ) &crt->ns_cert_type );
return( ret ); if( ret != 0 )
goto err;
break; break;
default: default:
/*
* If this is a non-critical extension, which the oid layer
* supports, but there isn't an X.509 parser for it,
* skip the extension.
*/
#if !defined(MBEDTLS_X509_ALLOW_UNSUPPORTED_CRITICAL_EXTENSION)
if( is_critical )
return( MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE ); return( MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE );
#endif
p = end_ext_octet;
} }
}
if( *p != end )
return( MBEDTLS_ERR_X509_INVALID_EXTENSIONS +
MBEDTLS_ERR_ASN1_LENGTH_MISMATCH );
return( 0 ); return( 0 );
err:
return( ret );
}
static int x509_get_crt_ext( unsigned char **p,
unsigned char *end,
mbedtls_x509_crt *crt )
{
int ret;
size_t len;
if( *p == end )
return( 0 );
ret = mbedtls_asn1_get_tag( p, end, &len,
MBEDTLS_ASN1_CONTEXT_SPECIFIC | MBEDTLS_ASN1_CONSTRUCTED | 3 );
if( ret != 0 )
{
goto err;
}
end = *p + len;
ret = mbedtls_asn1_traverse_sequence_of( p, end,
0xFF, MBEDTLS_ASN1_SEQUENCE | MBEDTLS_ASN1_CONSTRUCTED,
0, 0, x509_crt_get_ext_cb, crt );
if( ret != 0 )
goto err;
err:
if( ret == MBEDTLS_ERR_X509_FEATURE_UNAVAILABLE )
return( ret );
if( ret == MBEDTLS_ERR_X509_INVALID_EXTENSIONS )
return( ret );
if( ret != 0 )
ret += MBEDTLS_ERR_X509_INVALID_EXTENSIONS;
return( ret );
} }
/* /*