From: Ken Milmore <ken.milmore@gmail.com>
To: kernel-tls-handshake@lists.linux.dev
Subject: [PATCH 5/7] server: Add boolean config option "verify_peername" under "[authenticate.server]".
Date: Sun, 8 Jun 2025 18:43:58 +0100 [thread overview]
Message-ID: <4b17224c-0525-4e30-9ff3-4c0abaacab89@gmail.com> (raw)
If this option is set to true, then the server will pass the client hostname to GnuTLS to be verified against the client certificate.
A forward address lookup using getaddrinfo() is first performed to confirm that the client hostname maps to the client network address.
Should this forward confirmation fail, the client certificate is rejected out of hand.
Signed-off-by: Ken Milmore <ken.milmore@gmail.com>
---
src/tlshd/config.c | 3 ++
src/tlshd/server.c | 124 ++++++++++++++++++++++++++++++++++++++++++++-
src/tlshd/tlshd.h | 1 +
3 files changed, 127 insertions(+), 1 deletion(-)
diff --git a/src/tlshd/config.c b/src/tlshd/config.c
index be5d472..c647681 100644
--- a/src/tlshd/config.c
+++ b/src/tlshd/config.c
@@ -92,6 +92,9 @@ bool tlshd_config_init(const gchar *pathname)
"delay_done", NULL);
tlshd_delay_done = tmp > 0 ? (unsigned int)tmp : 0;
+ tlshd_server_verify_peername = g_key_file_get_string(tlshd_configuration,
+ "authenticate.server", "verify_peername", NULL);
+
keyrings = g_key_file_get_string_list(tlshd_configuration,
"authenticate",
"keyrings", &length, NULL);
diff --git a/src/tlshd/server.c b/src/tlshd/server.c
index 72ff6f5..4a47fe8 100644
--- a/src/tlshd/server.c
+++ b/src/tlshd/server.c
@@ -31,6 +31,7 @@
#include <stdio.h>
#include <errno.h>
#include <keyutils.h>
+#include <netdb.h>
#include <gnutls/gnutls.h>
#include <gnutls/abstract.h>
@@ -42,10 +43,127 @@
#include "tlshd.h"
#include "netlink.h"
+bool tlshd_server_verify_peername = false;
+
static gnutls_privkey_t tlshd_server_privkey;
static unsigned int tlshd_server_certs_len = TLSHD_MAX_CERTS;
static gnutls_pcert_st tlshd_server_certs[TLSHD_MAX_CERTS];
+/**
+ * tlshd_forward_confirm_hostname - Confirm that the given host name can be
+ * mapped to the given socket address by forward address lookup.
+ *
+ * Return values:
+ * %true: Forward lookup successfully mapped @hostname to @hostaddr.
+ * %false: No such mapping was found, or an error occurred.
+ */
+static bool tlshd_forward_confirm_hostname(const char *hostname,
+ const struct sockaddr *hostaddr,
+ socklen_t hostaddr_len)
+{
+ const struct sockaddr_in *ha4, *ai4;
+ const struct sockaddr_in6 *ha6, *ai6;
+ struct addrinfo hints = { 0 };
+ struct addrinfo *res, *ai;
+ bool matched = false;
+ int err;
+
+ /* Refuse to confirm an empty host name. */
+ if (hostname[0] == '\0')
+ return false;
+
+ /* Check that the supplied address is reasonable. */
+ if (hostaddr_len < sizeof(struct sockaddr_in))
+ return false;
+ if (hostaddr->sa_family == AF_INET6) {
+ if (hostaddr_len < sizeof(struct sockaddr_in6))
+ return false;
+ ha6 = (const struct sockaddr_in6 *)hostaddr;
+ /* Refuse to confirm a link-local address. */
+ if (IN6_IS_ADDR_LINKLOCAL(&ha6->sin6_addr))
+ return false;
+ if (IN6_IS_ADDR_MC_LINKLOCAL(&ha6->sin6_addr))
+ return false;
+ }
+
+ hints.ai_family = hostaddr->sa_family;
+ hints.ai_socktype = SOCK_RAW; /* To avoid duplicate results. */
+
+ /* Fetch a list of addresses matching the host name. */
+ err = getaddrinfo(hostname, NULL, &hints, &res);
+ if (err) {
+ tlshd_log_gai_error(err);
+ return false;
+ }
+
+ /* Search the returned addresses for a match against the supplied address. */
+ switch(hostaddr->sa_family) {
+ case AF_INET:
+ ha4 = (const struct sockaddr_in *)hostaddr;
+ for (ai = res; ai && !matched; ai = res->ai_next) {
+ if (ai->ai_family != AF_INET)
+ continue;
+ if (ai->ai_addrlen < sizeof(struct sockaddr_in))
+ continue;
+ ai4 = (const struct sockaddr_in *)ai->ai_addr;
+ if (ha4->sin_addr.s_addr == ai4->sin_addr.s_addr)
+ matched = true;
+ }
+ break;
+
+ case AF_INET6:
+ ha6 = (const struct sockaddr_in6 *)hostaddr;
+ for (ai = res; ai && !matched; ai = res->ai_next) {
+ if (ai->ai_family != AF_INET6)
+ continue;
+ if (ai->ai_addrlen < sizeof(struct sockaddr_in6))
+ continue;
+ ai6 = (const struct sockaddr_in6 *)ai->ai_addr;
+ if (IN6_ARE_ADDR_EQUAL(&ha6->sin6_addr, &ai6->sin6_addr))
+ matched = true;
+ }
+ break;
+ }
+
+ freeaddrinfo(res);
+ return matched;
+}
+
+/**
+ * tlshd_server_get_verify_hostname - Determine the client host name which will
+ * be passed to GnuTLS for client certificate verification.
+ *
+ * @parms: handshake parameters
+ * @hostname_p: Receives a pointer to the host name to use, which may be NULL.
+ *
+ * Return values:
+ * %true: Proceed with certificate verification.
+ * %false: Force a certificate error.
+ */
+static bool tlshd_server_get_verify_hostname(struct tlshd_handshake_parms *parms,
+ const char **hostname_p)
+{
+ const char *hostname = NULL;
+ bool verify_hostname = true;
+
+ if (tlshd_server_verify_peername) {
+ tlshd_log_debug("server: Forward-confirm peer name '%s' against address '%s'",
+ parms->peername, parms->peeraddr_txt);
+ if (tlshd_forward_confirm_hostname(parms->peername, parms->peeraddr,
+ parms->peeraddr_len)) {
+ tlshd_log_debug("server: Verify forward-confirmed peer name '%s'",
+ parms->peername);
+ hostname = parms->peername;
+ } else {
+ tlshd_log_debug("server: Peer name unconfirmed: Forcing error.");
+ verify_hostname = false;
+ }
+ }
+
+ *hostname_p = hostname;
+ return verify_hostname;
+}
+
/*
* XXX: After this point, tlshd_server_certs should be deinited on error.
*/
@@ -146,11 +264,15 @@ static int tlshd_server_x509_verify_function(gnutls_session_t session,
{
const gnutls_datum_t *peercerts;
gnutls_certificate_type_t type;
+ const char *hostname = NULL;
unsigned int i, status;
gnutls_datum_t out;
int ret;
- ret = gnutls_certificate_verify_peers3(session, NULL, &status);
+ if (!tlshd_server_get_verify_hostname(parms, &hostname))
+ goto certificate_error;
+
+ ret = gnutls_certificate_verify_peers3(session, hostname, &status);
switch (ret) {
case GNUTLS_E_SUCCESS:
break;
diff --git a/src/tlshd/tlshd.h b/src/tlshd/tlshd.h
index 29b0715..9e5b221 100644
--- a/src/tlshd/tlshd.h
+++ b/src/tlshd/tlshd.h
@@ -24,6 +24,7 @@ extern int tlshd_debug;
extern int tlshd_tls_debug;
extern unsigned int tlshd_delay_done;
extern int tlshd_stderr;
+extern bool tlshd_server_verify_peername;
struct nl_sock;
--
2.47.2
next reply other threads:[~2025-06-08 17:44 UTC|newest]
Thread overview: 3+ messages / expand[flat|nested] mbox.gz Atom feed top
2025-06-08 17:43 Ken Milmore [this message]
2025-06-08 18:30 ` [PATCH 5/7] server: Add boolean config option "verify_peername" under "[authenticate.server]" Chuck Lever
2025-06-08 18:45 ` Ken Milmore
Reply instructions:
You may reply publicly to this message via plain-text email
using any one of the following methods:
* Save the following mbox file, import it into your mail client,
and reply-to-all from there: mbox
Avoid top-posting and favor interleaved quoting:
https://en.wikipedia.org/wiki/Posting_style#Interleaved_style
* Reply using the --to, --cc, and --in-reply-to
switches of git-send-email(1):
git send-email \
--in-reply-to=4b17224c-0525-4e30-9ff3-4c0abaacab89@gmail.com \
--to=ken.milmore@gmail.com \
--cc=kernel-tls-handshake@lists.linux.dev \
/path/to/YOUR_REPLY
https://kernel.org/pub/software/scm/git/docs/git-send-email.html
* If your mail client supports setting the In-Reply-To header
via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line
before the message body.
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox