From 2041accecbfb855b2aa05d0cb9c03ca88382805e Mon Sep 17 00:00:00 2001 From: Simon Rozman Date: Mon, 24 Oct 2016 15:08:25 +0200 Subject: [PATCH] EAP-Message integration continues... --- lib/EAPMsg/include/Method.h | 107 +++++++++++++++- lib/EAPMsg/src/Method.cpp | 243 ++++++++++++++++++++++++++++++------ 2 files changed, 305 insertions(+), 45 deletions(-) diff --git a/lib/EAPMsg/include/Method.h b/lib/EAPMsg/include/Method.h index 61ccff6..57f2ed7 100644 --- a/lib/EAPMsg/include/Method.h +++ b/lib/EAPMsg/include/Method.h @@ -81,6 +81,13 @@ namespace eap _In_ HANDLE hTokenImpersonateUser, _In_opt_ DWORD dwMaxSendPacketSize = MAXDWORD); + /// + /// Ends an EAP authentication session for the EAP method. + /// + /// \sa [EapPeerEndSession function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363604.aspx) + /// + virtual void end_session(); + /// /// Processes a packet received by EapHost from a supplicant. /// @@ -91,15 +98,103 @@ namespace eap _In_ DWORD dwReceivedPacketSize, _Out_ EapPeerMethodOutput *pEapOutput); + /// + /// Obtains a response packet from the EAP method. + /// + /// \sa [EapPeerGetResponsePacket function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363610.aspx) + /// + virtual void get_response_packet( + _Inout_bytecap_(*dwSendPacketSize) void *pSendPacket, + _Inout_ DWORD *pdwSendPacketSize); + + /// + /// Obtains the result of an authentication session from the EAP method. + /// + /// \sa [EapPeerGetResult function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363611.aspx) + /// + virtual void get_result( + _In_ EapPeerMethodResultReason reason, + _Inout_ EapPeerMethodResult *pResult); + + /// @} + + /// \name User Interaction + /// @{ + + /// + /// Obtains the user interface context from the EAP method. + /// + /// \note This function is always followed by the `EapPeerInvokeInteractiveUI()` function, which is followed by the `EapPeerSetUIContext()` function. + /// + /// \sa [EapPeerGetUIContext function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363612.aspx) + /// + virtual void get_ui_context( + _Inout_ BYTE **ppUIContextData, + _Inout_ DWORD *pdwUIContextDataSize); + + /// + /// Provides a user interface context to the EAP method. + /// + /// \note This function is called after the UI has been raised through the `EapPeerGetUIContext()` function. + /// + /// \sa [EapPeerSetUIContext function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363626.aspx) + /// + virtual void set_ui_context( + _In_count_(dwUIContextDataSize) const BYTE *pUIContextData, + _In_ DWORD dwUIContextDataSize, + _Out_ EapPeerMethodOutput *pEapOutput); + + /// @} + + /// \name EAP Response Attributes + /// @{ + + /// + /// Obtains an array of EAP response attributes from the EAP method. + /// + /// \sa [EapPeerGetResponseAttributes function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363609.aspx) + /// + virtual void get_response_attributes(_Inout_ EapAttributes *pAttribs); + + /// + /// Provides an updated array of EAP response attributes to the EAP method. + /// + /// \sa [EapPeerSetResponseAttributes function](https://msdn.microsoft.com/en-us/library/windows/desktop/aa363625.aspx) + /// + virtual void set_response_attributes( + _In_ const EapAttributes *pAttribs, + _Out_ EapPeerMethodOutput *pEapOutput); + /// @} protected: - credentials_eapmsg &m_cred; ///< Method user credentials + /// + /// Converts EapHost peer action to output structure. + /// + /// \param[in ] action EapHost peer action + /// \param[out] pEapOutput EAP method output structure + /// + inline void action_to_output( + _In_ EapHostPeerResponseAction action, + _Out_ EapPeerMethodOutput *pEapOutput) + { + switch (action) { + case EapHostPeerResponseDiscard : pEapOutput->action = EapPeerMethodResponseActionDiscard ; break; + case EapHostPeerResponseSend : pEapOutput->action = EapPeerMethodResponseActionSend ; break; + case EapHostPeerResponseResult : pEapOutput->action = EapPeerMethodResponseActionResult ; break; + case EapHostPeerResponseInvokeUi : pEapOutput->action = EapPeerMethodResponseActionInvokeUI; break; + case EapHostPeerResponseRespond : pEapOutput->action = EapPeerMethodResponseActionRespond ; break; + case EapHostPeerResponseStartAuthentication: pEapOutput->action = EapPeerMethodResponseActionDiscard ; break; // The session could not be found. So the supplicant either needs to start session again with the same packet or discard the packet. + case EapHostPeerResponseNone : pEapOutput->action = EapPeerMethodResponseActionNone ; break; + default : throw std::invalid_argument(winstd::string_printf(__FUNCTION__ " Unknown action (%u).", action).c_str()); + } + pEapOutput->fAllowNotifications = TRUE; + } - enum { - phase_unknown = -1, ///< Unknown phase - phase_init = 0, ///< Handshake initialize - phase_finished, ///< Connection shut down - } m_phase; ///< What phase is our communication at? + protected: + EAP_SESSIONID m_session_id; ///< EAP session ID + + sanitizing_blob m_ctx_req_blob; ///< Inner UI context request + sanitizing_blob m_ctx_res_blob; ///< Inner UI context response }; } diff --git a/lib/EAPMsg/src/Method.cpp b/lib/EAPMsg/src/Method.cpp index 0221dc6..c63e8e4 100644 --- a/lib/EAPMsg/src/Method.cpp +++ b/lib/EAPMsg/src/Method.cpp @@ -29,16 +29,14 @@ using namespace winstd; ////////////////////////////////////////////////////////////////////// eap::method_eapmsg::method_eapmsg(_In_ module &module, _In_ config_method_eapmsg &cfg, _In_ credentials_eapmsg &cred) : - m_cred(cred), - m_phase(phase_unknown), + m_session_id(0), method_noneap(module, cfg, cred) { } eap::method_eapmsg::method_eapmsg(_Inout_ method_eapmsg &&other) : - m_cred ( other.m_cred ), - m_phase (std::move(other.m_phase )), + m_session_id (std::move(other.m_session_id)), method_noneap(std::move(other )) { } @@ -47,9 +45,8 @@ eap::method_eapmsg::method_eapmsg(_Inout_ method_eapmsg &&other) : eap::method_eapmsg& eap::method_eapmsg::operator=(_Inout_ method_eapmsg &&other) { if (this != std::addressof(other)) { - assert(std::addressof(m_cred) == std::addressof(other.m_cred)); // Move method with same credentials only! (method_noneap&)*this = std::move(other ); - m_phase = std::move(other.m_phase ); + m_session_id = std::move(other.m_session_id); } return *this; @@ -62,10 +59,46 @@ void eap::method_eapmsg::begin_session( _In_ HANDLE hTokenImpersonateUser, _In_opt_ DWORD dwMaxSendPacketSize) { - method_noneap::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize); + // Create EapHost peer session using available connection data (m_cfg) and user data (m_cred). + auto &cfg = dynamic_cast(m_cfg); + auto &cred = dynamic_cast(m_cred); + eap_error_runtime error; + DWORD dwResult = EapHostPeerBeginSession( + dwFlags, + cfg.m_type, + pAttributeArray, + hTokenImpersonateUser, + (DWORD)cfg.m_cfg_blob.size(), + cfg.m_cfg_blob.data(), + (DWORD)cred.m_cred_blob.size(), + cred.m_cred_blob.data(), + dwMaxSendPacketSize, + NULL, NULL, NULL, + &m_session_id, + &error._Myptr); + if (dwResult == ERROR_SUCCESS) { + // Session succesfully created. + method_noneap::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize); - m_module.log_event(&EAPMETHOD_METHOD_HANDSHAKE_START2, event_data((unsigned int)m_cfg.get_method_id()), event_data::blank); - m_phase = phase_init; + m_module.log_event(&EAPMETHOD_METHOD_HANDSHAKE_START2, event_data((unsigned int)m_cfg.get_method_id()), event_data::blank); + } else if (error) + throw eap_runtime_error(*error , __FUNCTION__ " EapHostPeerBeginSession failed."); + else + throw win_runtime_error(dwResult, __FUNCTION__ " EapHostPeerBeginSession failed."); +} + + +void eap::method_eapmsg::end_session() +{ + // End EapHost peer session. + eap_error_runtime error; + DWORD dwResult = EapHostPeerEndSession(m_session_id, &error._Myptr); + if (dwResult == ERROR_SUCCESS) { + // Session successfuly ended. + } else if (error) + throw eap_runtime_error(*error , __FUNCTION__ " EapHostPeerEndSession failed."); + else + throw win_runtime_error(dwResult, __FUNCTION__ " EapHostPeerEndSession failed."); } @@ -74,39 +107,171 @@ void eap::method_eapmsg::process_request_packet( _In_ DWORD dwReceivedPacketSize, _Out_ EapPeerMethodOutput *pEapOutput) { - UNREFERENCED_PARAMETER(pReceivedPacket); assert(pReceivedPacket || dwReceivedPacketSize == 0); assert(pEapOutput); m_module.log_event(&EAPMETHOD_PACKET_RECV, event_data((unsigned int)m_cfg.get_method_id()), event_data((unsigned int)dwReceivedPacketSize), event_data::blank); - // TODO: Finish! - //switch (m_phase) { - //case phase_init: { - // // Convert username and password to UTF-8. - // sanitizing_string identity_utf8, password_utf8; - // WideCharToMultiByte(CP_UTF8, 0, m_cred.m_identity.c_str(), (int)m_cred.m_identity.length(), identity_utf8, NULL, NULL); - // WideCharToMultiByte(CP_UTF8, 0, m_cred.m_password.c_str(), (int)m_cred.m_password.length(), password_utf8, NULL, NULL); - - // // EAPMsg passwords must be padded to 16B boundary according to RFC 5281. Will not add random extra padding here, as length obfuscation should be done by outer transport layers. - // size_t padding_password_ex = (16 - password_utf8.length()) % 16; - // password_utf8.append(padding_password_ex, 0); - - // m_packet_res.clear(); - - // // Diameter AVP (User-Name=1, User-Password=2) - // append_avp(1, diameter_avp_flag_mandatory, identity_utf8.data(), (unsigned int)identity_utf8.size()); - // append_avp(2, diameter_avp_flag_mandatory, password_utf8.data(), (unsigned int)password_utf8.size()); - - // m_phase = phase_finished; - // m_cfg.m_last_status = config_method::status_cred_invalid; // Blame credentials if we fail beyond this point. - // break; - //} - - //case phase_finished: - // break; - //} - - pEapOutput->fAllowNotifications = TRUE; - pEapOutput->action = EapPeerMethodResponseActionSend; + // Let EapHost peer process the packet. + EapHostPeerResponseAction action; + eap_error_runtime error; + DWORD dwResult = EapHostPeerProcessReceivedPacket( + m_session_id, + dwReceivedPacketSize, + reinterpret_cast(pReceivedPacket), + &action, + &error._Myptr); + if (dwResult == ERROR_SUCCESS) { + // Packet successfuly processed. + action_to_output(action, pEapOutput); + } else if (error) + throw eap_runtime_error(*error , __FUNCTION__ " EapHostPeerProcessReceivedPacket failed."); + else + throw win_runtime_error(dwResult, __FUNCTION__ " EapHostPeerProcessReceivedPacket failed."); +} + + +void eap::method_eapmsg::get_response_packet( + _Inout_bytecap_(*dwSendPacketSize) void *pSendPacket, + _Inout_ DWORD *pdwSendPacketSize) +{ + assert(pdwSendPacketSize); + assert(pSendPacket || !*pdwSendPacketSize); + + // Let EapHost peer prepare response packet. + DWORD size_max = *pdwSendPacketSize; + eap_blob_runtime packet; + eap_error_runtime error; + DWORD dwResult = EapHostPeerGetSendPacket( + m_session_id, + pdwSendPacketSize, + &packet._Myptr, + &error._Myptr); + if (dwResult == ERROR_SUCCESS) { + // Packet successfuly prepared. + memcpy_s(pSendPacket, size_max, packet.get(), *pdwSendPacketSize); + } else if (error) + throw eap_runtime_error(*error , __FUNCTION__ " EapHostPeerGetSendPacket failed."); + else + throw win_runtime_error(dwResult, __FUNCTION__ " EapHostPeerGetSendPacket failed."); +} + + +void eap::method_eapmsg::get_result( + _In_ EapPeerMethodResultReason reason, + _Inout_ EapPeerMethodResult *pResult) +{ + assert(pResult); + + if (reason == EapPeerMethodResultSuccess) { + // Let EapHost peer return result. + eap_error_runtime error; + EapHostPeerMethodResult result = {}; + DWORD dwResult = EapHostPeerGetResult( + m_session_id, + EapHostPeerMethodResultFromMethod, + &result, + &error._Myptr); + if (dwResult == ERROR_SUCCESS) { + // Result successfuly returned. + pResult->fIsSuccess = result.fIsSuccess; + pResult->dwFailureReasonCode = result.dwFailureReasonCode; + pResult->pAttribArray = result.pAttribArray; + pResult->pEapError = result.pEapError; + + if (result.fSaveConnectionData) + dynamic_cast(m_cfg).m_cfg_blob.assign(result.pConnectionData, result.pConnectionData + result.dwSizeofConnectionData); + + if (result.fSaveUserData) + dynamic_cast(m_cred).m_cred_blob.assign(result.pUserData, result.pUserData + result.dwSizeofUserData); + } else if (error) + throw eap_runtime_error(*error , __FUNCTION__ " EapHostPeerGetResult failed."); + else + throw win_runtime_error(dwResult, __FUNCTION__ " EapHostPeerGetResult failed."); + } +} + + +void eap::method_eapmsg::get_ui_context( + _Inout_ BYTE **ppUIContextData, + _Inout_ DWORD *pdwUIContextDataSize) +{ + // Get EapHost peer UI context data. + eap_error_runtime error; + DWORD dwResult = EapHostPeerGetUIContext( + m_session_id, + pdwUIContextDataSize, + ppUIContextData, + &error._Myptr); + if (dwResult == ERROR_SUCCESS) { + // UI context data successfuly returned. + } else if (error) + throw eap_runtime_error(*error , __FUNCTION__ " EapHostPeerGetUIContext failed."); + else + throw win_runtime_error(dwResult, __FUNCTION__ " EapHostPeerGetUIContext failed."); +} + + +void eap::method_eapmsg::set_ui_context( + _In_count_(dwUIContextDataSize) const BYTE *pUIContextData, + _In_ DWORD dwUIContextDataSize, + _Out_ EapPeerMethodOutput *pEapOutput) +{ + assert(pEapOutput); + + // Set EapHost peer UI context data. + EapHostPeerResponseAction action; + eap_error_runtime error; + DWORD dwResult = EapHostPeerSetUIContext( + m_session_id, + dwUIContextDataSize, + pUIContextData, + &action, + &error._Myptr); + if (dwResult == ERROR_SUCCESS) { + // UI context data successfuly returned. + action_to_output(action, pEapOutput); + } else if (error) + throw eap_runtime_error(*error , __FUNCTION__ " EapHostPeerSetUIContext failed."); + else + throw win_runtime_error(dwResult, __FUNCTION__ " EapHostPeerSetUIContext failed."); +} + + +void eap::method_eapmsg::get_response_attributes(_Inout_ EapAttributes *pAttribs) +{ + // Get response attributes from EapHost peer. + eap_error_runtime error; + DWORD dwResult = EapHostPeerGetResponseAttributes( + m_session_id, + pAttribs, + &error._Myptr); + if (dwResult == ERROR_SUCCESS) { + // Response attributes successfuly returned. + } else if (error) + throw eap_runtime_error(*error , __FUNCTION__ " EapHostPeerGetResponseAttributes failed."); + else + throw win_runtime_error(dwResult, __FUNCTION__ " EapHostPeerGetResponseAttributes failed."); +} + + +void eap::method_eapmsg::set_response_attributes( + _In_ const EapAttributes *pAttribs, + _Out_ EapPeerMethodOutput *pEapOutput) +{ + // Set response attributes for EapHost peer. + EapHostPeerResponseAction action; + eap_error_runtime error; + DWORD dwResult = EapHostPeerSetResponseAttributes( + m_session_id, + pAttribs, + &action, + &error._Myptr); + if (dwResult == ERROR_SUCCESS) { + // Response attributes successfuly set. + action_to_output(action, pEapOutput); + } else if (error) + throw eap_runtime_error(*error , __FUNCTION__ " EapHostPeerGetResponseAttributes failed."); + else + throw win_runtime_error(dwResult, __FUNCTION__ " EapHostPeerGetResponseAttributes failed."); }