From 61147a08aff300e630c0dd41550803f9c0657013 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Thu, 5 Feb 2026 13:26:24 -0800 Subject: [PATCH] Fix LOGON_CHALLENGE security flags buffer corruption and improve auth diagnostics Account for PIN/matrix/authenticator extra data in packet size calculation to prevent receive buffer corruption. Add hex dump of raw auth packets and show actual server error codes. --- src/auth/auth_handler.cpp | 27 +++++++++++++++++++++++++-- src/auth/srp.cpp | 17 ++++++++++++++++- src/network/tcp_socket.cpp | 23 ++++++++++++++--------- 3 files changed, 55 insertions(+), 12 deletions(-) diff --git a/src/auth/auth_handler.cpp b/src/auth/auth_handler.cpp index acdb5146..a8703119 100644 --- a/src/auth/auth_handler.cpp +++ b/src/auth/auth_handler.cpp @@ -2,6 +2,8 @@ #include "network/tcp_socket.hpp" #include "network/packet.hpp" #include "core/logger.hpp" +#include +#include namespace wowee { namespace auth { @@ -114,6 +116,16 @@ void AuthHandler::handleLogonChallengeResponse(network::Packet& packet) { return; } + if (response.securityFlags != 0) { + LOG_WARNING("Server sent security flags: 0x", std::hex, (int)response.securityFlags, std::dec); + if (response.securityFlags & 0x01) LOG_WARNING(" PIN required (not supported)"); + if (response.securityFlags & 0x02) LOG_WARNING(" Matrix card required (not supported)"); + if (response.securityFlags & 0x04) LOG_WARNING(" Authenticator required (not supported)"); + } + + LOG_INFO("Challenge: N=", response.N.size(), "B g=", response.g.size(), "B salt=", + response.salt.size(), "B secFlags=0x", std::hex, (int)response.securityFlags, std::dec); + // Feed SRP with server challenge data srp->feed(response.B, response.g, response.N, response.salt); @@ -145,7 +157,9 @@ void AuthHandler::handleLogonProofResponse(network::Packet& packet) { } if (!response.isSuccess()) { - fail("Login failed - incorrect username or password"); + std::string reason = "Login failed: "; + reason += getAuthResultString(static_cast(response.status)); + fail(reason); return; } @@ -227,7 +241,16 @@ void AuthHandler::handlePacket(network::Packet& packet) { AuthOpcode opcode = static_cast(opcodeValue); - LOG_DEBUG("Received auth packet, opcode: 0x", std::hex, (int)opcodeValue, std::dec); + // Hex dump first bytes for diagnostics + { + const auto& raw = packet.getData(); + std::ostringstream hs; + for (size_t i = 0; i < std::min(raw.size(), 40); ++i) + hs << std::hex << std::setfill('0') << std::setw(2) << (int)raw[i]; + if (raw.size() > 40) hs << "..."; + LOG_INFO("Auth pkt 0x", std::hex, (int)opcodeValue, std::dec, + " (", raw.size(), "B): ", hs.str()); + } switch (opcode) { case AuthOpcode::LOGON_CHALLENGE: diff --git a/src/auth/srp.cpp b/src/auth/srp.cpp index 09aca539..defb7a1f 100644 --- a/src/auth/srp.cpp +++ b/src/auth/srp.cpp @@ -3,6 +3,8 @@ #include "core/logger.hpp" #include #include +#include +#include namespace wowee { namespace auth { @@ -68,7 +70,20 @@ void SRP::feed(const std::vector& B_bytes, // 5. Compute proofs (M1, M2) computeProofs(stored_username); - LOG_INFO("SRP authentication data ready!"); + // Log key values for debugging auth issues + auto hexStr = [](const std::vector& v, size_t maxBytes = 8) -> std::string { + std::ostringstream ss; + for (size_t i = 0; i < std::min(v.size(), maxBytes); ++i) + ss << std::hex << std::setfill('0') << std::setw(2) << (int)v[i]; + if (v.size() > maxBytes) ss << "..."; + return ss.str(); + }; + auto A_wire = A.toArray(true, 32); + auto s_dbg = s.toArray(true); + auto B_dbg = B.toArray(true); + LOG_INFO("SRP ready: A=", hexStr(A_wire), " M1=", hexStr(M1), + " s_nat=", s_dbg.size(), " A_nat=", A.toArray(true).size(), + " B_nat=", B_dbg.size()); } std::vector SRP::computeAuthHash(const std::string& username, diff --git a/src/network/tcp_socket.cpp b/src/network/tcp_socket.cpp index 327c83f8..b87f134c 100644 --- a/src/network/tcp_socket.cpp +++ b/src/network/tcp_socket.cpp @@ -196,23 +196,28 @@ size_t TCPSocket::getExpectedPacketSize(uint8_t opcode) { switch (opcode) { case 0x00: // LOGON_CHALLENGE response - // Need to read second byte to determine success/failure + // Need to read status byte to determine success/failure if (receiveBuffer.size() >= 3) { uint8_t status = receiveBuffer[2]; if (status == 0x00) { - // Success - need to calculate full size - // Minimum: opcode(1) + unknown(1) + status(1) + B(32) + glen(1) + g(1) + Nlen(1) + N(32) + salt(32) + unk(16) + flags(1) - // With typical values: 1 + 1 + 1 + 32 + 1 + 1 + 1 + 32 + 32 + 16 + 1 = 119 bytes minimum - // But N is usually 256 bytes, so more like: 1 + 1 + 1 + 32 + 1 + 1 + 1 + 256 + 32 + 16 + 1 = 343 bytes - - // For safety, let's parse dynamically: + // Success: opcode(1) + unk(1) + status(1) + B(32) + gLen(1) + g(gLen) + + // nLen(1) + N(nLen) + salt(32) + crcHash(16) + securityFlags(1) + // + optional security flag data if (receiveBuffer.size() >= 36) { // enough to read g_len uint8_t gLen = receiveBuffer[35]; size_t minSize = 36 + gLen + 1; // up to N_len if (receiveBuffer.size() >= minSize) { uint8_t nLen = receiveBuffer[36 + gLen]; - size_t totalSize = 36 + gLen + 1 + nLen + 32 + 16 + 1; - return totalSize; + size_t baseSize = 36 + gLen + 1 + nLen + 32 + 16 + 1; + // Need to read securityFlags to account for extra data + if (receiveBuffer.size() >= baseSize) { + uint8_t secFlags = receiveBuffer[baseSize - 1]; + size_t extra = 0; + if (secFlags & 0x01) extra += 20; // PIN: seed(4) + salt(16) + if (secFlags & 0x02) extra += 12; // Matrix: w(1)+h(1)+digits(1)+challenges(1)+seed(8) + if (secFlags & 0x04) extra += 1; // Authenticator: required(1) + return baseSize + extra; + } } } return 0; // Need more data