From 509a0471b4a5f901596e6c669dbbf5313f637a4c Mon Sep 17 00:00:00 2001 From: Mike Gorse Date: Thu, 8 Jan 2026 16:19:37 -0600 Subject: [PATCH] soup-auth-ntlm: Reject excessively long passwords According to https://learn.microsoft.com/en-us/troubleshoot/windows-server/windows-security/ntlm-user-authentication, the practical limit for a NTLM password is 128 Unicode characters, so it should be safe to reject passwords longer than 256 bytes. Previously, md4sum could overflow and cause an out-of-bounds memory access if an extremely long password was provided. Also update md4sum to use unsigned variables for size-related calculations, as a precaution. This is CVE-2026-0719. Closes #477. --- libsoup/soup-auth-ntlm.c | 27 ++++++++++++++++++++------- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/libsoup/soup-auth-ntlm.c b/libsoup/soup-auth-ntlm.c index a4465ada..64eedc64 100644 --- a/libsoup/soup-auth-ntlm.c +++ b/libsoup/soup-auth-ntlm.c @@ -349,6 +349,14 @@ soup_auth_ntlm_update_connection (SoupConnectionAuth *auth, SoupMessage *msg, return FALSE; } + if (priv->password_state == SOUP_NTLM_PASSWORD_PROVIDED && !priv->nt_hash[0]) { + /* This can happen if an excessively long password was + * provided, in which case we don't try to hash */ + conn->state = SOUP_NTLM_FAILED; + priv->password_state = SOUP_NTLM_PASSWORD_REJECTED; + return TRUE; + } + if (!soup_ntlm_parse_challenge (auth_header + 5, &conn->nonce, priv->domain ? NULL : &priv->domain, &conn->ntlmv2_session, &conn->negotiate_target, @@ -439,8 +447,10 @@ soup_auth_ntlm_authenticate (SoupAuth *auth, const char *username, priv->username = g_strdup (username); } - soup_ntlm_nt_hash (password, priv->nt_hash); - soup_ntlm_lanmanager_hash (password, priv->lm_hash); + if (strlen (password) < 256) { + soup_ntlm_nt_hash (password, priv->nt_hash); + soup_ntlm_lanmanager_hash (password, priv->lm_hash); + } priv->password_state = SOUP_NTLM_PASSWORD_PROVIDED; } @@ -606,7 +616,7 @@ soup_auth_ntlm_class_init (SoupAuthNTLMClass *auth_ntlm_class) } static void md4sum (const unsigned char *in, - int nbytes, + size_t nbytes, unsigned char digest[16]); typedef guint32 DES_KS[16][2]; /* Single-key DES key schedule */ @@ -652,7 +662,7 @@ soup_ntlm_nt_hash (const char *password, guchar hash[21]) { unsigned char *buf, *p; - p = buf = g_malloc (strlen (password) * 2); + p = buf = g_malloc_n (strlen (password), 2); while (*password) { *p++ = *password++; @@ -1091,15 +1101,16 @@ calc_response (const guchar *key, const guchar *plaintext, guchar *results) #define ROT(val, n) ( ((val) << (n)) | ((val) >> (32 - (n))) ) static void -md4sum (const unsigned char *in, int nbytes, unsigned char digest[16]) +md4sum (const unsigned char *in, size_t nbytes, unsigned char digest[16]) { unsigned char *M; guint32 A, B, C, D, AA, BB, CC, DD, X[16]; - int pbytes, nbits = nbytes * 8, i, j; + size_t pbytes, nbits = nbytes * 8; + int i, j; /* There is *always* padding of at least one bit. */ pbytes = ((119 - (nbytes % 64)) % 64) + 1; - M = alloca (nbytes + pbytes + 8); + M = g_malloc (nbytes + pbytes + 8); memcpy (M, in, nbytes); memset (M + nbytes, 0, pbytes + 8); M[nbytes] = 0x80; @@ -1199,6 +1210,8 @@ md4sum (const unsigned char *in, int nbytes, unsigned char digest[16]) digest[13] = (D >> 8) & 0xFF; digest[14] = (D >> 16) & 0xFF; digest[15] = (D >> 24) & 0xFF; + + g_free (M); } -- 2.52.0