From a2c7c7e40a6355c05e40b5560bf76829a67d02d2 Mon Sep 17 00:00:00 2001 From: cameronrich Date: Wed, 28 Dec 2016 19:43:52 +0000 Subject: [PATCH] * Basic constraint functionality added. git-svn-id: svn://svn.code.sf.net/p/axtls/code/trunk@273 9a5d90b5-6617-0410-8a86-bb477d3ed2e3 --- crypto/os_int.h | 5 ++ ssl/asn1.c | 174 +++++++++++++++++++++++++++++++------- ssl/crypto_misc.h | 45 ++++++++-- ssl/p12.c | 6 +- ssl/tls1.c | 5 +- ssl/tls1_svr.c | 6 +- ssl/x509.c | 210 ++++++++++++++++++++++++++++++++++------------ 7 files changed, 356 insertions(+), 95 deletions(-) diff --git a/crypto/os_int.h b/crypto/os_int.h index a849e5b1a..02e7d2d54 100644 --- a/crypto/os_int.h +++ b/crypto/os_int.h @@ -41,6 +41,11 @@ extern "C" { #endif +/* some bool types - just make life easier */ +typedef char bool; +#define false 0 +#define true 1 + #if defined(WIN32) typedef UINT8 uint8_t; typedef INT8 int8_t; diff --git a/ssl/asn1.c b/ssl/asn1.c index d2bd88980..058e31506 100644 --- a/ssl/asn1.c +++ b/ssl/asn1.c @@ -80,6 +80,16 @@ static const uint8_t sig_subject_alt_name[] = 0x55, 0x1d, 0x11 }; +static const uint8_t sig_basic_constraints[] = +{ + 0x55, 0x1d, 0x13 +}; + +static const uint8_t sig_key_usage[] = +{ + 0x55, 0x1d, 0x0f +}; + /* CN, O, OU, L, C, ST */ static const uint8_t g_dn_types[] = { 3, 10, 11, 7, 6, 8 }; @@ -117,6 +127,7 @@ int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type) { if (buf[*offset] != obj_type) return X509_NOT_OK; + (*offset)++; return get_asn1_length(buf, offset); } @@ -141,12 +152,12 @@ int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type) * Read an integer value for ASN.1 data * Note: This function allocates memory which must be freed by the user. */ -int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object) +int asn1_get_big_int(const uint8_t *buf, int *offset, uint8_t **object) { int len; if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0) - goto end_int_array; + goto end_big_int; if (len > 1 && buf[*offset] == 0x00) /* ignore the negative byte */ { @@ -158,10 +169,93 @@ int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object) memcpy(*object, &buf[*offset], len); *offset += len; -end_int_array: +end_big_int: return len; } +/** + * Read an integer value for ASN.1 data + */ +int asn1_get_int(const uint8_t *buf, int *offset, int32_t *val) +{ + int res = X509_OK; + int len; + int i; + + if ((len = asn1_next_obj(buf, offset, ASN1_INTEGER)) < 0 || + len > sizeof(int32_t)) + { + res = X509_NOT_OK; + goto end_int; + } + + *val = 0; + for (i = 0; i < len; i++) + { + *val <<= 8; + *val |= buf[(*offset)++]; + } + +end_int: + return res; +} + +/** + * Read an boolean value for ASN.1 data + */ +int asn1_get_bool(const uint8_t *buf, int *offset, bool *val) +{ + int res = X509_OK; + + if (asn1_next_obj(buf, offset, ASN1_BOOLEAN) != 1) + { + res = X509_NOT_OK; + goto end_bool; + } + + /* DER demands that "If the encoding represents the boolean value TRUE, + its single contents octet shall have all eight bits set to one." + Thus only 0 and 255 are valid encoded values. */ + *val = buf[(*offset)++] == 0xFF; + +end_bool: + return res; +} + +/** + * Convert an ASN.1 bit string into a 32 bit integer + */ +int asn1_get_bit_string_as_int(const uint8_t *buf, int *offset, uint32_t *val) +{ + int res = X509_OK; + int len, i; + int ignore_bits; + + if ((len = asn1_next_obj(buf, offset, ASN1_BIT_STRING)) < 0 || len > 5) + { + res = X509_NOT_OK; + goto end_bit_string_as_int; + } + + /* number of bits left unused in the final byte of content */ + ignore_bits = buf[(*offset)++]; + len--; + *val = 0; + for (i = 0; i < len; i++) + { + *val <<= 8; + *val |= buf[(*offset)++]; + } + + for (i = 0; i < ignore_bits; i++) + { + *val >>= 1; + } + +end_bit_string_as_int: + return res; +} + /** * Get all the RSA private key specifics from an ASN.1 encoded file */ @@ -187,19 +281,19 @@ int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx) /* Use the private key to mix up the RNG if possible. */ RNG_custom_init(buf, len); - mod_len = asn1_get_int(buf, &offset, &modulus); - pub_len = asn1_get_int(buf, &offset, &pub_exp); - priv_len = asn1_get_int(buf, &offset, &priv_exp); + mod_len = asn1_get_big_int(buf, &offset, &modulus); + pub_len = asn1_get_big_int(buf, &offset, &pub_exp); + priv_len = asn1_get_big_int(buf, &offset, &priv_exp); if (mod_len <= 0 || pub_len <= 0 || priv_len <= 0) return X509_INVALID_PRIV_KEY; #ifdef CONFIG_BIGINT_CRT - p_len = asn1_get_int(buf, &offset, &p); - q_len = asn1_get_int(buf, &offset, &q); - dP_len = asn1_get_int(buf, &offset, &dP); - dQ_len = asn1_get_int(buf, &offset, &dQ); - qInv_len = asn1_get_int(buf, &offset, &qInv); + p_len = asn1_get_big_int(buf, &offset, &p); + q_len = asn1_get_big_int(buf, &offset, &q); + dP_len = asn1_get_big_int(buf, &offset, &dP); + dQ_len = asn1_get_big_int(buf, &offset, &dQ); + qInv_len = asn1_get_big_int(buf, &offset, &qInv); if (p_len <= 0 || q_len <= 0 || dP_len <= 0 || dQ_len <= 0 || qInv_len <= 0) return X509_INVALID_PRIV_KEY; @@ -295,25 +389,12 @@ static int asn1_get_utc_time(const uint8_t *buf, int *offset, time_t *t) } /** - * Get the version type of a certificate (which we don't actually care about) + * Get the version type of a certificate */ -int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) +int asn1_version(const uint8_t *cert, int *offset, int *val) { - int ret = X509_NOT_OK; - int len; - (*offset) += 2; /* get past explicit tag */ - if (cert[(*offset)++] != ASN1_INTEGER) - return X509_NOT_OK; - - len = get_asn1_length(cert, offset); - if (len == 1) - { - ret = cert[*offset]; - } - - *offset += len; - return ret; + return asn1_get_int(cert, offset, val); } /** @@ -463,8 +544,8 @@ int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx) if (asn1_next_obj(cert, offset, ASN1_SEQUENCE) < 0) goto end_pub_key; - mod_len = asn1_get_int(cert, offset, &modulus); - pub_len = asn1_get_int(cert, offset, &pub_exp); + mod_len = asn1_get_big_int(cert, offset, &modulus); + pub_len = asn1_get_big_int(cert, offset, &pub_exp); RSA_pub_key_new(&x509_ctx->rsa_ctx, modulus, mod_len, pub_exp, pub_len); @@ -584,7 +665,7 @@ int asn1_find_oid(const uint8_t* cert, int* offset, return 0; } -int asn1_find_subjectaltname(const uint8_t* cert, int offset) +int asn1_is_subject_alt_name(const uint8_t *cert, int offset) { if (asn1_find_oid(cert, &offset, sig_subject_alt_name, sizeof(sig_subject_alt_name))) @@ -595,6 +676,39 @@ int asn1_find_subjectaltname(const uint8_t* cert, int offset) return 0; } +int asn1_is_basic_constraints(const uint8_t *cert, int offset) +{ + if (asn1_find_oid(cert, &offset, sig_basic_constraints, + sizeof(sig_basic_constraints))) + { + return offset; + } + + return 0; +} + +int asn1_is_key_usage(const uint8_t *cert, int offset) +{ + if (asn1_find_oid(cert, &offset, sig_key_usage, + sizeof(sig_key_usage))) + { + return offset; + } + + return 0; +} + +bool asn1_is_critical_ext(const uint8_t *buf, int *offset) +{ + /* critical is optional */ + bool res = false; + + if (asn1_next_obj(buf, offset, ASN1_BOOLEAN) == 1) + res = buf[(*offset)++] == 0xFF; + + return res; +} + #endif /* CONFIG_SSL_CERT_VERIFICATION */ /** diff --git a/ssl/crypto_misc.h b/ssl/crypto_misc.h index 34593fdae..fb9d965a6 100644 --- a/ssl/crypto_misc.h +++ b/ssl/crypto_misc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2007-2015, Cameron Rich + * Copyright (c) 2007-2016, Cameron Rich * * All rights reserved. * @@ -56,6 +56,7 @@ extern "C" { #define X509_VFY_ERROR_UNSUPPORTED_DIGEST -8 #define X509_INVALID_PRIV_KEY -9 #define X509_MAX_CERTS -10 +#define X509_VFY_ERROR_BASIC_CONSTRAINT -11 /* * The Distinguished Name @@ -68,6 +69,21 @@ extern "C" { #define X509_COUNTRY 4 #define X509_STATE 5 +/* + * Key Usage bits + */ +#define IS_SET_KEY_USAGE_FLAG(A, B) (A->key_usage & B) + +#define KEY_USAGE_DIGITAL_SIGNATURE 0x0001 +#define KEY_USAGE_CONTENT_COMMITMENT 0x0002 +#define KEY_USAGE_KEY_ENCIPHERMENT 0x0004 +#define KEY_USAGE_DATA_ENCIPHERMENT 0x0008 +#define KEY_USAGE_KEY_AGREEMENT 0x0010 +#define KEY_USAGE_KEY_CERT_SIGN 0x0020 +#define KEY_USAGE_CRL_SIGN 0x0040 +#define KEY_USAGE_ENCIPHER_ONLY 0x0080 +#define KEY_USAGE_DECIPHER_ONLY 0x0100 + struct _x509_ctx { char *ca_cert_dn[X509_NUM_DN_TYPES]; @@ -76,12 +92,19 @@ struct _x509_ctx time_t not_before; time_t not_after; uint8_t *signature; - uint16_t sig_len; - uint8_t sig_type; RSA_CTX *rsa_ctx; bigint *digest; uint8_t *fingerprint; uint8_t *spki_sha256; + uint16_t sig_len; + uint8_t sig_type; + bool basic_constraint_present; + bool basic_constraint_is_critical; + bool key_usage_present; + bool key_usage_is_critical; + bool basic_constaint_cA; + int basic_constraint_pathLenConstraint; + uint32_t key_usage; struct _x509_ctx *next; }; @@ -97,7 +120,8 @@ typedef struct int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx); void x509_free(X509_CTX *x509_ctx); #ifdef CONFIG_SSL_CERT_VERIFICATION -int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert); +int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert, + int *pathLenConstraint); #endif #ifdef CONFIG_SSL_FULL_MODE void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx); @@ -107,6 +131,7 @@ const char * x509_display_error(int error); /************************************************************************** * ASN1 declarations **************************************************************************/ +#define ASN1_BOOLEAN 0x01 #define ASN1_INTEGER 0x02 #define ASN1_BIT_STRING 0x03 #define ASN1_OCTET_STRING 0x04 @@ -139,15 +164,21 @@ uint32_t get_asn1_length(const uint8_t *buf, int *offset); int asn1_get_private_key(const uint8_t *buf, int len, RSA_CTX **rsa_ctx); int asn1_next_obj(const uint8_t *buf, int *offset, int obj_type); int asn1_skip_obj(const uint8_t *buf, int *offset, int obj_type); -int asn1_get_int(const uint8_t *buf, int *offset, uint8_t **object); -int asn1_version(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); +int asn1_get_big_int(const uint8_t *buf, int *offset, uint8_t **object); +int asn1_get_int(const uint8_t *buf, int *offset, int32_t *val); +int asn1_get_bool(const uint8_t *buf, int *offset, bool *val); +int asn1_get_bit_string_as_int(const uint8_t *buf, int *offset, uint32_t *val); +int asn1_version(const uint8_t *cert, int *offset, int *val); int asn1_validity(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); int asn1_name(const uint8_t *cert, int *offset, char *dn[]); int asn1_public_key(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); #ifdef CONFIG_SSL_CERT_VERIFICATION int asn1_signature(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); -int asn1_find_subjectaltname(const uint8_t* cert, int offset); int asn1_compare_dn(char * const dn1[], char * const dn2[]); +int asn1_is_subject_alt_name(const uint8_t *cert, int offset); +int asn1_is_basic_constraints(const uint8_t *cert, int offset); +int asn1_is_key_usage(const uint8_t *cert, int offset); +bool asn1_is_critical_ext(const uint8_t *buf, int *offset); #endif /* CONFIG_SSL_CERT_VERIFICATION */ int asn1_signature_type(const uint8_t *cert, int *offset, X509_CTX *x509_ctx); diff --git a/ssl/p12.c b/ssl/p12.c index 2bafaf7ea..5bd239462 100644 --- a/ssl/p12.c +++ b/ssl/p12.c @@ -105,7 +105,7 @@ int pkcs8_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password) } /* unencrypted key? */ - if (asn1_get_int(buf, &offset, &version) > 0 && *version == 0) + if (asn1_get_big_int(buf, &offset, &version) > 0 && *version == 0) { ret = p8_add_key(ssl_ctx, buf); goto error; @@ -257,7 +257,7 @@ int pkcs12_decode(SSL_CTX *ssl_ctx, SSLObjLoader *ssl_obj, const char *password) goto error; } - if (asn1_get_int(buf, &offset, &version) < 0 || *version != 3) + if (asn1_get_big_int(buf, &offset, &version) < 0 || *version != 3) { ret = SSL_ERROR_INVALID_VERSION; goto error; @@ -463,7 +463,7 @@ static int get_pbe_params(uint8_t *buf, int *offset, *salt = &buf[*offset]; *offset += len; - if ((len = asn1_get_int(buf, offset, &iter)) < 0) + if ((len = asn1_get_big_int(buf, offset, &iter)) < 0) goto error; *iterations = 0; diff --git a/ssl/tls1.c b/ssl/tls1.c index 5257cbda6..fcc0bbaad 100644 --- a/ssl/tls1.c +++ b/ssl/tls1.c @@ -2090,8 +2090,11 @@ error: EXP_FUNC int STDCALL ssl_verify_cert(const SSL *ssl) { int ret; + int pathLenConstraint = 0; + SSL_CTX_LOCK(ssl->ssl_ctx->mutex); - ret = x509_verify(ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx); + ret = x509_verify(ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx, + &pathLenConstraint); SSL_CTX_UNLOCK(ssl->ssl_ctx->mutex); if (ret) /* modify into an SSL error type */ diff --git a/ssl/tls1_svr.c b/ssl/tls1_svr.c index 163abbd41..abc044552 100644 --- a/ssl/tls1_svr.c +++ b/ssl/tls1_svr.c @@ -92,8 +92,10 @@ int do_svr_handshake(SSL *ssl, int handshake_type, uint8_t *buf, int hs_len) if (ret == SSL_OK) /* verify the cert */ { int cert_res; - cert_res = x509_verify( - ssl->ssl_ctx->ca_cert_ctx, ssl->x509_ctx); + int pathLenConstraint = 0; + + cert_res = x509_verify(ssl->ssl_ctx->ca_cert_ctx, + ssl->x509_ctx, &pathLenConstraint); ret = (cert_res == 0) ? SSL_OK : SSL_X509_ERROR(cert_res); } break; diff --git a/ssl/x509.c b/ssl/x509.c index 3d1541461..7375ae37f 100644 --- a/ssl/x509.c +++ b/ssl/x509.c @@ -42,6 +42,13 @@ #include "crypto_misc.h" #ifdef CONFIG_SSL_CERT_VERIFICATION +static int x509_v3_subject_alt_name(const uint8_t *cert, int offset, + X509_CTX *x509_ctx); +static int x509_v3_basic_constraints(const uint8_t *cert, int offset, + X509_CTX *x509_ctx); +static int x509_v3_key_usage(const uint8_t *cert, int offset, + X509_CTX *x509_ctx); + /** * Retrieve the signature from a certificate. */ @@ -95,11 +102,10 @@ int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx) if (asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) goto end_cert; - if (cert[offset] == ASN1_EXPLICIT_TAG) /* optional version */ - { - if ((version = asn1_version(cert, &offset, x509_ctx)) == X509_NOT_OK) - goto end_cert; - } + /* optional version */ + if (cert[offset] == ASN1_EXPLICIT_TAG && + asn1_version(cert, &offset, &version) == X509_NOT_OK) + goto end_cert; if (asn1_skip_obj(cert, &offset, ASN1_INTEGER) || /* serial number */ asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0) @@ -197,50 +203,11 @@ int x509_new(const uint8_t *cert, int *len, X509_CTX **ctx) break; } - if (version == 2 && cert[offset] == ASN1_V3_DATA) + if (version == 2 && asn1_next_obj(cert, &offset, ASN1_V3_DATA) > 0) { - int suboffset; - - ++offset; - get_asn1_length(cert, &offset); - - if ((suboffset = asn1_find_subjectaltname(cert, offset)) > 0) - { - if (asn1_next_obj(cert, &suboffset, ASN1_OCTET_STRING) > 0) - { - int altlen; - - if ((altlen = asn1_next_obj(cert, - &suboffset, ASN1_SEQUENCE)) > 0) - { - int endalt = suboffset + altlen; - int totalnames = 0; - - while (suboffset < endalt) - { - int type = cert[suboffset++]; - int dnslen = get_asn1_length(cert, &suboffset); - - if (type == ASN1_CONTEXT_DNSNAME) - { - x509_ctx->subject_alt_dnsnames = (char**) - realloc(x509_ctx->subject_alt_dnsnames, - (totalnames + 2) * sizeof(char*)); - x509_ctx->subject_alt_dnsnames[totalnames] = - (char*)malloc(dnslen + 1); - x509_ctx->subject_alt_dnsnames[totalnames+1] = NULL; - memcpy(x509_ctx->subject_alt_dnsnames[totalnames], - cert + suboffset, dnslen); - x509_ctx->subject_alt_dnsnames[ - totalnames][dnslen] = 0; - ++totalnames; - } - - suboffset += dnslen; - } - } - } - } + x509_v3_subject_alt_name(cert, offset, x509_ctx); + x509_v3_basic_constraints(cert, offset, x509_ctx); + x509_v3_key_usage(cert, offset, x509_ctx); } offset = end_tbs; /* skip the rest of v3 data */ @@ -268,6 +235,106 @@ end_cert: return ret; } +#ifdef CONFIG_SSL_CERT_VERIFICATION /* only care if doing verification */ +static int x509_v3_subject_alt_name(const uint8_t *cert, int offset, + X509_CTX *x509_ctx) +{ + if ((offset = asn1_is_subject_alt_name(cert, offset)) > 0) + { + /* ignore if present */ + asn1_is_critical_ext(cert, &offset); + + if (asn1_next_obj(cert, &offset, ASN1_OCTET_STRING) > 0) + { + int altlen; + + if ((altlen = asn1_next_obj(cert, &offset, ASN1_SEQUENCE)) > 0) + { + int endalt = offset + altlen; + int totalnames = 0; + + while (offset < endalt) + { + int type = cert[offset++]; + int dnslen = get_asn1_length(cert, &offset); + + if (type == ASN1_CONTEXT_DNSNAME) + { + x509_ctx->subject_alt_dnsnames = (char**) + realloc(x509_ctx->subject_alt_dnsnames, + (totalnames + 2) * sizeof(char*)); + x509_ctx->subject_alt_dnsnames[totalnames] = + (char*)malloc(dnslen + 1); + x509_ctx->subject_alt_dnsnames[totalnames+1] = NULL; + memcpy(x509_ctx->subject_alt_dnsnames[totalnames], + cert + offset, dnslen); + x509_ctx->subject_alt_dnsnames[ + totalnames][dnslen] = 0; + ++totalnames; + } + + offset += dnslen; + } + } + } + } + + return X509_OK; +} + +/** + * Basic constraints - see https://tools.ietf.org/html/rfc5280#page-39 + */ +static int x509_v3_basic_constraints(const uint8_t *cert, int offset, + X509_CTX *x509_ctx) +{ + int ret = X509_OK; + + if ((offset = asn1_is_basic_constraints(cert, offset)) == 0) + goto end_contraints; + + x509_ctx->basic_constraint_present = true; + x509_ctx->basic_constraint_is_critical = + asn1_is_critical_ext(cert, &offset); + + if (asn1_next_obj(cert, &offset, ASN1_OCTET_STRING) < 0 || + asn1_next_obj(cert, &offset, ASN1_SEQUENCE) < 0 || + asn1_get_bool(cert, &offset, &x509_ctx->basic_constaint_cA) < 0 || + asn1_get_int(cert, &offset, + &x509_ctx->basic_constraint_pathLenConstraint) < 0) + { + ret = X509_NOT_OK; + } + +end_contraints: + return ret; +} + +/* + * Key usage - see https://tools.ietf.org/html/rfc5280#section-4.2.1.3 + */ +static int x509_v3_key_usage(const uint8_t *cert, int offset, + X509_CTX *x509_ctx) +{ + int ret = X509_OK; + + if ((offset = asn1_is_key_usage(cert, offset)) == 0) + goto end_key_usage; + + x509_ctx->key_usage_present = true; + x509_ctx->key_usage_is_critical = asn1_is_critical_ext(cert, &offset); + + if (asn1_next_obj(cert, &offset, ASN1_OCTET_STRING) < 0 || + asn1_get_bit_string_as_int(cert, &offset, &x509_ctx->key_usage)) + { + ret = X509_NOT_OK; + } + +end_key_usage: + return ret; +} +#endif + /** * Free an X.509 object's resources. */ @@ -371,8 +438,10 @@ static bigint *sig_verify(BI_CTX *ctx, const uint8_t *sig, int sig_len, * - That the certificate(s) are not self-signed. * - The certificate chain is valid. * - The signature of the certificate is valid. + * - Basic constraints */ -int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) +int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert, + int *pathLenConstraint) { int ret = X509_OK, i = 0; bigint *cert_sig; @@ -415,6 +484,13 @@ int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) goto end_verify; } + if (cert->basic_constaint_cA && + IS_SET_KEY_USAGE_FLAG(cert, KEY_USAGE_KEY_CERT_SIGN)) + { + ret = X509_VFY_ERROR_BASIC_CONSTRAINT; + goto end_verify; + } + next_cert = cert->next; /* last cert in the chain - look for a trusted cert */ @@ -425,14 +501,29 @@ int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) /* go thu the CA store */ while (i < CONFIG_X509_MAX_CA_CERTS && ca_cert_ctx->cert[i]) { + /* ignore CA certs that are not really CA certs */ + if (cert->basic_constraint_present && + !ca_cert_ctx->cert[i]->basic_constaint_cA) + continue; + if (asn1_compare_dn(cert->ca_cert_dn, ca_cert_ctx->cert[i]->cert_dn) == 0) { /* use this CA certificate for signature verification */ - match_ca_cert = 1; + match_ca_cert = true; ctx = ca_cert_ctx->cert[i]->rsa_ctx->bi_ctx; mod = ca_cert_ctx->cert[i]->rsa_ctx->m; expn = ca_cert_ctx->cert[i]->rsa_ctx->e; + + if (ca_cert_ctx->cert[i]->basic_constaint_cA && + ca_cert_ctx->cert[i]-> + basic_constraint_pathLenConstraint < + *pathLenConstraint) + { + ret = X509_VFY_ERROR_BASIC_CONSTRAINT; + goto end_verify; + } + break; } @@ -491,7 +582,8 @@ int x509_verify(const CA_CERT_CTX *ca_cert_ctx, const X509_CTX *cert) /* go down the certificate chain using recursion. */ if (next_cert != NULL) { - ret = x509_verify(ca_cert_ctx, next_cert); + ret = x509_verify(ca_cert_ctx, next_cert, pathLenConstraint); + (*pathLenConstraint)++; /* don't include last certificate */ } end_verify: @@ -603,8 +695,19 @@ void x509_print(const X509_CTX *cert, CA_CERT_CTX *ca_cert_ctx) if (ca_cert_ctx) { + int pathLenConstraint = 0; printf("Verify:\t\t\t\t%s\n", - x509_display_error(x509_verify(ca_cert_ctx, cert))); + x509_display_error(x509_verify(ca_cert_ctx, cert, + &pathLenConstraint))); + } + + if (cert->basic_constraint_present) + { + printf("Basic Constraints:\t\t%s, CA:%s, pathlen:%d\n", + cert->basic_constraint_is_critical ? + "critical" : "NOT critical", + cert->basic_constaint_cA? "TRUE" : "FALSE", + cert->basic_constraint_pathLenConstraint); } #if 0 @@ -655,6 +758,9 @@ const char * x509_display_error(int error) case X509_INVALID_PRIV_KEY: return "Invalid private key"; + case X509_VFY_ERROR_BASIC_CONSTRAINT: + return "Basic constraint invalid"; + default: return "Unknown"; }