]> git.ipfire.org Git - thirdparty/squid.git/commitdiff
Fix libntlmauth string parsing on big-endian machines (#2242)
authorJoshua Rogers <MegaManSec@users.noreply.github.com>
Sun, 19 Oct 2025 17:33:18 +0000 (17:33 +0000)
committerSquid Anubis <squid-anubis@squid-cache.org>
Sun, 19 Oct 2025 18:18:42 +0000 (18:18 +0000)
Prevent off-by-one reads in ntlm_fetch_string(); clamp copies too.
Convert flags with le32toh; validate lengths; ensure NUL-terminate.
cast ntlm string characters to unsigned char explicitly.
Return BlobError on oversized fields to avoid UB.

Combined these changes affect Big-Endian CPU architectures
which will fail to detect ASCII vs UTF encoding properly and
produce invalid strings (including user/password to compare).

lib/ntlmauth/ntlmauth.cc

index b5f5838eb327cc070e4a8a2111e1f94f7efcf502..4f194aa82e7f66e5bfc8b5bd74e3c6c379cd534c 100644 (file)
@@ -142,9 +142,10 @@ ntlm_fetch_string(const ntlmhdr *packet, const int32_t packet_size, const strhdr
         /* ASCII/OEM string */
         char *sc = rv.str;
 
-        for (; l>=0; ++sc, --l) {
-            if (*sc == '\0' || !xisprint(*sc)) {
-                fprintf(stderr, "ntlmssp: bad ascii: %04x\n", *sc);
+        for (; l>0; ++sc, --l) {
+            const auto c = static_cast<unsigned char>(*sc);
+            if (c == '\0' || !xisprint(c)) {
+                fprintf(stderr, "ntlmssp: bad ascii: %04x\n", c);
                 return rv;
             }
             ++rv.l;
@@ -252,7 +253,7 @@ ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const
         return NtlmError::ProtocolError;
     }
     debug("ntlm_unpack_auth: size of %d\n", size);
-    debug("ntlm_unpack_auth: flg %08x\n", auth->flags);
+    debug("ntlm_unpack_auth: flg %08x\n", le32toh(auth->flags));
     debug("ntlm_unpack_auth: lmr o(%d) l(%d)\n", le32toh(auth->lmresponse.offset), auth->lmresponse.len);
     debug("ntlm_unpack_auth: ntr o(%d) l(%d)\n", le32toh(auth->ntresponse.offset), auth->ntresponse.len);
     debug("ntlm_unpack_auth: dom o(%d) l(%d)\n", le32toh(auth->domain.offset), auth->domain.len);
@@ -260,24 +261,30 @@ ntlm_unpack_auth(const ntlm_authenticate *auth, char *user, char *domain, const
     debug("ntlm_unpack_auth: wst o(%d) l(%d)\n", le32toh(auth->workstation.offset), auth->workstation.len);
     debug("ntlm_unpack_auth: key o(%d) l(%d)\n", le32toh(auth->sessionkey.offset), auth->sessionkey.len);
 
-    rv = ntlm_fetch_string(&auth->hdr, size, &auth->domain, auth->flags);
-    if (rv.l > 0) {
-        memcpy(domain, rv.str, rv.l);
-        domain[rv.l] = '\0';
-        debug("ntlm_unpack_auth: Domain '%s' (len=%d).\n", domain, rv.l);
-    }
+    rv = ntlm_fetch_string(&auth->hdr, size, &auth->domain, le32toh(auth->flags));
     if (rv.l >= size) {
         debug("ntlm_unpack_auth: Domain length %d too big for %d byte packet.\n", rv.l, size);
         return NtlmError::BlobError;
     }
-
-    rv = ntlm_fetch_string(&auth->hdr, size, &auth->user, auth->flags);
     if (rv.l > 0) {
-        memcpy(user, rv.str, rv.l);
-        user[rv.l] = '\0';
-        debug("ntlm_unpack_auth: Username '%s' (len=%d).\n", user, rv.l);
-    } else
+        const size_t dcopy = rv.l > (NTLM_MAX_FIELD_LENGTH - 1) ? (NTLM_MAX_FIELD_LENGTH - 1) : static_cast<size_t>(rv.l);
+        memcpy(domain, rv.str, dcopy);
+        domain[dcopy] = '\0';
+        debug("ntlm_unpack_auth: Domain '%s' (len=%zu).\n", domain, dcopy);
+    }
+
+    rv = ntlm_fetch_string(&auth->hdr, size, &auth->user, le32toh(auth->flags));
+    if (rv.l <= 0)
         return NtlmError::LoginEror;
+    if (rv.l >= size) {
+        debug("ntlm_unpack_auth: Username length %d too big for %d byte packet.\n", rv.l, size);
+        return NtlmError::BlobError;
+    }
+
+    const size_t ucopy = rv.l > (NTLM_MAX_FIELD_LENGTH - 1) ? (NTLM_MAX_FIELD_LENGTH - 1) : static_cast<size_t>(rv.l);
+    memcpy(user, rv.str, ucopy);
+    user[ucopy] = '\0';
+    debug("ntlm_unpack_auth: Username '%s' (len=%zu).\n", user, ucopy);
 
     return NtlmError::None;
 }