354 lines
11 KiB
C++
354 lines
11 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
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
eap::method::method(_In_ module &mod) :
|
|
m_module(mod)
|
|
{
|
|
}
|
|
|
|
|
|
eap::method::method(_Inout_ method &&other) :
|
|
m_module(other.m_module)
|
|
{
|
|
}
|
|
|
|
|
|
eap::method& eap::method::operator=(_Inout_ method &&other)
|
|
{
|
|
if (this != std::addressof(other)) {
|
|
assert(std::addressof(m_module) == std::addressof(other.m_module)); // Move method within same module only!
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
void eap::method::begin_session(
|
|
_In_ DWORD dwFlags,
|
|
_In_ const EapAttributes *pAttributeArray,
|
|
_In_ HANDLE hTokenImpersonateUser,
|
|
_In_opt_ DWORD dwMaxSendPacketSize)
|
|
{
|
|
UNREFERENCED_PARAMETER(dwFlags);
|
|
UNREFERENCED_PARAMETER(pAttributeArray);
|
|
UNREFERENCED_PARAMETER(hTokenImpersonateUser);
|
|
UNREFERENCED_PARAMETER(dwMaxSendPacketSize);
|
|
}
|
|
|
|
|
|
void eap::method::end_session()
|
|
{
|
|
}
|
|
|
|
|
|
void eap::method::get_result(
|
|
_In_ EapPeerMethodResultReason reason,
|
|
_Inout_ EapPeerMethodResult *pResult)
|
|
{
|
|
UNREFERENCED_PARAMETER(reason);
|
|
UNREFERENCED_PARAMETER(pResult);
|
|
}
|
|
|
|
|
|
void eap::method::get_ui_context(_Out_ sanitizing_blob &context_data)
|
|
{
|
|
// Default implementation returns blank context data.
|
|
context_data.clear();
|
|
}
|
|
|
|
|
|
EapPeerMethodResponseAction eap::method::set_ui_context(
|
|
_In_count_(dwUIContextDataSize) const BYTE *pUIContextData,
|
|
_In_ DWORD dwUIContextDataSize)
|
|
{
|
|
UNREFERENCED_PARAMETER(pUIContextData);
|
|
UNREFERENCED_PARAMETER(dwUIContextDataSize);
|
|
|
|
// Default implementation does nothing with context data.
|
|
return EapPeerMethodResponseActionNone;
|
|
}
|
|
|
|
|
|
void eap::method::get_response_attributes(_Inout_ EapAttributes *pAttribs)
|
|
{
|
|
assert(pAttribs);
|
|
|
|
// Default implementation returns no EAP attributes.
|
|
pAttribs->dwNumberOfAttributes = 0;
|
|
pAttribs->pAttribs = NULL;
|
|
}
|
|
|
|
|
|
EapPeerMethodResponseAction eap::method::set_response_attributes(_In_ const EapAttributes *pAttribs)
|
|
{
|
|
UNREFERENCED_PARAMETER(pAttribs);
|
|
|
|
// Default implementation does nothing with EAP attributes.
|
|
return EapPeerMethodResponseActionNone;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// eap::method_tunnel
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
eap::method_tunnel::method_tunnel(_In_ module &mod, _In_ method *inner) :
|
|
m_inner(inner),
|
|
method(mod)
|
|
{
|
|
}
|
|
|
|
|
|
eap::method_tunnel::method_tunnel(_Inout_ method_tunnel &&other) :
|
|
m_inner(std::move(other.m_inner)),
|
|
method (std::move(other ))
|
|
{
|
|
}
|
|
|
|
|
|
eap::method_tunnel& eap::method_tunnel::operator=(_Inout_ method_tunnel &&other)
|
|
{
|
|
if (this != std::addressof(other)) {
|
|
(method&)*this = std::move(other );
|
|
m_inner = std::move(other.m_inner);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
void eap::method_tunnel::begin_session(
|
|
_In_ DWORD dwFlags,
|
|
_In_ const EapAttributes *pAttributeArray,
|
|
_In_ HANDLE hTokenImpersonateUser,
|
|
_In_opt_ DWORD dwMaxSendPacketSize)
|
|
{
|
|
method::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize);
|
|
|
|
assert(m_inner);
|
|
m_inner->begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize);
|
|
}
|
|
|
|
|
|
void eap::method_tunnel::end_session()
|
|
{
|
|
assert(m_inner);
|
|
m_inner->end_session();
|
|
|
|
method::end_session();
|
|
}
|
|
|
|
|
|
EapPeerMethodResponseAction eap::method_tunnel::process_request_packet(
|
|
_In_bytecount_(dwReceivedPacketSize) const void *pReceivedPacket,
|
|
_In_ DWORD dwReceivedPacketSize)
|
|
{
|
|
assert(m_inner);
|
|
return m_inner->process_request_packet(pReceivedPacket, dwReceivedPacketSize);
|
|
}
|
|
|
|
|
|
void eap::method_tunnel::get_response_packet(
|
|
_Out_ sanitizing_blob &packet,
|
|
_In_opt_ DWORD size_max)
|
|
{
|
|
assert(m_inner);
|
|
m_inner->get_response_packet(packet, size_max);
|
|
}
|
|
|
|
|
|
void eap::method_tunnel::get_result(
|
|
_In_ EapPeerMethodResultReason reason,
|
|
_Inout_ EapPeerMethodResult *pResult)
|
|
{
|
|
assert(m_inner);
|
|
m_inner->get_result(reason, pResult);
|
|
}
|
|
|
|
|
|
void eap::method_tunnel::get_ui_context(_Out_ sanitizing_blob &context_data)
|
|
{
|
|
assert(m_inner);
|
|
|
|
// Default implementation forwards UI context handling to the inner method.
|
|
m_inner->get_ui_context(context_data);
|
|
}
|
|
|
|
|
|
EapPeerMethodResponseAction eap::method_tunnel::set_ui_context(
|
|
_In_count_(dwUIContextDataSize) const BYTE *pUIContextData,
|
|
_In_ DWORD dwUIContextDataSize)
|
|
{
|
|
assert(m_inner);
|
|
|
|
// Default implementation forwards UI context handling to the inner method.
|
|
return m_inner->set_ui_context(pUIContextData, dwUIContextDataSize);
|
|
}
|
|
|
|
|
|
void eap::method_tunnel::get_response_attributes(_Inout_ EapAttributes *pAttribs)
|
|
{
|
|
assert(m_inner);
|
|
m_inner->get_response_attributes(pAttribs);
|
|
}
|
|
|
|
|
|
EapPeerMethodResponseAction eap::method_tunnel::set_response_attributes(_In_ const EapAttributes *pAttribs)
|
|
{
|
|
assert(m_inner);
|
|
return m_inner->set_response_attributes(pAttribs);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////
|
|
// eap::method_eap
|
|
//////////////////////////////////////////////////////////////////////
|
|
|
|
eap::method_eap::method_eap(_In_ module &mod, _In_ winstd::eap_type_t eap_method, _In_ method *inner) :
|
|
m_eap_method(eap_method),
|
|
m_id(0),
|
|
m_send_nak(false),
|
|
method_tunnel(mod, inner)
|
|
{
|
|
}
|
|
|
|
|
|
eap::method_eap::method_eap(_Inout_ method_eap &&other) :
|
|
m_eap_method (std::move(other.m_eap_method)),
|
|
m_id (std::move(other.m_id )),
|
|
m_send_nak (std::move(other.m_send_nak )),
|
|
method_tunnel(std::move(other ))
|
|
{
|
|
}
|
|
|
|
|
|
eap::method_eap& eap::method_eap::operator=(_Inout_ method_eap &&other)
|
|
{
|
|
if (this != std::addressof(other)) {
|
|
assert(m_eap_method == other.m_eap_method); // Move method within same EAP method type only!
|
|
(method_tunnel&)*this = std::move(other );
|
|
m_id = std::move(other.m_id );
|
|
m_send_nak = std::move(other.m_send_nak);
|
|
}
|
|
|
|
return *this;
|
|
}
|
|
|
|
|
|
void eap::method_eap::begin_session(
|
|
_In_ DWORD dwFlags,
|
|
_In_ const EapAttributes *pAttributeArray,
|
|
_In_ HANDLE hTokenImpersonateUser,
|
|
_In_opt_ DWORD dwMaxSendPacketSize)
|
|
{
|
|
// Initialize tunnel method session only.
|
|
method::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize);
|
|
|
|
// 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: %u, available: %u).", sizeof(EapPacket) + 1, dwMaxSendPacketSize));
|
|
assert(m_inner);
|
|
m_inner->begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, std::min<DWORD>(dwMaxSendPacketSize, MAXWORD) - sizeof(EapPacket));
|
|
}
|
|
|
|
|
|
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<const EapPacket*>(pReceivedPacket);
|
|
|
|
// 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));
|
|
|
|
// Check packet size.
|
|
DWORD size_packet = ntohs(*reinterpret_cast<const unsigned short*>(hdr->Length));
|
|
if (size_packet > dwReceivedPacketSize)
|
|
throw invalid_argument(string_printf(__FUNCTION__ " Incorrect EAP packet length (expected: %uB, received: %uB).", size_packet, dwReceivedPacketSize));
|
|
|
|
// Save request packet ID to make matching response packet in get_response_packet() later.
|
|
m_id = hdr->Id;
|
|
|
|
if (hdr->Data[0] != m_eap_method) {
|
|
// Unsupported EAP method. Respond with Legacy Nak.
|
|
m_send_nak = true;
|
|
return EapPeerMethodResponseActionSend;
|
|
} else {
|
|
// Process the data with underlying method.
|
|
m_send_nak = false;
|
|
return method_tunnel::process_request_packet(hdr->Data + 1, size_packet - sizeof(EapPacket));
|
|
}
|
|
}
|
|
|
|
|
|
void eap::method_eap::get_response_packet(
|
|
_Out_ sanitizing_blob &packet,
|
|
_In_opt_ DWORD size_max)
|
|
{
|
|
assert(size_max >= sizeof(EapPacket)); // We should be able to respond with at least an EAP packet header.
|
|
if (size_max > MAXWORD) size_max = MAXWORD; // EAP packets maximum size is 64kB.
|
|
|
|
// Prepare EAP packet header.
|
|
EapPacket hdr;
|
|
hdr.Code = (BYTE)EapCodeResponse;
|
|
hdr.Id = m_id;
|
|
|
|
if (!m_send_nak) {
|
|
hdr.Data[0] = m_eap_method;
|
|
|
|
packet.reserve(size_max); // To avoid reallocation when inserting EAP packet header later.
|
|
|
|
// Get data from underlying method.
|
|
method_tunnel::get_response_packet(packet, size_max - sizeof(EapPacket));
|
|
} else {
|
|
// Respond with Legacy Nak suggesting our EAP method to continue.
|
|
hdr.Data[0] = eap_type_nak;
|
|
|
|
// Check packet size. We will suggest one EAP method alone, so we need one byte for data.
|
|
size_t size_packet = sizeof(EapPacket) + 1;
|
|
if (size_packet > size_max)
|
|
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, size_max));
|
|
packet.reserve(size_packet); // To avoid reallocation when inserting EAP packet header later.
|
|
|
|
// Data of Legacy Nak packet is a list of supported EAP types: our method alone.
|
|
packet.assign(1, m_eap_method);
|
|
}
|
|
|
|
size_t size_packet = packet.size() + sizeof(EapPacket);
|
|
assert(size_packet <= MAXWORD); // Packets spanning over 64kB are not supported.
|
|
*reinterpret_cast<unsigned short*>(hdr.Length) = htons((unsigned short)size_packet);
|
|
|
|
// Insert EAP packet header before data.
|
|
packet.insert(packet.begin(), reinterpret_cast<const unsigned char*>(&hdr), reinterpret_cast<const unsigned char*>(&hdr + 1));
|
|
}
|