tlshd: Send fatal alert to client when there are server config issues

Resolves: RHEL-128086
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
This commit is contained in:
Scott Mayhew 2026-02-19 12:53:33 -05:00
parent b7d9966184
commit c943e3e1a9
4 changed files with 292 additions and 0 deletions

View File

@ -0,0 +1,44 @@
From b52ca7c0a9c167340950a10d8dc56be1b95f5c40 Mon Sep 17 00:00:00 2001
From: Chuck Lever <chuck.lever@oracle.com>
Date: Tue, 23 Sep 2025 19:30:16 -0400
Subject: [PATCH] tlshd: Clean up logic in tlshd_start_tls_handshake()
gnutls_handshake() is supposed to return only a GNUTLS_E value, and
the session_status field is supposed to contain only a positive
errno.
GNUTLS_E_PREMATURE_TERMINATION is -110. It turns out that on x86,
-ETIMEDOUT is also -110. Make sure the correct symbolic constants
and auditing functions are utilized.
Fixes: b010190cfed2 ("tlshd: Pass ETIMEDOUT from gnutls to kernel")
Reviewed-by: Benjamin Coddington <bcodding@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
src/tlshd/handshake.c | 8 ++++----
1 file changed, 4 insertions(+), 4 deletions(-)
diff --git a/src/tlshd/handshake.c b/src/tlshd/handshake.c
index 5a28939..f688932 100644
--- a/src/tlshd/handshake.c
+++ b/src/tlshd/handshake.c
@@ -97,12 +97,12 @@ void tlshd_start_tls_handshake(gnutls_session_t session,
case GNUTLS_E_CERTIFICATE_VERIFICATION_ERROR:
tlshd_log_cert_verification_error(session);
break;
- case -ETIMEDOUT:
- tlshd_log_gnutls_error(ret);
- parms->session_status = -ret;
+ case GNUTLS_E_PREMATURE_TERMINATION:
+ tlshd_log_error("Handshake timeout, retrying");
+ parms->session_status = ETIMEDOUT;
break;
default:
- tlshd_log_notice("tlshd_start_tls_handshake unhandled error %d, returning EACCES\n", ret);
+ tlshd_log_gnutls_error(ret);
}
return;
}
--
2.52.0

View File

@ -0,0 +1,57 @@
From 9e79a86b85748ba4888da49652986b09beac2459 Mon Sep 17 00:00:00 2001
From: Chuck Lever <chuck.lever@oracle.com>
Date: Thu, 5 Feb 2026 14:40:28 -0500
Subject: [PATCH] tlshd: Fix session leak on error paths in x509 server
handshake
Conflicts: src/tlshd/server.c - context difference in
tlshd_tls13_server_x509_handshake() because RHEL10 doesn't have
f11519a ("tlshd: Match ingress certificates with defined TLS session
tags")
After gnutls_init() succeeds, the error paths for gnutls_credentials_set()
and tlshd_gnutls_priority_set() failures jump to out_free_certs without
calling gnutls_deinit(session), leaking the session object.
Introduce an out_deinit_session label so all post-init error paths
properly release the session before cleaning up certificates and
credentials.
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
src/tlshd/server.c | 5 +++--
1 file changed, 3 insertions(+), 2 deletions(-)
diff --git a/src/tlshd/server.c b/src/tlshd/server.c
index 4850210..3bfcca3 100644
--- a/src/tlshd/server.c
+++ b/src/tlshd/server.c
@@ -424,7 +424,7 @@ static void tlshd_tls13_server_x509_handshake(struct tlshd_handshake_parms *parm
ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
if (ret != GNUTLS_E_SUCCESS) {
tlshd_log_gnutls_error(ret);
- goto out_free_certs;
+ goto out_deinit_session;
}
gnutls_certificate_set_verify_function(xcred,
tlshd_tls13_server_x509_verify_function);
@@ -433,7 +433,7 @@ static void tlshd_tls13_server_x509_handshake(struct tlshd_handshake_parms *parm
ret = tlshd_gnutls_priority_set(session, parms, 0);
if (ret) {
tlshd_log_gnutls_error(ret);
- goto out_free_certs;
+ goto out_deinit_session;
}
tlshd_start_tls_handshake(session, parms);
@@ -373,6 +373,7 @@ static void tlshd_tls13_server_x509_hand
}
}
+out_deinit_session:
gnutls_deinit(session);
out_free_certs:
--
2.52.0

View File

@ -0,0 +1,185 @@
From a58a3600f35c16e435cdda476fe7ca0554a0a466 Mon Sep 17 00:00:00 2001
From: Scott Mayhew <smayhew@redhat.com>
Date: Thu, 5 Feb 2026 12:28:43 -0500
Subject: [PATCH] tlshd: Send fatal alert to client when there are server
config issues
Conflicts: src/tlshd/log.c - context difference because RHEL10 doesn't
have ff28f1f ("tlshd: Translate kernel-style Doxygen comments in
src/tlshd/log.c")
Currently if a client attempts an x.509 handshake and the server is
misconfigured (no certificates, no private keys, etc), the server simply
closes the connection. Prior to b010190 ("tlshd: Pass ETIMEDOUT from
gnutls to kernel"), this would result in a quick failure on the client.
Now the client keeps retrying until the mount program times out, which
takes several minutes.
A misconfigured server isn't a self-correcting problem, so send a fatal
alert to the client when this occurs so the client stops retrying
immediately. This requires some minor refactoring of
tlshd_tls13_server_x509_handshake() so that the session is initialized
before attempting to load the certs and keys (otherwise it is not
possible to send an alert). Also log an error message to help the
admin take corrective action.
Finally add some logging when an alert is received during the handshake.
Following suit with handshake completions, alerts will only be logged if
debug logging is enabled.
Signed-off-by: Scott Mayhew <smayhew@redhat.com>
Signed-off-by: Chuck Lever <chuck.lever@oracle.com>
---
src/tlshd/handshake.c | 4 ++++
src/tlshd/log.c | 15 ++++++++++++
src/tlshd/server.c | 55 ++++++++++++++++++++++++-------------------
src/tlshd/tlshd.h | 1 +
4 files changed, 51 insertions(+), 24 deletions(-)
diff --git a/src/tlshd/handshake.c b/src/tlshd/handshake.c
index e78f78f..511a369 100644
--- a/src/tlshd/handshake.c
+++ b/src/tlshd/handshake.c
@@ -115,6 +115,10 @@ void tlshd_start_tls_handshake(gnutls_session_t session,
tlshd_log_error("Handshake timeout, retrying");
parms->session_status = ETIMEDOUT;
break;
+ case GNUTLS_E_WARNING_ALERT_RECEIVED:
+ case GNUTLS_E_FATAL_ALERT_RECEIVED:
+ tlshd_log_alert(session);
+ break;
default:
tlshd_log_gnutls_error(ret);
}
diff --git a/src/tlshd/log.c b/src/tlshd/log.c
index b70d4af..a890b60 100644
--- a/src/tlshd/log.c
+++ b/src/tlshd/log.c
@@ -178,6 +178,21 @@ void tlshd_log_cert_verification_error(g
}
/**
+ * @brief Report a TLS alert
+ * @param[in] session Controlling GnuTLS session
+ */
+void tlshd_log_alert(gnutls_session_t session)
+{
+ gnutls_alert_description_t alert;
+
+ if (!tlshd_debug)
+ return;
+
+ alert = gnutls_alert_get(session);
+ tlshd_log_notice("Received alert: %s", gnutls_alert_get_name(alert));
+}
+
+/**
* tlshd_log_gnutls_error - Emit "library call failed" notification
* @error: GnuTLS error code to log
*
diff --git a/src/tlshd/server.c b/src/tlshd/server.c
index 3bfcca3..8e0f192 100644
--- a/src/tlshd/server.c
+++ b/src/tlshd/server.c
@@ -397,29 +397,46 @@ static void tlshd_tls13_server_x509_handshake(struct tlshd_handshake_parms *parm
gnutls_session_t session;
int ret;
- ret = gnutls_certificate_allocate_credentials(&xcred);
+ ret = gnutls_init(&session, GNUTLS_SERVER);
if (ret != GNUTLS_E_SUCCESS) {
tlshd_log_gnutls_error(ret);
return;
}
- ret = tlshd_server_get_truststore(xcred);
- if (ret != GNUTLS_E_SUCCESS)
- goto out_free_creds;
+ gnutls_transport_set_int(session, parms->sockfd);
+ gnutls_session_set_ptr(session, parms);
- if (!tlshd_x509_server_get_certs(parms))
- goto out_free_creds;
- if (!tlshd_x509_server_get_privkey(parms))
- goto out_free_certs;
- gnutls_certificate_set_retrieve_function2(xcred,
- tlshd_x509_retrieve_key_cb);
+ ret = tlshd_gnutls_priority_set(session, parms, 0);
+ if (ret) {
+ tlshd_log_gnutls_error(ret);
+ gnutls_deinit(session);
+ return;
+ }
- ret = gnutls_init(&session, GNUTLS_SERVER);
+ ret = gnutls_certificate_allocate_credentials(&xcred);
if (ret != GNUTLS_E_SUCCESS) {
tlshd_log_gnutls_error(ret);
- goto out_free_certs;
+ gnutls_deinit(session);
+ return;
}
- gnutls_transport_set_int(session, parms->sockfd);
- gnutls_session_set_ptr(session, parms);
+ ret = tlshd_server_get_truststore(xcred);
+ if (ret != GNUTLS_E_SUCCESS) {
+ tlshd_log_error("Failed to initialize server trust store - check the configuration");
+ gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_ACCESS_DENIED);
+ goto out_deinit_session;
+ }
+
+ if (!tlshd_x509_server_get_certs(parms)) {
+ tlshd_log_error("No usable server certificates were found - check the configuration");
+ gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_ACCESS_DENIED);
+ goto out_deinit_session;
+ }
+ if (!tlshd_x509_server_get_privkey(parms)) {
+ tlshd_log_error("No usable private keys were found - check the configuration");
+ gnutls_alert_send(session, GNUTLS_AL_FATAL, GNUTLS_A_ACCESS_DENIED);
+ goto out_deinit_session;
+ }
+ gnutls_certificate_set_retrieve_function2(xcred,
+ tlshd_x509_retrieve_key_cb);
ret = gnutls_credentials_set(session, GNUTLS_CRD_CERTIFICATE, xcred);
if (ret != GNUTLS_E_SUCCESS) {
@@ -430,12 +447,6 @@ static void tlshd_tls13_server_x509_handshake(struct tlshd_handshake_parms *parm
tlshd_tls13_server_x509_verify_function);
gnutls_certificate_server_set_request(session, GNUTLS_CERT_REQUEST);
- ret = tlshd_gnutls_priority_set(session, parms, 0);
- if (ret) {
- tlshd_log_gnutls_error(ret);
- goto out_deinit_session;
- }
-
tlshd_start_tls_handshake(session, parms);
if (tlshd_debug &&
@@ -464,12 +475,8 @@ static void tlshd_tls13_server_x509_handshake(struct tlshd_handshake_parms *parm
out_deinit_session:
gnutls_deinit(session);
-
-out_free_certs:
tlshd_x509_server_put_privkey();
tlshd_x509_server_put_certs();
-
-out_free_creds:
gnutls_certificate_free_credentials(xcred);
}
diff --git a/src/tlshd/tlshd.h b/src/tlshd/tlshd.h
index 75d9a4a..d00cfdf 100644
--- a/src/tlshd/tlshd.h
+++ b/src/tlshd/tlshd.h
@@ -118,6 +118,7 @@ extern void tlshd_log_perror(const char *prefix);
extern void tlshd_log_gai_error(int error);
extern void tlshd_log_cert_verification_error(gnutls_session_t session);
+extern void tlshd_log_alert(gnutls_session_t session);
extern void tlshd_log_gnutls_error(int error);
extern void tlshd_gnutls_log_func(int level, const char *msg);
extern void tlshd_gnutls_audit_func(gnutls_session_t session, const char *msg);
--
2.52.0

View File

@ -16,10 +16,16 @@ URL: %{forgeurl}
# FIXME: is this a bug in the tagging scheme or forgesource macro?
Source0: %{forgeurl}/releases/download/%{name}-%{baseversion}/%{name}-%{baseversion}.tar.gz
#
# RHEL10.2
#
Patch0: ktls-utils-1.2.1-tlshd-deduplicate-client-and-server-config-functions.patch
Patch1: ktls-utils-1.2.1-tlshd-Fix-priority-string-to-allow-PQC.patch
Patch2: ktls-utils-1.2.1-tlshd-Server-side-dual-certificate-support.patch
Patch3: ktls-utils-1.2.1-tlshd-Client-side-dual-certificate-support.patch
Patch4: ktls-utils-1.2.1-tlshd-Clean-up-logic-in-tlshd_start_tls_handshake.patch
Patch5: ktls-utils-1.2.1-tlshd-Fix-session-leak-on-error-paths-in-x509-server.patch
Patch6: ktls-utils-1.2.1-tlshd-Send-fatal-alert-to-client-when-there-are-serv.patch
BuildRequires: bash systemd-rpm-macros
BuildRequires: gcc make coreutils