Blob Blame History Raw
From 339bef12f478b3a12c59571c53645e31280baf7e Mon Sep 17 00:00:00 2001
From: Daiki Ueno <ueno@gnu.org>
Date: Fri, 14 May 2021 15:59:37 +0200
Subject: [PATCH] cert auth: filter out unsupported cert types from TLS 1.2 CR

When the server is advertising signature algorithms in TLS 1.2
CertificateRequest, it shouldn't send certificate_types not backed by
any of those algorithms.

Signed-off-by: Daiki Ueno <ueno@gnu.org>
---
 lib/auth/cert.c                         | 76 +++++++++++++++++++++++--
 tests/suite/tls-fuzzer/gnutls-cert.json | 19 +++++++
 2 files changed, 89 insertions(+), 6 deletions(-)

diff --git a/lib/auth/cert.c b/lib/auth/cert.c
index 3073a33d3..0b0f04b2b 100644
--- a/lib/auth/cert.c
+++ b/lib/auth/cert.c
@@ -64,6 +64,16 @@ typedef enum CertificateSigType { RSA_SIGN = 1, DSA_SIGN = 2, ECDSA_SIGN = 64,
 #endif
 } CertificateSigType;
 
+enum CertificateSigTypeFlags {
+	RSA_SIGN_FLAG = 1,
+	DSA_SIGN_FLAG = 1 << 1,
+	ECDSA_SIGN_FLAG = 1 << 2,
+#ifdef ENABLE_GOST
+	GOSTR34102012_256_SIGN_FLAG = 1 << 3,
+	GOSTR34102012_512_SIGN_FLAG = 1 << 4
+#endif
+};
+
 /* Moves data from an internal certificate struct (gnutls_pcert_st) to
  * another internal certificate struct (cert_auth_info_t), and deinitializes
  * the former.
@@ -1281,6 +1291,7 @@ _gnutls_gen_cert_server_cert_req(gnutls_session_t session,
 	uint8_t tmp_data[CERTTYPE_SIZE];
 	const version_entry_st *ver = get_version(session);
 	unsigned init_pos = data->length;
+	enum CertificateSigTypeFlags flags;
 
 	if (unlikely(ver == NULL))
 		return gnutls_assert_val(GNUTLS_E_INTERNAL_ERROR);
@@ -1297,18 +1308,71 @@ _gnutls_gen_cert_server_cert_req(gnutls_session_t session,
 		return GNUTLS_E_INSUFFICIENT_CREDENTIALS;
 	}
 
-	i = 1;
+	if (_gnutls_version_has_selectable_sighash(ver)) {
+		size_t j;
+
+		flags = 0;
+		for (j = 0; j < session->internals.priorities->sigalg.size; j++) {
+			const gnutls_sign_entry_st *se =
+				session->internals.priorities->sigalg.entry[j];
+			switch (se->pk) {
+			case GNUTLS_PK_RSA:
+			case GNUTLS_PK_RSA_PSS:
+				flags |= RSA_SIGN_FLAG;
+				break;
+			case GNUTLS_PK_DSA:
+				flags |= DSA_SIGN_FLAG;
+				break;
+			case GNUTLS_PK_ECDSA:
+				flags |= ECDSA_SIGN_FLAG;
+				break;
 #ifdef ENABLE_GOST
-	if (_gnutls_kx_is_vko_gost(session->security_parameters.cs->kx_algorithm)) {
-		tmp_data[i++] = GOSTR34102012_256_SIGN;
-		tmp_data[i++] = GOSTR34102012_512_SIGN;
-	} else
+			case GNUTLS_PK_GOST_12_256:
+				flags |= GOSTR34102012_256_SIGN_FLAG;
+				break;
+			case GNUTLS_PK_GOST_12_512:
+				flags |= GOSTR34102012_512_SIGN_FLAG;
+				break;
+#endif
+			default:
+				gnutls_assert();
+				_gnutls_debug_log(
+					"%s is unsupported for cert request\n",
+					gnutls_pk_get_name(se->pk));
+			}
+		}
+
+	} else {
+#ifdef ENABLE_GOST
+		if (_gnutls_kx_is_vko_gost(session->security_parameters.
+					   cs->kx_algorithm)) {
+			flags = GOSTR34102012_256_SIGN_FLAG |
+				GOSTR34102012_512_SIGN_FLAG;
+		} else
 #endif
-	{
+		{
+			flags = RSA_SIGN_FLAG | DSA_SIGN_FLAG | ECDSA_SIGN_FLAG;
+		}
+	}
+
+	i = 1;
+	if (flags & RSA_SIGN_FLAG) {
 		tmp_data[i++] = RSA_SIGN;
+	}
+	if (flags & DSA_SIGN_FLAG) {
 		tmp_data[i++] = DSA_SIGN;
+	}
+	if (flags & ECDSA_SIGN_FLAG) {
 		tmp_data[i++] = ECDSA_SIGN;
 	}
+#ifdef ENABLE_GOST
+	if (flags & GOSTR34102012_256_SIGN_FLAG) {
+		tmp_data[i++] = GOSTR34102012_256_SIGN;
+	}
+	if (flags & GOSTR34102012_512_SIGN_FLAG) {
+		tmp_data[i++] = GOSTR34102012_512_SIGN;
+	}
+#endif
 	tmp_data[0] = i - 1;
 
 	ret = _gnutls_buffer_append_data(data, tmp_data, i);
-- 
2.31.1