GEANTLink/lib/MSCHAPv2/src/Method.cpp

208 lines
8.1 KiB
C++

/*
Copyright 2015-2016 Amebis
Copyright 2016 GÉANT
This file is part of GÉANTLink.
GÉANTLink is free software: you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
GÉANTLink is distributed in the hope that it will be useful, but
WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GÉANTLink. If not, see <http://www.gnu.org/licenses/>.
*/
#include "StdAfx.h"
using namespace std;
using namespace winstd;
//////////////////////////////////////////////////////////////////////
// eap::method_mschapv2
//////////////////////////////////////////////////////////////////////
eap::method_mschapv2::method_mschapv2(_In_ module &module, _In_ config_method_mschapv2 &cfg, _In_ credentials_mschapv2 &cred) :
m_cred(cred),
m_phase(phase_unknown),
m_phase_prev(phase_unknown),
method(module, cfg, cred)
{
}
eap::method_mschapv2::method_mschapv2(_Inout_ method_mschapv2 &&other) :
m_cred ( other.m_cred ),
m_packet_res(std::move(other.m_packet_res)),
m_phase (std::move(other.m_phase )),
m_phase_prev(std::move(other.m_phase_prev)),
method (std::move(other ))
{
}
eap::method_mschapv2& eap::method_mschapv2::operator=(_Inout_ method_mschapv2 &&other)
{
if (this != std::addressof(other)) {
assert(std::addressof(m_cred) == std::addressof(other.m_cred)); // Move method with same credentials only!
(method&)*this = std::move(other );
m_packet_res = std::move(other.m_packet_res);
m_phase = std::move(other.m_phase );
m_phase_prev = std::move(other.m_phase_prev);
}
return *this;
}
void eap::method_mschapv2::begin_session(
_In_ DWORD dwFlags,
_In_ const EapAttributes *pAttributeArray,
_In_ HANDLE hTokenImpersonateUser,
_In_opt_ DWORD dwMaxSendPacketSize)
{
method::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize);
m_module.log_event(&EAPMETHOD_METHOD_HANDSHAKE_START2, event_data((unsigned int)eap_type_legacy_mschapv2), event_data::blank);
m_phase = phase_init;
}
void eap::method_mschapv2::process_request_packet(
_In_bytecount_(dwReceivedPacketSize) const void *pReceivedPacket,
_In_ DWORD dwReceivedPacketSize,
_Inout_ EapPeerMethodOutput *pEapOutput)
{
assert(pReceivedPacket || dwReceivedPacketSize == 0);
assert(pEapOutput);
m_module.log_event(&EAPMETHOD_PACKET_RECV, event_data((unsigned int)eap_type_legacy_mschapv2), event_data((unsigned int)dwReceivedPacketSize), event_data::blank);
m_phase_prev = m_phase;
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);
// PAP 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);
size_t
size_identity = identity_utf8.length(),
size_password = password_utf8.length(),
padding_identity = (4 - size_identity ) % 4,
padding_password = (4 - password_utf8.length()) % 4,
size_identity_outer,
size_password_outer;
m_packet_res.clear();
m_packet_res.reserve(
(size_identity_outer =
sizeof(diameter_avp_header) + // Diameter header
size_identity) + // Identity
padding_identity + // Identity padding
(size_password_outer =
sizeof(diameter_avp_header) + // Diameter header
size_password) + // Password
padding_password); // Password padding
// Diameter AVP Code User-Name (0x00000001)
diameter_avp_header hdr;
*(unsigned int*)hdr.code = htonl(0x00000001);
hdr.flags = diameter_avp_flag_mandatory;
hton24((unsigned int)size_identity_outer, hdr.length);
m_packet_res.insert(m_packet_res.end(), (unsigned char*)&hdr, (unsigned char*)(&hdr + 1));
// Identity
m_packet_res.insert(m_packet_res.end(), identity_utf8.begin(), identity_utf8.end());
m_packet_res.insert(m_packet_res.end(), padding_identity, 0);
// Diameter AVP Code User-Password (0x00000002)
*(unsigned int*)hdr.code = htonl(0x00000002);
hton24((unsigned int)size_password_outer, hdr.length);
m_packet_res.insert(m_packet_res.end(), (unsigned char*)&hdr, (unsigned char*)(&hdr + 1));
// Password
m_packet_res.insert(m_packet_res.end(), password_utf8.begin(), password_utf8.end());
m_packet_res.insert(m_packet_res.end(), padding_password, 0);
m_phase = phase_finished;
break;
}
case phase_finished:
break;
}
pEapOutput->fAllowNotifications = TRUE;
pEapOutput->action = EapPeerMethodResponseActionSend;
}
void eap::method_mschapv2::get_response_packet(
_Inout_bytecap_(*dwSendPacketSize) void *pSendPacket,
_Inout_ DWORD *pdwSendPacketSize)
{
assert(pdwSendPacketSize);
assert(pSendPacket);
size_t size_packet = m_packet_res.size();
if (size_packet > *pdwSendPacketSize)
throw invalid_argument(string_printf(__FUNCTION__ " This method does not support packet fragmentation, but the data size is too big to fit in one packet (packet: %u, maximum: %u).", size_packet, *pdwSendPacketSize).c_str());
memcpy(pSendPacket, m_packet_res.data(), size_packet);
*pdwSendPacketSize = (DWORD)size_packet;
m_packet_res.clear();
}
void eap::method_mschapv2::get_result(
_In_ EapPeerMethodResultReason reason,
_Inout_ EapPeerMethodResult *ppResult)
{
assert(ppResult);
switch (reason) {
case EapPeerMethodResultSuccess: {
m_module.log_event(&EAPMETHOD_METHOD_SUCCESS, event_data((unsigned int)eap_type_legacy_mschapv2), event_data::blank);
m_cfg.m_auth_failed = false;
ppResult->fIsSuccess = TRUE;
ppResult->dwFailureReasonCode = ERROR_SUCCESS;
break;
}
case EapPeerMethodResultFailure:
m_module.log_event(
m_phase_prev < phase_finished ? &EAPMETHOD_METHOD_FAILURE_INIT : &EAPMETHOD_METHOD_FAILURE,
event_data((unsigned int)eap_type_legacy_mschapv2), event_data::blank);
// Mark credentials as failed, so GUI can re-prompt user.
// But be careful: do so only after credentials were actually tried.
m_cfg.m_auth_failed = m_phase == phase_finished;
// Do not report failure to EapHost, as it will not save updated configuration then. But we need it to save it, to alert user on next connection attempt.
// EapHost is well aware of the failed condition.
//ppResult->fIsSuccess = FALSE;
//ppResult->dwFailureReasonCode = EAP_E_AUTHENTICATION_FAILED;
break;
default:
throw win_runtime_error(ERROR_NOT_SUPPORTED, __FUNCTION__ " Not supported.");
}
// Always ask EAP host to save the connection data.
ppResult->fSaveConnectionData = TRUE;
}