summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/algorithms/ciphersuites.c2
-rw-r--r--lib/auth.c23
-rw-r--r--lib/auth/cert.c68
-rw-r--r--lib/auth/cert.h11
-rw-r--r--lib/gnutls_int.h4
-rw-r--r--lib/handshake.c8
-rw-r--r--lib/tls-sig.c11
-rw-r--r--lib/tls-sig.h3
-rw-r--r--lib/tls13-sig.c11
9 files changed, 109 insertions, 32 deletions
diff --git a/lib/algorithms/ciphersuites.c b/lib/algorithms/ciphersuites.c
index b97bbc82db..7269861ffe 100644
--- a/lib/algorithms/ciphersuites.c
+++ b/lib/algorithms/ciphersuites.c
@@ -1578,7 +1578,7 @@ _gnutls_figure_common_ciphersuite(gnutls_session_t session,
/* RFC7919 requires that we reply with insufficient security if we have
* negotiated an FFDHE group, but cannot find a common ciphersuite. However,
* we must also distinguish between not matching a ciphersuite due to an
- * incompatible certificate which we traditionally return GNUTLS_E_INSUFFICIENT_SECURITY.
+ * incompatible certificate which we traditionally return GNUTLS_E_NO_CIPHER_SUITES.
*/
if (!no_cert_found && (session->internals.hsk_flags & HSK_HAVE_FFDHE) &&
session->internals.priorities->groups.have_ffdhe && !version->tls13_sem)
diff --git a/lib/auth.c b/lib/auth.c
index 91a67c9afa..dd3fc861fb 100644
--- a/lib/auth.c
+++ b/lib/auth.c
@@ -138,6 +138,29 @@ gnutls_credentials_set(gnutls_session_t session,
}
}
+ /* sanity tests */
+ if (type == GNUTLS_CRD_CERTIFICATE) {
+ gnutls_certificate_credentials_t c = cred;
+ unsigned i;
+ bool allow_tls13 = 0;
+ unsigned key_usage;
+
+ if (c != NULL && c->ncerts != 0) {
+ for (i = 0; i < c->ncerts; i++) {
+ key_usage = get_key_usage(session, c->certs[i].cert_list[0].pubkey);
+ if (key_usage == 0 || (key_usage & GNUTLS_KEY_DIGITAL_SIGNATURE)) {
+ allow_tls13 = 1;
+ break;
+ }
+ }
+
+ if (!allow_tls13) {
+ /* to prevent the server random indicate TLS1.3 support */
+ session->internals.flags |= INT_FLAG_NO_TLS13;
+ }
+ }
+ }
+
return 0;
}
diff --git a/lib/auth/cert.c b/lib/auth/cert.c
index b6bd3bf91e..5868ca244d 100644
--- a/lib/auth/cert.c
+++ b/lib/auth/cert.c
@@ -181,6 +181,7 @@ find_x509_client_cert(gnutls_session_t session,
ssize_t data_size = _data_size;
unsigned i, j;
int result, cert_pk;
+ unsigned key_usage;
*indx = -1;
@@ -191,8 +192,16 @@ find_x509_client_cert(gnutls_session_t session,
(data_size == 0
|| (session->internals.flags & GNUTLS_FORCE_CLIENT_CERT))) {
if (cred->certs[0].cert_list[0].type == GNUTLS_CRT_X509) {
- /* This check is necessary to prevent sending other certificate
- * credentials that are set (e.g. raw public-key). */
+
+ key_usage = get_key_usage(session, cred->certs[0].cert_list[0].pubkey);
+
+ /* For client certificates we require signatures */
+ result = _gnutls_check_key_usage_for_sig(session, key_usage, 1);
+ if (result < 0) {
+ _gnutls_debug_log("Client certificate is not suitable for signing\n");
+ return gnutls_assert_val(result);
+ }
+
*indx = 0;
return 0;
}
@@ -221,6 +230,14 @@ find_x509_client_cert(gnutls_session_t session,
if (odn.size == 0 || odn.size != asked_dn.size)
continue;
+ key_usage = get_key_usage(session, cred->certs[i].cert_list[0].pubkey);
+
+ /* For client certificates we require signatures */
+ if (_gnutls_check_key_usage_for_sig(session, key_usage, 1) < 0) {
+ _gnutls_debug_log("Client certificate is not suitable for signing\n");
+ continue;
+ }
+
/* If the DN matches and
* the *_SIGN algorithm matches
* the cert is our cert!
@@ -268,6 +285,7 @@ find_rawpk_client_cert(gnutls_session_t session,
int pk_algos_length, int* indx)
{
unsigned i;
+ int ret;
gnutls_pk_algorithm_t pk;
*indx = -1;
@@ -276,15 +294,22 @@ find_rawpk_client_cert(gnutls_session_t session,
/* We know that our list length will be 1, therefore we can
* ignore the rest.
*/
- if (cred->certs[i].cert_list_length == 1) {
- pk = gnutls_pubkey_get_pk_algorithm(cred->certs[i].
- cert_list[0].pubkey, NULL);
+ if (cred->certs[i].cert_list_length == 1 && cred->certs[i].cert_list[0].type == GNUTLS_CRT_RAWPK) {
+ pk = gnutls_pubkey_get_pk_algorithm(cred->certs[i].cert_list[0].pubkey, NULL);
+
+ /* For client certificates we require signatures */
+ ret = _gnutls_check_key_usage_for_sig(session, get_key_usage(session, cred->certs[i].cert_list[0].pubkey), 1);
+ if (ret < 0) {
+ /* we return an error instead of skipping so that the user is notified about
+ * the key incompatibility */
+ _gnutls_debug_log("Client certificate is not suitable for signing\n");
+ return gnutls_assert_val(ret);
+ }
/* Check whether the public-key algorithm of our credential is in
* the list with supported public-key algorithms and whether the
* cert type matches. */
- if ((check_pk_algo_in_list(pk_algos, pk_algos_length, pk) == 0)
- && (cred->certs[i].cert_list[0].type == GNUTLS_CRT_RAWPK)) {
+ if ((check_pk_algo_in_list(pk_algos, pk_algos_length, pk) == 0)) {
// We found a compatible credential
*indx = i;
break;
@@ -643,21 +668,24 @@ _gnutls_gen_rawpk_crt(gnutls_session_t session, gnutls_buffer_st* data)
/* Since we are transmitting a raw public key with no additional
* certificate credentials attached to it, it doesn't make sense to
* have more than one certificate set (i.e. to have a certificate chain).
- * This is enforced by the API so having a value other than 1 should
- * be an impossible situation.
*/
- assert(apr_cert_list_length == 1);
-
+ assert(apr_cert_list_length <= 1);
+
/* Write our certificate containing only the SubjectPublicKeyInfo to
* the output buffer. We always have exactly one certificate that
* contains our raw public key. Our message looks like:
* <length++certificate> where
- * length = 3 bytes and
+ * length = 3 bytes (or 24 bits) and
* certificate = length bytes.
*/
- ret = _gnutls_buffer_append_data_prefix(data, 24,
- apr_cert_list[0].cert.data,
- apr_cert_list[0].cert.size);
+ if (apr_cert_list_length == 0) {
+ ret = _gnutls_buffer_append_prefix(data, 24, 0);
+ } else {
+ ret = _gnutls_buffer_append_data_prefix(data, 24,
+ apr_cert_list[0].cert.data,
+ apr_cert_list[0].cert.size);
+ }
+
if (ret < 0) return gnutls_assert_val(ret);
@@ -1462,11 +1490,11 @@ int cert_select_sign_algorithm(gnutls_session_t session,
return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
}
- if (unlikely(session->internals.priorities->allow_server_key_usage_violation)) {
- key_usage = 0;
- } else {
- key_usage = pubkey->key_usage;
- }
+ key_usage = get_key_usage(session, pubkey);
+
+ /* In TLS1.3 we support only signatures; ensure the selected key supports them */
+ if (ver->tls13_sem && _gnutls_check_key_usage_for_sig(session, key_usage, 1) < 0)
+ return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
if (!ver->tls13_sem && !_gnutls_kx_supports_pk_usage(cs->kx_algorithm, pk, key_usage)) {
return gnutls_assert_val(GNUTLS_E_INSUFFICIENT_CREDENTIALS);
diff --git a/lib/auth/cert.h b/lib/auth/cert.h
index 3f57ec1c74..fd66628820 100644
--- a/lib/auth/cert.h
+++ b/lib/auth/cert.h
@@ -1,6 +1,6 @@
/*
* Copyright (C) 2002-2012 Free Software Foundation, Inc.
- * Copyright (C) 2016-2017 Red Hat, Inc.
+ * Copyright (C) 2016-2019 Red Hat, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
@@ -29,6 +29,7 @@
#include <gnutls/abstract.h>
#include <gnutls/compat.h>
#include <str_array.h>
+#include "abstract_int.h"
#define MAX_OCSP_RESPONSES 8
@@ -171,5 +172,13 @@ int _gnutls_gen_rawpk_crt(gnutls_session_t session, gnutls_buffer_st* data);
int _gnutls_proc_rawpk_crt(gnutls_session_t session,
uint8_t * data, size_t data_size);
+inline static unsigned get_key_usage(gnutls_session_t session, gnutls_pubkey_t pubkey)
+{
+ if (unlikely(session->internals.priorities &&
+ session->internals.priorities->allow_server_key_usage_violation))
+ return 0;
+ else
+ return pubkey->key_usage;
+}
#endif
diff --git a/lib/gnutls_int.h b/lib/gnutls_int.h
index a0c47efa0f..93ffd7cee9 100644
--- a/lib/gnutls_int.h
+++ b/lib/gnutls_int.h
@@ -1566,9 +1566,9 @@ inline static size_t max_user_send_size(gnutls_session_t session,
*
* This function is made static inline for optimization reasons.
*/
-static inline gnutls_certificate_type_t
+inline static gnutls_certificate_type_t
get_certificate_type(gnutls_session_t session,
- gnutls_ctype_target_t target)
+ gnutls_ctype_target_t target)
{
switch (target) {
case GNUTLS_CTYPE_CLIENT:
diff --git a/lib/handshake.c b/lib/handshake.c
index 70b4486266..481210ebc0 100644
--- a/lib/handshake.c
+++ b/lib/handshake.c
@@ -444,6 +444,9 @@ _gnutls_negotiate_version(gnutls_session_t session,
if (aversion && aversion->id == GNUTLS_TLS1_2) {
vers = _gnutls_version_max(session);
+ if (unlikely(vers == NULL))
+ return gnutls_assert_val(GNUTLS_E_NO_CIPHER_SUITES);
+
if (vers->id >= GNUTLS_TLS1_2) {
session->security_parameters.pversion = aversion;
return 0;
@@ -2138,7 +2141,10 @@ static int send_client_hello(gnutls_session_t session, int again)
if (hver == NULL) {
gnutls_assert();
- ret = GNUTLS_E_NO_PRIORITIES_WERE_SET;
+ if (session->internals.flags & INT_FLAG_NO_TLS13)
+ ret = GNUTLS_E_INSUFFICIENT_CREDENTIALS;
+ else
+ ret = GNUTLS_E_NO_PRIORITIES_WERE_SET;
goto cleanup;
}
diff --git a/lib/tls-sig.c b/lib/tls-sig.c
index 19357c06a1..73ca4314a3 100644
--- a/lib/tls-sig.c
+++ b/lib/tls-sig.c
@@ -40,8 +40,7 @@
#include <x509/common.h>
#include <abstract_int.h>
-static
-int check_key_usage_for_sig(gnutls_session_t session, unsigned key_usage, unsigned our_cert)
+int _gnutls_check_key_usage_for_sig(gnutls_session_t session, unsigned key_usage, unsigned our_cert)
{
const char *lstr;
unsigned allow_key_usage_violation;
@@ -192,7 +191,7 @@ _gnutls_handshake_sign_data(gnutls_session_t session,
gnutls_pubkey_get_key_usage(cert->pubkey, &key_usage);
- ret = check_key_usage_for_sig(session, key_usage, 1);
+ ret = _gnutls_check_key_usage_for_sig(session, key_usage, 1);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -336,7 +335,7 @@ _gnutls_handshake_verify_data(gnutls_session_t session,
gnutls_pubkey_get_key_usage(cert->pubkey, &key_usage);
- ret = check_key_usage_for_sig(session, key_usage, 0);
+ ret = _gnutls_check_key_usage_for_sig(session, key_usage, 0);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -497,7 +496,7 @@ _gnutls_handshake_verify_crt_vrfy(gnutls_session_t session,
gnutls_pubkey_get_key_usage(cert->pubkey, &key_usage);
- ret = check_key_usage_for_sig(session, key_usage, 0);
+ ret = _gnutls_check_key_usage_for_sig(session, key_usage, 0);
if (ret < 0)
return gnutls_assert_val(ret);
@@ -708,7 +707,7 @@ _gnutls_handshake_sign_crt_vrfy(gnutls_session_t session,
gnutls_pubkey_get_key_usage(cert->pubkey, &key_usage);
- ret = check_key_usage_for_sig(session, key_usage, 1);
+ ret = _gnutls_check_key_usage_for_sig(session, key_usage, 1);
if (ret < 0)
return gnutls_assert_val(ret);
diff --git a/lib/tls-sig.h b/lib/tls-sig.h
index afea4ebaf9..6e0564de1b 100644
--- a/lib/tls-sig.h
+++ b/lib/tls-sig.h
@@ -32,6 +32,9 @@
*/
#define MAX_SIG_SIZE (19 + MAX_HASH_SIZE)
+int _gnutls_check_key_usage_for_sig(gnutls_session_t session, unsigned key_usage,
+ unsigned our_cert);
+
int _gnutls_handshake_sign_crt_vrfy(gnutls_session_t session,
gnutls_pcert_st * cert,
gnutls_privkey_t pkey,
diff --git a/lib/tls13-sig.c b/lib/tls13-sig.c
index 8eea6166b3..1f3a74bb5e 100644
--- a/lib/tls13-sig.c
+++ b/lib/tls13-sig.c
@@ -1,5 +1,5 @@
/*
- * Copyright (C) 2017-2018 Red Hat, Inc.
+ * Copyright (C) 2017-2019 Red Hat, Inc.
*
* Author: Nikos Mavrogiannopoulos
*
@@ -27,6 +27,7 @@
#include <ext/signature.h>
#include <abstract_int.h>
#include "tls13-sig.h"
+#include "tls-sig.h"
#include "hash_int.h"
#undef PREFIX_SIZE
@@ -48,6 +49,7 @@ _gnutls13_handshake_verify_data(gnutls_session_t session,
const version_entry_st *ver = get_version(session);
gnutls_buffer_st buf;
uint8_t prefix[PREFIX_SIZE];
+ unsigned key_usage = 0;
gnutls_datum_t p;
_gnutls_handshake_log
@@ -75,6 +77,12 @@ _gnutls13_handshake_verify_data(gnutls_session_t session,
if (se->tls13_ok == 0) /* explicitly prohibited */
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ gnutls_pubkey_get_key_usage(cert->pubkey, &key_usage);
+
+ ret = _gnutls_check_key_usage_for_sig(session, key_usage, 0);
+ if (ret < 0)
+ return gnutls_assert_val(ret);
+
_gnutls_buffer_init(&buf);
memset(prefix, 0x20, sizeof(prefix));
@@ -150,6 +158,7 @@ _gnutls13_handshake_sign_data(gnutls_session_t session,
if (unlikely(sign_supports_priv_pk_algorithm(se, pkey->pk_algorithm) == 0))
return gnutls_assert_val(GNUTLS_E_RECEIVED_ILLEGAL_PARAMETER);
+ /* when we reach here we know we have a signing certificate */
_gnutls_handshake_log
("HSK[%p]: signing TLS 1.3 handshake data: using %s and PRF: %s\n", session, se->name,
session->security_parameters.prf->name);