From d326f81daed5a1a06476d66a81584f8c7b71141d Mon Sep 17 00:00:00 2001 From: Nikos Mavrogiannopoulos Date: Mon, 23 Feb 2015 10:03:47 +0100 Subject: [PATCH] Added fix for GNUTLS-SA-2015-1 Fixes CVE-2015-0282. Upstream-Status: Backport Signed-off-by: Sona Sarmadi --- lib/gnutls_algorithms.c | 8 +++++ lib/gnutls_algorithms.h | 1 + lib/gnutls_pubkey.c | 4 +-- lib/gnutls_sig.c | 14 +++++---- lib/x509/common.h | 2 +- lib/x509/crq.c | 49 ++++++++++++++++++++++++++++++- lib/x509/privkey.c | 3 +- lib/x509/verify.c | 77 ++++++++++++++++++++++++++++++++----------------- lib/x509/x509.c | 4 +-- lib/x509/x509_int.h | 7 +++-- 10 files changed, 127 insertions(+), 42 deletions(-) diff --git a/lib/gnutls_algorithms.c b/lib/gnutls_algorithms.c index 9ce73ce..b46f6f2 100644 --- a/lib/gnutls_algorithms.c +++ b/lib/gnutls_algorithms.c @@ -2056,6 +2056,14 @@ gnutls_sign_get_name (gnutls_sign_algorithm_t algorithm) return ret; } +int +_gnutls_sign_get_hash (gnutls_sign_algorithm_t algorithm) +{ + GNUTLS_SIGN_LOOP (if (p->id == algorithm) return p->mac); + + return GNUTLS_MAC_UNKNOWN; +} + gnutls_sign_algorithm_t _gnutls_x509_oid2sign_algorithm (const char *oid) { diff --git a/lib/gnutls_algorithms.h b/lib/gnutls_algorithms.h index ac2ec71..8fa0fcb 100644 --- a/lib/gnutls_algorithms.h +++ b/lib/gnutls_algorithms.h @@ -105,6 +105,7 @@ enum encipher_type enum encipher_type _gnutls_kx_encipher_type (gnutls_kx_algorithm_t algorithm); /* Functions for sign algorithms. */ +int _gnutls_sign_get_hash (gnutls_sign_algorithm_t algorithm); gnutls_sign_algorithm_t _gnutls_x509_oid2sign_algorithm (const char *oid); gnutls_sign_algorithm_t _gnutls_x509_pk_to_sign (gnutls_pk_algorithm_t pk, gnutls_mac_algorithm_t mac); diff --git a/lib/gnutls_pubkey.c b/lib/gnutls_pubkey.c index dc4f545..8b50647 100644 --- a/lib/gnutls_pubkey.c +++ b/lib/gnutls_pubkey.c @@ -1048,7 +1048,7 @@ gnutls_pubkey_verify_data (gnutls_pubkey_t pubkey, unsigned int flags, return GNUTLS_E_INVALID_REQUEST; } - ret = pubkey_verify_sig( data, NULL, signature, pubkey->pk_algorithm, + ret = pubkey_verify_sig(GNUTLS_MAC_UNKNOWN, data, NULL, signature, pubkey->pk_algorithm, pubkey->params, pubkey->params_size); if (ret < 0) { @@ -1086,7 +1086,7 @@ gnutls_pubkey_verify_hash (gnutls_pubkey_t key, unsigned int flags, } ret = - pubkey_verify_sig (NULL, hash, signature, key->pk_algorithm, + pubkey_verify_sig (GNUTLS_MAC_UNKNOWN, NULL, hash, signature, key->pk_algorithm, key->params, key->params_size); return ret; diff --git a/lib/gnutls_sig.c b/lib/gnutls_sig.c index a2f38e5..9542925 100644 --- a/lib/gnutls_sig.c +++ b/lib/gnutls_sig.c @@ -273,7 +273,8 @@ static int verify_tls_hash (gnutls_session_t session, gnutls_protocol_t ver, gnutls_cert * cert, const gnutls_datum_t * hash_concat, gnutls_datum_t * signature, size_t sha1pos, - gnutls_pk_algorithm_t pk_algo) + gnutls_pk_algorithm_t pk_algo, + int hashalg) { int ret; gnutls_datum_t vdata; @@ -309,7 +310,7 @@ verify_tls_hash (gnutls_session_t session, gnutls_protocol_t ver, gnutls_cert * ret = _gnutls_rsa_verify (&vdata, signature, cert->params, cert->params_size, 1); else - ret = pubkey_verify_sig( NULL, &vdata, signature, pk_algo, + ret = pubkey_verify_sig(hashalg, NULL, &vdata, signature, pk_algo, cert->params, cert->params_size); if (ret < 0) @@ -324,7 +325,7 @@ verify_tls_hash (gnutls_session_t session, gnutls_protocol_t ver, gnutls_cert * vdata.data = &hash_concat->data[sha1pos]; vdata.size = hash_concat->size - sha1pos; - ret = pubkey_verify_sig( NULL, &vdata, signature, pk_algo, + ret = pubkey_verify_sig(hashalg, NULL, &vdata, signature, pk_algo, cert->params, cert->params_size); /* verify signature */ if (ret < 0) @@ -428,7 +429,8 @@ _gnutls_handshake_verify_data (gnutls_session_t session, gnutls_cert * cert, ret = verify_tls_hash (session, ver, cert, &dconcat, signature, dconcat.size - _gnutls_hash_get_algo_len (hash_algo), - _gnutls_sign_get_pk_algorithm (algo)); + _gnutls_sign_get_pk_algorithm (algo), + hash_algo); if (ret < 0) { gnutls_assert (); @@ -491,7 +493,7 @@ _gnutls_handshake_verify_cert_vrfy12 (gnutls_session_t session, ret = verify_tls_hash (session, ver, cert, &dconcat, signature, 0, - cert->subject_pk_algorithm); + cert->subject_pk_algorithm, hash_algo); if (ret < 0) { gnutls_assert (); @@ -582,7 +584,7 @@ _gnutls_handshake_verify_cert_vrfy (gnutls_session_t session, ret = verify_tls_hash (session, ver, cert, &dconcat, signature, 16, - cert->subject_pk_algorithm); + cert->subject_pk_algorithm, GNUTLS_MAC_UNKNOWN); if (ret < 0) { gnutls_assert (); diff --git a/lib/x509/common.h b/lib/x509/common.h index 5cc6a10..561e31c 100644 --- a/lib/x509/common.h +++ b/lib/x509/common.h @@ -151,7 +151,7 @@ int _gnutls_get_key_id (gnutls_pk_algorithm_t pk, bigint_t * params, void _asnstr_append_name (char *name, size_t name_size, const char *part1, const char *part2); -int pubkey_verify_sig (const gnutls_datum_t * tbs, +int pubkey_verify_sig (int hashalg, const gnutls_datum_t * tbs, const gnutls_datum_t * hash, const gnutls_datum_t * signature, gnutls_pk_algorithm_t pk, bigint_t * issuer_params, diff --git a/lib/x509/crq.c b/lib/x509/crq.c index ed0f844..e0d3346 100644 --- a/lib/x509/crq.c +++ b/lib/x509/crq.c @@ -2540,6 +2540,7 @@ gnutls_datum data = { NULL, 0 }; gnutls_datum signature = { NULL, 0 }; bigint_t params[MAX_PUBLIC_PARAMS_SIZE]; int ret, params_size = 0, i; +int hashalg, sigalg; ret = _gnutls_x509_get_signed_data (crq->crq, "certificationRequestInfo", &data); @@ -2565,7 +2566,10 @@ int ret, params_size = 0, i; goto cleanup; } - ret = pubkey_verify_sig(&data, NULL, &signature, + sigalg = gnutls_x509_crq_get_signature_algorithm (crq); + hashalg = _gnutls_sign_get_hash(sigalg); + + ret = pubkey_verify_sig(hashalg, &data, NULL, &signature, gnutls_x509_crq_get_pk_algorithm (crq, NULL), params, params_size); if (ret < 0) @@ -2588,5 +2592,48 @@ cleanup: return ret; } +/** + * gnutls_x509_crq_get_signature_algorithm: + * @crl: should contain a #gnutls_x509_crl_t structure + * + * This function will return a value of the #gnutls_sign_algorithm_t + * enumeration that is the signature algorithm. + * + * Returns: On success, %GNUTLS_E_SUCCESS is returned, otherwise a + * negative error value. + **/ +int +gnutls_x509_crq_get_signature_algorithm (gnutls_x509_crq_t crq) +{ + int result; + gnutls_datum_t sa; + + if (crq == NULL) + { + gnutls_assert (); + return GNUTLS_E_INVALID_REQUEST; + } + + /* Read the signature algorithm. Note that parameters are not + * read. They will be read from the issuer's certificate if needed. + */ + + result = + _gnutls_x509_read_value (crq->crq, "signatureAlgorithm.algorithm", + &sa, 0); + + if (result < 0) + { + gnutls_assert (); + return result; + } + + result = _gnutls_x509_oid2sign_algorithm ((const char *) sa.data); + + _gnutls_free_datum (&sa); + + return result; +} + #endif /* ENABLE_PKI */ diff --git a/lib/x509/privkey.c b/lib/x509/privkey.c index 41e6587..7f477a1 100644 --- a/lib/x509/privkey.c +++ b/lib/x509/privkey.c @@ -1828,7 +1828,8 @@ gnutls_x509_privkey_verify_data (gnutls_x509_privkey_t key, return GNUTLS_E_INVALID_REQUEST; } - result = _gnutls_x509_privkey_verify_signature (data, signature, key); + result = _gnutls_x509_privkey_verify_signature (GNUTLS_MAC_UNKNOWN, data, signature, key); + if (result < 0) { gnutls_assert (); diff --git a/lib/x509/verify.c b/lib/x509/verify.c index eef85a8..ba4fdcd 100644 --- a/lib/x509/verify.c +++ b/lib/x509/verify.c @@ -332,6 +332,7 @@ _gnutls_verify_certificate2 (gnutls_x509_crt_t cert, gnutls_datum_t cert_signature = { NULL, 0 }; gnutls_x509_crt_t issuer = NULL; int issuer_version, result = 0; + int sigalg, hashalg; if (output) *output = 0; @@ -399,8 +400,18 @@ _gnutls_verify_certificate2 (gnutls_x509_crt_t cert, goto cleanup; } + sigalg = gnutls_x509_crt_get_signature_algorithm (cert); + hashalg = _gnutls_sign_get_hash(sigalg); + + if (hashalg == GNUTLS_MAC_UNKNOWN) + { + gnutls_assert(); + result = 0; + goto cleanup; + } + result = - _gnutls_x509_verify_signature (&cert_signed_data, NULL, &cert_signature, + _gnutls_x509_verify_signature (hashalg, &cert_signed_data, NULL, &cert_signature, issuer); if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { @@ -423,10 +434,6 @@ _gnutls_verify_certificate2 (gnutls_x509_crt_t cert, */ if (is_issuer (cert, cert) == 0) { - int sigalg; - - sigalg = gnutls_x509_crt_get_signature_algorithm (cert); - if (((sigalg == GNUTLS_SIGN_RSA_MD2) && !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) || ((sigalg == GNUTLS_SIGN_RSA_MD5) && @@ -750,12 +757,12 @@ decode_ber_digest_info (const gnutls_datum_t * info, * params[1] is public key */ static int -_pkcs1_rsa_verify_sig (const gnutls_datum_t * text, - const gnutls_datum_t * prehash, - const gnutls_datum_t * signature, bigint_t * params, - int params_len) +_pkcs1_rsa_verify_sig (gnutls_mac_algorithm_t hash, const gnutls_datum_t * text, + const gnutls_datum_t * prehash, + const gnutls_datum_t * signature, bigint_t * params, + int params_len) { - gnutls_mac_algorithm_t hash = GNUTLS_MAC_UNKNOWN; + gnutls_mac_algorithm_t phash = GNUTLS_MAC_UNKNOWN; int ret; opaque digest[MAX_HASH_SIZE], md[MAX_HASH_SIZE], *cmp; int digest_size; @@ -775,7 +782,7 @@ _pkcs1_rsa_verify_sig (const gnutls_datum_t * text, digest_size = sizeof (digest); if ((ret = - decode_ber_digest_info (&decrypted, &hash, digest, &digest_size)) != 0) + decode_ber_digest_info (&decrypted, &phash, digest, &digest_size)) != 0) { gnutls_assert (); _gnutls_free_datum (&decrypted); @@ -784,6 +791,15 @@ _pkcs1_rsa_verify_sig (const gnutls_datum_t * text, _gnutls_free_datum (&decrypted); + if (hash != GNUTLS_MAC_UNKNOWN && hash != phash) + { + gnutls_assert(); + return GNUTLS_E_PK_SIG_VERIFY_FAILED; + } + else + hash = phash; + + if (digest_size != _gnutls_hash_get_algo_len (hash)) { gnutls_assert (); @@ -879,11 +895,11 @@ dsa_verify_sig (const gnutls_datum_t * text, * not verified, or 1 otherwise. */ int -pubkey_verify_sig (const gnutls_datum_t * tbs, - const gnutls_datum_t * hash, - const gnutls_datum_t * signature, - gnutls_pk_algorithm_t pk, bigint_t * issuer_params, - int issuer_params_size) +pubkey_verify_sig (int hashalg, const gnutls_datum_t * tbs, + const gnutls_datum_t * hash, + const gnutls_datum_t * signature, + gnutls_pk_algorithm_t pk, bigint_t * issuer_params, + int issuer_params_size) { switch (pk) @@ -891,7 +907,7 @@ pubkey_verify_sig (const gnutls_datum_t * tbs, case GNUTLS_PK_RSA: if (_pkcs1_rsa_verify_sig - (tbs, hash, signature, issuer_params, issuer_params_size) != 0) + (hashalg, tbs, hash, signature, issuer_params, issuer_params_size) != 0) { gnutls_assert (); return GNUTLS_E_PK_SIG_VERIFY_FAILED; @@ -1022,7 +1038,7 @@ cleanup: * 'signature' is the signature! */ int -_gnutls_x509_verify_signature (const gnutls_datum_t * tbs, +_gnutls_x509_verify_signature (int hashalg, const gnutls_datum_t * tbs, const gnutls_datum_t * hash, const gnutls_datum_t * signature, gnutls_x509_crt_t issuer) @@ -1042,7 +1058,7 @@ _gnutls_x509_verify_signature (const gnutls_datum_t * tbs, } ret = - pubkey_verify_sig (tbs, hash, signature, + pubkey_verify_sig (hashalg, tbs, hash, signature, gnutls_x509_crt_get_pk_algorithm (issuer, NULL), issuer_params, issuer_params_size); if (ret < 0) @@ -1067,13 +1083,13 @@ _gnutls_x509_verify_signature (const gnutls_datum_t * tbs, * 'signature' is the signature! */ int -_gnutls_x509_privkey_verify_signature (const gnutls_datum_t * tbs, +_gnutls_x509_privkey_verify_signature (int hashalg, const gnutls_datum_t * tbs, const gnutls_datum_t * signature, gnutls_x509_privkey_t issuer) { int ret; - ret = pubkey_verify_sig (tbs, NULL, signature, issuer->pk_algorithm, + ret = pubkey_verify_sig (hashalg, tbs, NULL, signature, issuer->pk_algorithm, issuer->params, issuer->params_size); if (ret < 0) { @@ -1294,6 +1310,7 @@ _gnutls_verify_crl2 (gnutls_x509_crl_t crl, gnutls_datum_t crl_signature = { NULL, 0 }; gnutls_x509_crt_t issuer; int result; + int sigalg, hashalg; if (output) *output = 0; @@ -1335,6 +1352,7 @@ _gnutls_verify_crl2 (gnutls_x509_crl_t crl, if (result < 0) { gnutls_assert (); + result = 0; goto cleanup; } @@ -1342,11 +1360,21 @@ _gnutls_verify_crl2 (gnutls_x509_crl_t crl, if (result < 0) { gnutls_assert (); + result = 0; + goto cleanup; + } + + sigalg = gnutls_x509_crl_get_signature_algorithm (crl); + hashalg = _gnutls_sign_get_hash(sigalg); + if (hashalg == GNUTLS_MAC_UNKNOWN) + { + gnutls_assert(); + result = 0; goto cleanup; } result = - _gnutls_x509_verify_signature (&crl_signed_data, NULL, &crl_signature, + _gnutls_x509_verify_signature (hashalg, &crl_signed_data, NULL, &crl_signature, issuer); if (result == GNUTLS_E_PK_SIG_VERIFY_FAILED) { @@ -1359,14 +1387,11 @@ _gnutls_verify_crl2 (gnutls_x509_crl_t crl, else if (result < 0) { gnutls_assert (); + result = 0; goto cleanup; } { - int sigalg; - - sigalg = gnutls_x509_crl_get_signature_algorithm (crl); - if (((sigalg == GNUTLS_SIGN_RSA_MD2) && !(flags & GNUTLS_VERIFY_ALLOW_SIGN_RSA_MD2)) || ((sigalg == GNUTLS_SIGN_RSA_MD5) && diff --git a/lib/x509/x509.c b/lib/x509/x509.c index f51ba3b..d12d44e 100644 --- a/lib/x509/x509.c +++ b/lib/x509/x509.c @@ -2714,7 +2714,7 @@ gnutls_x509_crt_verify_data (gnutls_x509_crt_t crt, unsigned int flags, return GNUTLS_E_INVALID_REQUEST; } - result = _gnutls_x509_verify_signature (data, NULL, signature, crt); + result = _gnutls_x509_verify_signature (GNUTLS_MAC_UNKNOWN, data, NULL, signature, crt); if (result < 0) { gnutls_assert (); @@ -2752,7 +2752,7 @@ gnutls_x509_crt_verify_hash (gnutls_x509_crt_t crt, unsigned int flags, return GNUTLS_E_INVALID_REQUEST; } - result = _gnutls_x509_verify_signature (NULL, hash, signature, crt); + result = _gnutls_x509_verify_signature (GNUTLS_MAC_UNKNOWN, NULL, hash, signature, crt); if (result < 0) { gnutls_assert (); diff --git a/lib/x509/x509_int.h b/lib/x509/x509_int.h index 1b3cfe5..eed56a9 100644 --- a/lib/x509/x509_int.h +++ b/lib/x509/x509_int.h @@ -187,11 +187,11 @@ _gnutls_x509_verify_algorithm (gnutls_mac_algorithm_t * hash, bigint_t * issuer_params, unsigned int issuer_params_size); -int _gnutls_x509_verify_signature (const gnutls_datum_t * tbs, +int _gnutls_x509_verify_signature (int sigalg, const gnutls_datum_t * tbs, const gnutls_datum_t * hash, const gnutls_datum_t * signature, gnutls_x509_crt_t issuer); -int _gnutls_x509_privkey_verify_signature (const gnutls_datum_t * tbs, +int _gnutls_x509_privkey_verify_signature (int sigalg, const gnutls_datum_t * tbs, const gnutls_datum_t * signature, gnutls_x509_privkey_t issuer); @@ -390,5 +390,6 @@ int _gnutls_x509_crq_set_extension (gnutls_x509_crq_t crq, const char *ext_id, const gnutls_datum_t * ext_data, unsigned int critical); - +int +gnutls_x509_crq_get_signature_algorithm (gnutls_x509_crq_t crq); #endif -- 1.9.1