From 9d0e261bbe4fc931d545ba91e1a9b769dc9acddd Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Mon, 20 Jan 2020 16:00:37 +0100 Subject: [PATCH] method_eap: Add EAP Success/Failure support Although, EapHost takes care for EAP Success and Failure packets for us, it does so for the outer-most method only. When using EAP inside a TLS tunnel, we are responsible for EAP Success and Failure packets ourselves. Signed-off-by: Simon Rozman --- EAPMethods/src/Main.cpp | 2 +- lib/EAPBase/include/Method.h | 4 ++- lib/EAPBase/src/Method.cpp | 63 ++++++++++++++++++++++++------------ 3 files changed, 47 insertions(+), 22 deletions(-) diff --git a/EAPMethods/src/Main.cpp b/EAPMethods/src/Main.cpp index 512b99f..3709a80 100644 --- a/EAPMethods/src/Main.cpp +++ b/EAPMethods/src/Main.cpp @@ -358,7 +358,7 @@ DWORD APIENTRY EapPeerProcessRequestPacket( *ppEapError = NULL; // Parameter check - if (!hSession || !pReceivedPacket || dwReceivedPacketSize < 6 || pReceivedPacket->Data[0] != EAPMETHOD_TYPE || !pEapOutput) + if (!hSession || !pReceivedPacket || dwReceivedPacketSize < sizeof(EapPacket) || pReceivedPacket->Data[0] != EAPMETHOD_TYPE || !pEapOutput) return dwResult = ERROR_INVALID_PARAMETER; try { diff --git a/lib/EAPBase/include/Method.h b/lib/EAPBase/include/Method.h index 51f986f..87d6085 100644 --- a/lib/EAPBase/include/Method.h +++ b/lib/EAPBase/include/Method.h @@ -319,9 +319,11 @@ namespace eap /// enum class phase_t { unknown = -1, ///< Unknown phase - identity = 0, ///< Send identity + init = 0, ///< Initialize + identity, ///< Send identity inner, ///< Send inner method response nak, ///< Send Legacy Nak response + finished, ///< EAP Success packet received } m_phase; ///< What phase is our communication at? }; diff --git a/lib/EAPBase/src/Method.cpp b/lib/EAPBase/src/Method.cpp index 0d50499..ce33a75 100644 --- a/lib/EAPBase/src/Method.cpp +++ b/lib/EAPBase/src/Method.cpp @@ -222,9 +222,11 @@ void eap::method_eap::begin_session( // Inner method can generate packets of up to 64kB (less the EAP packet header). // Initialize inner method with appropriately less packet size maximum. if (dwMaxSendPacketSize < sizeof(EapPacket)) - throw invalid_argument(string_printf(__FUNCTION__ " Maximum packet size too small (minimum: %zu, available: %u).", sizeof(EapPacket) + 1, dwMaxSendPacketSize)); + throw invalid_argument(string_printf(__FUNCTION__ " Maximum packet size too small (minimum: %zu, available: %u).", sizeof(EapPacket), dwMaxSendPacketSize)); assert(m_inner); m_inner->begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, std::min(dwMaxSendPacketSize, MAXWORD) - sizeof(EapPacket)); + + m_phase = phase_t::init; } @@ -232,33 +234,54 @@ EapPeerMethodResponseAction eap::method_eap::process_request_packet( _In_bytecount_(dwReceivedPacketSize) const void *pReceivedPacket, _In_ DWORD dwReceivedPacketSize) { - assert(dwReceivedPacketSize >= sizeof(EapPacket)); // Request packet should contain an EAP packet header at least. - auto hdr = reinterpret_cast(pReceivedPacket); + if (dwReceivedPacketSize < offsetof(EapPacket, Data)) + throw invalid_argument(string_printf(__FUNCTION__ " Incomplete EAP packet header (minimum: %zu, received: %u).", offsetof(EapPacket, Data), dwReceivedPacketSize)); - // This must be an EAP-Request packet. - if (hdr->Code != EapCodeRequest) - throw invalid_argument(string_printf(__FUNCTION__ " Unknown EAP packet received (expected: %u, received: %u).", EapCodeRequest, (int)hdr->Code)); + auto hdr = reinterpret_cast(pReceivedPacket); // Check packet size. DWORD size_packet = ntohs(*reinterpret_cast(hdr->Length)); if (size_packet > dwReceivedPacketSize) throw invalid_argument(string_printf(__FUNCTION__ " Incorrect EAP packet length (expected: %u, received: %u).", size_packet, dwReceivedPacketSize)); - // Save request packet ID to make matching response packet in get_response_packet() later. - m_id = hdr->Id; + switch (hdr->Code) { + case EapCodeRequest: + if (dwReceivedPacketSize < sizeof(EapPacket)) + throw invalid_argument(string_printf(__FUNCTION__ " Incomplete EAP packet (minimum: %zu, received: %u).", sizeof(EapPacket), dwReceivedPacketSize)); - if ((eap_type_t)hdr->Data[0] == eap_type_t::identity) { - // EAP Identity. Respond with identity. - m_phase = phase_t::identity; - return EapPeerMethodResponseActionSend; - } else if ((eap_type_t)hdr->Data[0] == m_eap_method) { - // Process the data with underlying method. - m_phase = phase_t::inner; - return method_tunnel::process_request_packet(hdr->Data + 1, size_packet - sizeof(EapPacket)); - } else { - // Unsupported EAP method. Respond with Legacy Nak. - m_phase = phase_t::nak; - return EapPeerMethodResponseActionSend; + // Save request packet ID to make matching response packet in get_response_packet() later. + m_id = hdr->Id; + + if ((eap_type_t)hdr->Data[0] == eap_type_t::identity) { + // EAP Identity. Respond with identity. + m_phase = phase_t::identity; + return EapPeerMethodResponseActionSend; + } else if ((eap_type_t)hdr->Data[0] == m_eap_method) { + // Process the data with underlying method. + m_phase = phase_t::inner; + return method_tunnel::process_request_packet(hdr->Data + 1, size_packet - sizeof(EapPacket)); + } else { + // Unsupported EAP method. Respond with Legacy Nak. + m_phase = phase_t::nak; + return EapPeerMethodResponseActionSend; + } + + // Check EAP Success/Failure packets for inner methods. + case EapCodeSuccess: + assert(size_packet == 4); + if (m_phase == phase_t::init) { + // Discard "canned" success packet. + return EapPeerMethodResponseActionDiscard; + } + m_phase = phase_t::finished; + return EapPeerMethodResponseActionResult; + + case EapCodeFailure: + assert(size_packet == 4); + throw invalid_argument(string_printf(__FUNCTION__ " EAP Failure packet received.")); + + default: + throw invalid_argument(string_printf(__FUNCTION__ " Unknown EAP packet received (expected: %u, received: %u).", EapCodeRequest, (int)hdr->Code)); } }