[gnutls-devel] [PATCH V3] Check for all error conditions when verifying a certificate
Armin Burgmeier
armin at arbur.net
Tue Sep 16 20:02:24 CEST 2014
This allows to check for all possible flaws with a certificate chain with a
single call to gnutls_x509_crt_list_verify and friends.
Signed-off-by: Armin Burgmeier <armin at arbur.net>
---
lib/x509/verify.c | 463 +++++++++++++++++++++++++---------------------------
tests/test-chains.h | 13 +-
2 files changed, 232 insertions(+), 244 deletions(-)
diff --git a/lib/x509/verify.c b/lib/x509/verify.c
index e71832d..53de776 100644
--- a/lib/x509/verify.c
+++ b/lib/x509/verify.c
@@ -423,7 +423,7 @@ int is_broken_allowed(gnutls_sign_algorithm_t sig, unsigned int flags)
* according to verification profile specified.
*
* @crt: a certificate
- * @issuer: the certificates issuer
+ * @issuer: the certificates issuer (allowed to be NULL)
* @sigalg: the signature algorithm used
* @flags: the specified verification flags
*/
@@ -469,11 +469,6 @@ int hash;
return gnutls_assert_val(0);
}
- if (gnutls_x509_crt_get_version(issuer) != 3) {
- _gnutls_debug_log("SUITEB: certificate's issuer uses an unacceptable version number\n");
- return gnutls_assert_val(0);
- }
-
if (sigalg != GNUTLS_SIGN_ECDSA_SHA256 && sigalg != GNUTLS_SIGN_ECDSA_SHA384) {
_gnutls_debug_log("SUITEB: certificate is not signed using ECDSA-SHA256 or ECDSA-SHA384\n");
return gnutls_assert_val(0);
@@ -498,38 +493,12 @@ int hash;
curve = params.flags;
gnutls_pk_params_release(¶ms);
- ret = _gnutls_x509_crt_get_mpis(issuer, ¶ms);
- if (ret < 0) {
- _gnutls_debug_log("SUITEB: cannot read certificate params\n");
- return gnutls_assert_val(0);
- }
-
- issuer_curve = params.flags;
- gnutls_pk_params_release(¶ms);
-
- if (issuer_curve != GNUTLS_ECC_CURVE_SECP256R1 &&
- issuer_curve != GNUTLS_ECC_CURVE_SECP384R1) {
- _gnutls_debug_log("SUITEB: certificate's issuer ECC params do not contain SECP256R1 or SECP384R1\n");
- return gnutls_assert_val(0);
- }
-
if (curve != GNUTLS_ECC_CURVE_SECP256R1 &&
curve != GNUTLS_ECC_CURVE_SECP384R1) {
_gnutls_debug_log("SUITEB: certificate's ECC params do not contain SECP256R1 or SECP384R1\n");
return gnutls_assert_val(0);
}
- if (issuer_curve < curve) {
- _gnutls_debug_log("SUITEB: certificate's issuer ECC params are weaker than the certificate's\n");
- return gnutls_assert_val(0);
- }
-
- if (sigalg == GNUTLS_SIGN_ECDSA_SHA256 &&
- issuer_curve == GNUTLS_ECC_CURVE_SECP384R1) {
- _gnutls_debug_log("SUITEB: certificate is signed with ECDSA-SHA256 when using SECP384R1\n");
- return gnutls_assert_val(0);
- }
-
if (profile == GNUTLS_PROFILE_SUITEB192) {
if (curve != GNUTLS_ECC_CURVE_SECP384R1) {
_gnutls_debug_log("SUITEB192: certificate does not use SECP384R1\n");
@@ -537,6 +506,39 @@ int hash;
}
}
+ if (issuer != NULL) {
+ if (gnutls_x509_crt_get_version(issuer) != 3) {
+ _gnutls_debug_log("SUITEB: certificate's issuer uses an unacceptable version number\n");
+ return gnutls_assert_val(0);
+ }
+
+ ret = _gnutls_x509_crt_get_mpis(issuer, ¶ms);
+ if (ret < 0) {
+ _gnutls_debug_log("SUITEB: cannot read certificate params\n");
+ return gnutls_assert_val(0);
+ }
+
+ issuer_curve = params.flags;
+ gnutls_pk_params_release(¶ms);
+
+ if (issuer_curve != GNUTLS_ECC_CURVE_SECP256R1 &&
+ issuer_curve != GNUTLS_ECC_CURVE_SECP384R1) {
+ _gnutls_debug_log("SUITEB: certificate's issuer ECC params do not contain SECP256R1 or SECP384R1\n");
+ return gnutls_assert_val(0);
+ }
+
+ if (issuer_curve < curve) {
+ _gnutls_debug_log("SUITEB: certificate's issuer ECC params are weaker than the certificate's\n");
+ return gnutls_assert_val(0);
+ }
+
+ if (sigalg == GNUTLS_SIGN_ECDSA_SHA256 &&
+ issuer_curve == GNUTLS_ECC_CURVE_SECP384R1) {
+ _gnutls_debug_log("SUITEB: certificate is signed with ECDSA-SHA256 when using SECP384R1\n");
+ return gnutls_assert_val(0);
+ }
+ }
+
break;
}
}
@@ -572,7 +574,7 @@ verify_crt(gnutls_x509_crt_t cert,
gnutls_datum_t cert_signature = { NULL, 0 };
gnutls_x509_crt_t issuer = NULL;
int issuer_version, hash_algo;
- bool result = 0;
+ bool result = 1;
const mac_entry_st * me;
unsigned int out = 0, usage;
int sigalg, ret;
@@ -581,13 +583,12 @@ verify_crt(gnutls_x509_crt_t cert,
*output = 0;
if (*max_path == 0) {
- out =
+ out |=
GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE |
GNUTLS_CERT_INVALID;
- if (output)
- *output |= out;
gnutls_assert();
result = 0;
+ /* bail immediately, to avoid inconistency */
goto cleanup;
}
(*max_path)--;
@@ -595,115 +596,16 @@ verify_crt(gnutls_x509_crt_t cert,
if (tcas_size >= 1)
issuer = find_issuer(cert, trusted_cas, tcas_size);
- /* issuer is not in trusted certificate
- * authorities.
- */
- if (issuer == NULL) {
- out = GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID;
- if (output)
- *output |= out;
- gnutls_assert();
- result = 0;
- goto cleanup;
- }
-
if (_issuer != NULL)
*_issuer = issuer;
- if (nc != NULL) {
- /* append the issuer's constraints */
- ret = gnutls_x509_crt_get_name_constraints(issuer, nc,
- GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, NULL);
- if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
- nc_fail:
- out =
- GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE |
- GNUTLS_CERT_INVALID;
- if (output)
- *output |= out;
- gnutls_assert();
- result = 0;
- goto cleanup;
- }
-
- /* only check name constraints in server certificates, not CAs */
- if (end_cert != 0) {
- ret = gnutls_x509_name_constraints_check_crt(nc, GNUTLS_SAN_DNSNAME, cert);
- if (ret == 0) {
- gnutls_assert();
- goto nc_fail;
- }
-
- ret = gnutls_x509_name_constraints_check_crt(nc, GNUTLS_SAN_RFC822NAME, cert);
- if (ret == 0) {
- gnutls_assert();
- goto nc_fail;
- }
-
- ret = gnutls_x509_name_constraints_check_crt(nc, GNUTLS_SAN_DN, cert);
- if (ret == 0) {
- gnutls_assert();
- goto nc_fail;
- }
-
- ret = gnutls_x509_name_constraints_check_crt(nc, GNUTLS_SAN_URI, cert);
- if (ret == 0) {
- gnutls_assert();
- goto nc_fail;
- }
-
- ret = gnutls_x509_name_constraints_check_crt(nc, GNUTLS_SAN_IPADDRESS, cert);
- if (ret == 0) {
- gnutls_assert();
- goto nc_fail;
- }
- }
- }
-
- issuer_version = gnutls_x509_crt_get_version(issuer);
- if (issuer_version < 0) {
- gnutls_assert();
- result = 0;
- goto cleanup;
- }
-
- if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN) &&
- ((flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT)
- || issuer_version != 1)) {
- if (check_if_ca(cert, issuer, max_path, flags) != 1) {
- gnutls_assert();
- out =
- GNUTLS_CERT_SIGNER_NOT_CA |
- GNUTLS_CERT_INVALID;
- if (output)
- *output |= out;
- result = 0;
- goto cleanup;
- }
-
- ret =
- gnutls_x509_crt_get_key_usage(issuer, &usage, NULL);
- if (ret >= 0) {
- if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) {
- gnutls_assert();
- out =
- GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE
- | GNUTLS_CERT_INVALID;
- if (output)
- *output |= out;
- result = 0;
- goto cleanup;
- }
- }
- }
-
ret =
_gnutls_x509_get_signed_data(cert->cert, &cert->der, "tbsCertificate",
&cert_signed_data);
if (ret < 0) {
result = 0;
gnutls_assert();
- goto cleanup;
+ cert_signed_data.data = NULL;
}
ret =
@@ -712,7 +614,7 @@ verify_crt(gnutls_x509_crt_t cert,
if (ret < 0) {
result = 0;
gnutls_assert();
- goto cleanup;
+ cert_signature.data = NULL;
}
ret =
@@ -721,75 +623,160 @@ verify_crt(gnutls_x509_crt_t cert,
if (ret < 0) {
result = 0;
gnutls_assert();
- goto cleanup;
}
sigalg = ret;
- if (is_level_acceptable(cert, issuer, sigalg, flags) == 0) {
- gnutls_assert();
- out =
- GNUTLS_CERT_INSECURE_ALGORITHM |
- GNUTLS_CERT_INVALID;
- if (output)
- *output |= out;
- result = 0;
- goto cleanup;
- }
-
- /* If the certificate is not self signed check if the algorithms
- * used are secure. If the certificate is self signed it doesn't
- * really matter.
+ /* issuer is not in trusted certificate
+ * authorities.
*/
- if (gnutls_sign_is_secure(sigalg) == 0 &&
- is_broken_allowed(sigalg, flags) == 0 &&
- is_issuer(cert, cert) == 0) {
+ if (issuer == NULL) {
+ out |= GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID;
gnutls_assert();
- out =
- GNUTLS_CERT_INSECURE_ALGORITHM |
- GNUTLS_CERT_INVALID;
- if (output)
- *output |= out;
result = 0;
- goto cleanup;
- }
+ } else {
+ if (nc != NULL) {
+ /* append the issuer's constraints */
+ ret = gnutls_x509_crt_get_name_constraints(issuer, nc,
+ GNUTLS_NAME_CONSTRAINTS_FLAG_APPEND, NULL);
+ if (ret < 0 && ret != GNUTLS_E_REQUESTED_DATA_NOT_AVAILABLE) {
+ nc_fail:
+ out |=
+ GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE |
+ GNUTLS_CERT_INVALID;
+ gnutls_assert();
+ result = 0;
+ goto nc_done;
+ }
- hash_algo = gnutls_sign_get_hash_algorithm(sigalg);
- me = mac_to_entry(hash_algo);
- if (me == NULL) {
- gnutls_assert();
- result = 0;
- goto cleanup;
+ /* only check name constraints in server certificates, not CAs */
+ if (end_cert != 0) {
+ ret = gnutls_x509_name_constraints_check_crt(nc, GNUTLS_SAN_DNSNAME, cert);
+ if (ret == 0) {
+ gnutls_assert();
+ goto nc_fail;
+ }
+
+ ret = gnutls_x509_name_constraints_check_crt(nc, GNUTLS_SAN_RFC822NAME, cert);
+ if (ret == 0) {
+ gnutls_assert();
+ goto nc_fail;
+ }
+
+ ret = gnutls_x509_name_constraints_check_crt(nc, GNUTLS_SAN_DN, cert);
+ if (ret == 0) {
+ gnutls_assert();
+ goto nc_fail;
+ }
+
+ ret = gnutls_x509_name_constraints_check_crt(nc, GNUTLS_SAN_URI, cert);
+ if (ret == 0) {
+ gnutls_assert();
+ goto nc_fail;
+ }
+
+ ret = gnutls_x509_name_constraints_check_crt(nc, GNUTLS_SAN_IPADDRESS, cert);
+ if (ret == 0) {
+ gnutls_assert();
+ goto nc_fail;
+ }
+ }
+ }
+ nc_done:
+
+ issuer_version = gnutls_x509_crt_get_version(issuer);
+
+ if (issuer_version < 0) {
+ gnutls_assert();
+ result = 0;
+ } else if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN) &&
+ ((flags & GNUTLS_VERIFY_DO_NOT_ALLOW_X509_V1_CA_CRT)
+ || issuer_version != 1)) {
+ if (check_if_ca(cert, issuer, max_path, flags) != 1) {
+ gnutls_assert();
+ out |=
+ GNUTLS_CERT_SIGNER_NOT_CA |
+ GNUTLS_CERT_INVALID;
+ result = 0;
+ }
+
+ ret =
+ gnutls_x509_crt_get_key_usage(issuer, &usage, NULL);
+ if (ret >= 0) {
+ if (!(usage & GNUTLS_KEY_KEY_CERT_SIGN)) {
+ gnutls_assert();
+ out |=
+ GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE
+ | GNUTLS_CERT_INVALID;
+ result = 0;
+ }
+ }
+ }
+
+ if (sigalg >= 0) {
+ hash_algo = gnutls_sign_get_hash_algorithm(sigalg);
+ me = mac_to_entry(hash_algo);
+ } else {
+ me = NULL;
+ }
+
+ if (me == NULL) {
+ gnutls_assert();
+ result = 0;
+ } else if (cert_signed_data.data != NULL &&
+ cert_signature.data != NULL) {
+ ret =
+ _gnutls_x509_verify_data(me,
+ &cert_signed_data,
+ &cert_signature,
+ issuer);
+ if (ret == GNUTLS_E_PK_SIG_VERIFY_FAILED) {
+ gnutls_assert();
+ out |=
+ GNUTLS_CERT_INVALID |
+ GNUTLS_CERT_SIGNATURE_FAILURE;
+ /* error. ignore it */
+ result = 0;
+ } else if (ret < 0) {
+ result = 0;
+ gnutls_assert();
+ }
+ }
}
- ret =
- _gnutls_x509_verify_data(me,
- &cert_signed_data, &cert_signature,
- issuer);
- if (ret == GNUTLS_E_PK_SIG_VERIFY_FAILED) {
- gnutls_assert();
- out |= GNUTLS_CERT_INVALID | GNUTLS_CERT_SIGNATURE_FAILURE;
- /* error. ignore it */
- if (output)
- *output |= out;
- result = 0;
- } else if (ret < 0) {
- result = 0;
- gnutls_assert();
- goto cleanup;
- } else if (ret == 1)
- result = 1;
+ if (sigalg >= 0) {
+ if (is_level_acceptable(cert, issuer, sigalg, flags) == 0) {
+ gnutls_assert();
+ out |=
+ GNUTLS_CERT_INSECURE_ALGORITHM |
+ GNUTLS_CERT_INVALID;
+ result = 0;
+ }
+
+ /* If the certificate is not self signed check if the algorithms
+ * used are secure. If the certificate is self signed it doesn't
+ * really matter.
+ */
+ if (gnutls_sign_is_secure(sigalg) == 0 &&
+ is_broken_allowed(sigalg, flags) == 0 &&
+ is_issuer(cert, cert) == 0) {
+ gnutls_assert();
+ out |=
+ GNUTLS_CERT_INSECURE_ALGORITHM |
+ GNUTLS_CERT_INVALID;
+ result = 0;
+ }
+ }
/* Check activation/expiration times
*/
if (!(flags & GNUTLS_VERIFY_DISABLE_TIME_CHECKS)) {
/* check the time of the issuer first */
- if (!(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS)) {
+ if (issuer != NULL &&
+ !(flags & GNUTLS_VERIFY_DISABLE_TRUSTED_TIME_CHECKS)) {
out |= check_time_status(issuer, now);
if (out != 0) {
gnutls_assert();
result = 0;
- if (output)
- *output |= out;
}
}
@@ -797,12 +784,13 @@ verify_crt(gnutls_x509_crt_t cert,
if (out != 0) {
gnutls_assert();
result = 0;
- if (output)
- *output |= out;
}
}
cleanup:
+ if (output)
+ *output |= out;
+
if (func) {
if (result == 0) {
out |= GNUTLS_CERT_INVALID;
@@ -1407,42 +1395,6 @@ gnutls_x509_crl_verify(gnutls_x509_crl_t crl,
if (tcas_size >= 1)
issuer = find_crl_issuer(crl, trusted_cas, tcas_size);
- /* issuer is not in trusted certificate
- * authorities.
- */
- if (issuer == NULL) {
- gnutls_assert();
- if (verify)
- *verify |=
- GNUTLS_CERT_SIGNER_NOT_FOUND |
- GNUTLS_CERT_INVALID;
- return 0;
- }
-
- if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN)) {
- if (gnutls_x509_crt_get_ca_status(issuer, NULL) != 1) {
- gnutls_assert();
- if (verify)
- *verify |=
- GNUTLS_CERT_SIGNER_NOT_CA |
- GNUTLS_CERT_INVALID;
- return 0;
- }
-
- result =
- gnutls_x509_crt_get_key_usage(issuer, &usage, NULL);
- if (result >= 0) {
- if (!(usage & GNUTLS_KEY_CRL_SIGN)) {
- gnutls_assert();
- if (verify)
- *verify |=
- GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE
- | GNUTLS_CERT_INVALID;
- return 0;
- }
- }
- }
-
result =
_gnutls_x509_get_signed_data(crl->crl, &crl->der, "tbsCertList",
&crl_signed_data);
@@ -1475,21 +1427,54 @@ gnutls_x509_crl_verify(gnutls_x509_crl_t crl,
hash_algo = gnutls_sign_get_hash_algorithm(result);
- result =
- _gnutls_x509_verify_data(mac_to_entry(hash_algo),
- &crl_signed_data, &crl_signature,
- issuer);
- if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) {
- gnutls_assert();
- /* error. ignore it */
- if (verify)
- *verify |= GNUTLS_CERT_SIGNATURE_FAILURE;
- result = 0;
- } else if (result < 0) {
+ /* issuer is not in trusted certificate
+ * authorities.
+ */
+ if (issuer == NULL) {
gnutls_assert();
if (verify)
- *verify |= GNUTLS_CERT_INVALID;
- goto cleanup;
+ *verify |=
+ GNUTLS_CERT_SIGNER_NOT_FOUND |
+ GNUTLS_CERT_INVALID;
+ } else {
+ if (!(flags & GNUTLS_VERIFY_DISABLE_CA_SIGN)) {
+ if (gnutls_x509_crt_get_ca_status(issuer, NULL) != 1) {
+ gnutls_assert();
+ if (verify)
+ *verify |=
+ GNUTLS_CERT_SIGNER_NOT_CA |
+ GNUTLS_CERT_INVALID;
+ }
+
+ result =
+ gnutls_x509_crt_get_key_usage(issuer, &usage, NULL);
+ if (result >= 0) {
+ if (!(usage & GNUTLS_KEY_CRL_SIGN)) {
+ gnutls_assert();
+ if (verify)
+ *verify |=
+ GNUTLS_CERT_SIGNER_CONSTRAINTS_FAILURE
+ | GNUTLS_CERT_INVALID;
+ }
+ }
+ }
+
+ result =
+ _gnutls_x509_verify_data(mac_to_entry(hash_algo),
+ &crl_signed_data, &crl_signature,
+ issuer);
+ if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) {
+ gnutls_assert();
+ /* error. ignore it */
+ if (verify)
+ *verify |= GNUTLS_CERT_SIGNATURE_FAILURE;
+ result = 0;
+ } else if (result < 0) {
+ gnutls_assert();
+ if (verify)
+ *verify |= GNUTLS_CERT_INVALID;
+ goto cleanup;
+ }
}
{
diff --git a/tests/test-chains.h b/tests/test-chains.h
index 28974e1..8e250cd 100644
--- a/tests/test-chains.h
+++ b/tests/test-chains.h
@@ -1366,9 +1366,11 @@ static struct
} chains[] =
{
{ "CVE-2014-0092", cve_2014_0092_check, &cve_2014_0092_check[1],
- 0, GNUTLS_CERT_SIGNER_NOT_CA | GNUTLS_CERT_INVALID },
+ 0,
+ GNUTLS_CERT_SIGNER_NOT_CA | GNUTLS_CERT_NOT_ACTIVATED | GNUTLS_CERT_INVALID },
{ "CVE-2008-4989", cve_2008_4989_chain, &cve_2008_4989_chain[2],
- 0, GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID },
+ 0,
+ GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_EXPIRED | GNUTLS_CERT_INVALID },
{ "amazon.com ok", verisign_com_chain_g5, &verisign_com_chain_g5[4],
GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_LOW),
0 },
@@ -1404,9 +1406,10 @@ static struct
GNUTLS_VERIFY_DISABLE_TIME_CHECKS,
0 },
{ "rsa-md5 fail", mayfirst_chain, &mayfirst_chain[1],
- 0, GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID },
+ GNUTLS_VERIFY_DISABLE_TIME_CHECKS,
+ GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID },
{ "rsa-md5 not ok", mayfirst_chain, &mayfirst_chain[1],
- GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2,
+ GNUTLS_VERIFY_DISABLE_TIME_CHECKS | GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2,
GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID },
{ "rsa-md5 not ok2", mayfirst_chain, &mayfirst_chain[1],
GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5,
@@ -1449,7 +1452,7 @@ static struct
GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD5, 0 },
{ "cacertrsamd5 short-cut not ok", cacertrsamd5, &cacertrsamd5[0],
GNUTLS_VERIFY_DO_NOT_ALLOW_SAME,
- GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INVALID },
+ GNUTLS_CERT_SIGNER_NOT_FOUND | GNUTLS_CERT_INSECURE_ALGORITHM | GNUTLS_CERT_INVALID },
{ "cacertrsamd5 short-cut ok", cacertrsamd5, &cacertrsamd5[1],
0, 0 },
{ "ecc cert ok", ecc_cert, &ecc_cert[1], GNUTLS_PROFILE_TO_VFLAGS(GNUTLS_PROFILE_HIGH), 0 },
--
2.1.0
More information about the Gnutls-devel
mailing list