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


Reply via email to