From: Yegor Yefremov <[email protected]>
The harness handed sni_name to gnutls_psk_set_client_credentials() as the
PSK identity, but the two have different limits: MAX_SNI_LEN is 255, while
GnuTLS keeps the identity in a char[MAX_USERNAME_SIZE + 1] with
MAX_USERNAME_SIZE == 128. _gnutls_copy_psk_username() asserts on anything
longer rather than rejecting it, so any generated name above 128 bytes
aborted the harness's own client inside gnutls_handshake(), before a single
byte reached MHD -- the harness was reporting its own bug.
Truncate rather than skip the connection, so the input still reaches the
server with the name it picked: the full 255-byte range is still exercised
through gnutls_server_name_set() just below, and psk_gnutls_adapter() still
sees fuzzer-chosen bytes.
This was only reachable once the certificate callback crash it sat behind
was fixed; seed 1 hit it at iteration 9656.
Assisted-by: Claude:claude-opus-5
---
src/fuzz/fuzz_tls.c | 31 ++++++++++++++++++++++++++-----
1 file changed, 26 insertions(+), 5 deletions(-)
diff --git a/src/fuzz/fuzz_tls.c b/src/fuzz/fuzz_tls.c
index 19f3ba45..0115eff9 100644
--- a/src/fuzz/fuzz_tls.c
+++ b/src/fuzz/fuzz_tls.c
@@ -233,6 +233,17 @@ __lsan_default_suppressions (void)
#define RESP_BUF_SIZE 16384
#define GEN_BUF_SIZE 4096
#define MAX_SNI_LEN 255
+/**
+ * Longest PSK identity GnuTLS will accept.
+ *
+ * Its psk_auth_info keeps the identity in a char[MAX_USERNAME_SIZE + 1] and
+ * _gnutls_copy_psk_username() asserts on anything longer rather than
+ * rejecting it, so an over-long identity aborts the *client* inside
+ * gnutls_handshake() before a single byte reaches MHD -- the harness would
+ * be reporting its own bug. MAX_USERNAME_SIZE is internal to GnuTLS
+ * (lib/gnutls_int.h) and appears in no public header, hence the literal.
+ */
+#define MAX_PSK_IDENTITY_LEN 128
#define FUZZ_PEM_BODY_MAX 1024
/** Number of client priority strings offered to the input. */
@@ -1426,11 +1437,21 @@ tc_open (struct MHD_Daemon *d,
if (cfg.client_psk)
{
/* The identity is the same string byte 9 (or an op 1 segment) picked
- for SNI; it is what arrives as @a username in psk_gnutls_adapter().
- A client that offers no PSK credentials at all against a PSK-only
- server is the other half of this: the handshake then fails without
- the adapter ever being asked. */
+ for SNI, truncated to what GnuTLS can carry (see
+ #MAX_PSK_IDENTITY_LEN); it is what arrives as @a username in
+ psk_gnutls_adapter(). A client that offers no PSK credentials at
+ all against a PSK-only server is the other half of this: the
+ handshake then fails without the adapter ever being asked. */
gnutls_datum_t k;
+ char identity[MAX_PSK_IDENTITY_LEN + 1];
+ size_t identity_len = sni_name_len;
+
+ /* Truncate rather than skip the connection: the input picked this
+ name to reach the server with, and its leading bytes still do. */
+ if (MAX_PSK_IDENTITY_LEN < identity_len)
+ identity_len = MAX_PSK_IDENTITY_LEN;
+ memcpy (identity, sni_name, identity_len);
+ identity[identity_len] = '\0';
k.data = (unsigned char *) (intptr_t) psk_key_bytes;
k.size = (unsigned int) sizeof (psk_key_bytes);
@@ -1441,7 +1462,7 @@ tc_open (struct MHD_Daemon *d,
{
if (GNUTLS_E_SUCCESS !=
gnutls_psk_set_client_credentials (tc->psk,
- sni_name,
+ identity,
&k,
GNUTLS_PSK_KEY_RAW))
tc->dead = 1;
--
2.34.1