method_eap: Support EAP identity exchange
Although, EapHost takes care for EAP identity exchange for us, it does so for the outer-most method only. When using EAP inside a TLS tunnel, we are responsible for EAP identity exchange ourselves. Signed-off-by: Simon Rozman <simon@rozman.si>
This commit is contained in:
parent
1c295360fc
commit
e5e5f1c63e
@ -280,9 +280,10 @@ namespace eap
|
|||||||
///
|
///
|
||||||
/// \param[in] mod Module to use for global services
|
/// \param[in] mod Module to use for global services
|
||||||
/// \param[in] eap_method EAP method type
|
/// \param[in] eap_method EAP method type
|
||||||
|
/// \param[in] cred User credentials
|
||||||
/// \param[in] inner Inner method
|
/// \param[in] inner Inner method
|
||||||
///
|
///
|
||||||
method_eap(_In_ module &mod, _In_ winstd::eap_type_t eap_method, _In_ method *inner);
|
method_eap(_In_ module &mod, _In_ winstd::eap_type_t eap_method, _In_ credentials &cred, _In_ method *inner);
|
||||||
|
|
||||||
/// \name Session management
|
/// \name Session management
|
||||||
/// @{
|
/// @{
|
||||||
@ -310,8 +311,18 @@ namespace eap
|
|||||||
|
|
||||||
protected:
|
protected:
|
||||||
const winstd::eap_type_t m_eap_method; ///< EAP method type
|
const winstd::eap_type_t m_eap_method; ///< EAP method type
|
||||||
|
credentials &m_cred; ///< User credentials
|
||||||
unsigned char m_id; ///< Request packet ID
|
unsigned char m_id; ///< Request packet ID
|
||||||
bool m_send_nak; ///< Are we sending Legacy Nak response?
|
|
||||||
|
///
|
||||||
|
/// Communication phase
|
||||||
|
///
|
||||||
|
enum class phase_t {
|
||||||
|
unknown = -1, ///< Unknown phase
|
||||||
|
identity = 0, ///< Send identity
|
||||||
|
inner, ///< Send inner method response
|
||||||
|
nak, ///< Send Legacy Nak response
|
||||||
|
} m_phase; ///< What phase is our communication at?
|
||||||
};
|
};
|
||||||
|
|
||||||
/// @}
|
/// @}
|
||||||
|
@ -200,10 +200,11 @@ EapPeerMethodResponseAction eap::method_tunnel::set_response_attributes(_In_ con
|
|||||||
// eap::method_eap
|
// eap::method_eap
|
||||||
//////////////////////////////////////////////////////////////////////
|
//////////////////////////////////////////////////////////////////////
|
||||||
|
|
||||||
eap::method_eap::method_eap(_In_ module &mod, _In_ winstd::eap_type_t eap_method, _In_ method *inner) :
|
eap::method_eap::method_eap(_In_ module &mod, _In_ eap_type_t eap_method, _In_ credentials &cred, _In_ method *inner) :
|
||||||
m_eap_method(eap_method),
|
m_eap_method(eap_method),
|
||||||
|
m_cred(cred),
|
||||||
m_id(0),
|
m_id(0),
|
||||||
m_send_nak(false),
|
m_phase(phase_t::unknown),
|
||||||
method_tunnel(mod, inner)
|
method_tunnel(mod, inner)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
@ -246,14 +247,18 @@ EapPeerMethodResponseAction eap::method_eap::process_request_packet(
|
|||||||
// Save request packet ID to make matching response packet in get_response_packet() later.
|
// Save request packet ID to make matching response packet in get_response_packet() later.
|
||||||
m_id = hdr->Id;
|
m_id = hdr->Id;
|
||||||
|
|
||||||
if ((eap_type_t)hdr->Data[0] != m_eap_method) {
|
if ((eap_type_t)hdr->Data[0] == eap_type_t::identity) {
|
||||||
// Unsupported EAP method. Respond with Legacy Nak.
|
// EAP Identity. Respond with identity.
|
||||||
m_send_nak = true;
|
m_phase = phase_t::identity;
|
||||||
return EapPeerMethodResponseActionSend;
|
return EapPeerMethodResponseActionSend;
|
||||||
} else {
|
} else if ((eap_type_t)hdr->Data[0] == m_eap_method) {
|
||||||
// Process the data with underlying method.
|
// Process the data with underlying method.
|
||||||
m_send_nak = false;
|
m_phase = phase_t::inner;
|
||||||
return method_tunnel::process_request_packet(hdr->Data + 1, size_packet - sizeof(EapPacket));
|
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;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -270,14 +275,27 @@ void eap::method_eap::get_response_packet(
|
|||||||
hdr.Code = (BYTE)EapCodeResponse;
|
hdr.Code = (BYTE)EapCodeResponse;
|
||||||
hdr.Id = m_id;
|
hdr.Id = m_id;
|
||||||
|
|
||||||
if (!m_send_nak) {
|
switch (m_phase) {
|
||||||
|
case phase_t::identity: {
|
||||||
|
hdr.Data[0] = (BYTE)eap_type_t::identity;
|
||||||
|
|
||||||
|
// Convert identity to UTF-8.
|
||||||
|
sanitizing_string identity_utf8;
|
||||||
|
WideCharToMultiByte(CP_UTF8, 0, m_cred.get_identity(), identity_utf8, NULL, NULL);
|
||||||
|
packet.assign(identity_utf8.cbegin(), identity_utf8.cend());
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case phase_t::inner:
|
||||||
hdr.Data[0] = (BYTE)m_eap_method;
|
hdr.Data[0] = (BYTE)m_eap_method;
|
||||||
|
|
||||||
packet.reserve(size_max); // To avoid reallocation when inserting EAP packet header later.
|
packet.reserve(size_max); // To avoid reallocation when inserting EAP packet header later.
|
||||||
|
|
||||||
// Get data from underlying method.
|
// Get data from underlying method.
|
||||||
method_tunnel::get_response_packet(packet, size_max - sizeof(EapPacket));
|
method_tunnel::get_response_packet(packet, size_max - sizeof(EapPacket));
|
||||||
} else {
|
break;
|
||||||
|
|
||||||
|
case phase_t::nak: {
|
||||||
// Respond with Legacy Nak suggesting our EAP method to continue.
|
// Respond with Legacy Nak suggesting our EAP method to continue.
|
||||||
hdr.Data[0] = (BYTE)eap_type_t::nak;
|
hdr.Data[0] = (BYTE)eap_type_t::nak;
|
||||||
|
|
||||||
@ -289,6 +307,11 @@ void eap::method_eap::get_response_packet(
|
|||||||
|
|
||||||
// Data of Legacy Nak packet is a list of supported EAP types: our method alone.
|
// Data of Legacy Nak packet is a list of supported EAP types: our method alone.
|
||||||
packet.assign(1, (unsigned char)m_eap_method);
|
packet.assign(1, (unsigned char)m_eap_method);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
throw invalid_argument(string_printf(__FUNCTION__ " Unknown phase (phase %u).", m_phase));
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t size_packet = packet.size() + sizeof(EapPacket);
|
size_t size_packet = packet.size() + sizeof(EapPacket);
|
||||||
|
@ -121,7 +121,7 @@ void eap::peer_ttls::get_identity(
|
|||||||
} else {
|
} else {
|
||||||
// Per-machine authentication, cannot use UI.
|
// Per-machine authentication, cannot use UI.
|
||||||
throw win_runtime_error(ERROR_NO_SUCH_USER, __FUNCTION__ " Credentials for per-machine authentication not available.");
|
throw win_runtime_error(ERROR_NO_SUCH_USER, __FUNCTION__ " Credentials for per-machine authentication not available.");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Build our identity. ;)
|
// Build our identity. ;)
|
||||||
@ -138,7 +138,7 @@ void eap::peer_ttls::get_identity(
|
|||||||
|
|
||||||
void eap::peer_ttls::get_method_properties(
|
void eap::peer_ttls::get_method_properties(
|
||||||
_In_ DWORD dwVersion,
|
_In_ DWORD dwVersion,
|
||||||
_In_ DWORD dwFlags,
|
_In_ DWORD dwFlags,
|
||||||
_In_ HANDLE hUserImpersonationToken,
|
_In_ HANDLE hUserImpersonationToken,
|
||||||
_In_count_(dwConnectionDataSize) const BYTE *pConnectionData,
|
_In_count_(dwConnectionDataSize) const BYTE *pConnectionData,
|
||||||
_In_ DWORD dwConnectionDataSize,
|
_In_ DWORD dwConnectionDataSize,
|
||||||
@ -259,11 +259,11 @@ EAP_SESSION_HANDLE eap::peer_ttls::begin_session(
|
|||||||
case eap_type_t::legacy_mschapv2: meth_inner.reset(new method_mschapv2_diameter(*this, dynamic_cast<config_method_mschapv2&>(*cfg_inner), dynamic_cast<credentials_pass&>(*cred_inner))); break;
|
case eap_type_t::legacy_mschapv2: meth_inner.reset(new method_mschapv2_diameter(*this, dynamic_cast<config_method_mschapv2&>(*cfg_inner), dynamic_cast<credentials_pass&>(*cred_inner))); break;
|
||||||
case eap_type_t::mschapv2 : meth_inner.reset(
|
case eap_type_t::mschapv2 : meth_inner.reset(
|
||||||
new method_eapmsg (*this, cred_inner->get_identity().c_str(),
|
new method_eapmsg (*this, cred_inner->get_identity().c_str(),
|
||||||
new method_eap (*this, eap_type_t::mschapv2,
|
new method_eap (*this, eap_type_t::mschapv2, *cred_inner,
|
||||||
new method_mschapv2(*this, dynamic_cast<config_method_mschapv2&>(*cfg_inner), dynamic_cast<credentials_pass&>(*cred_inner))))); break;
|
new method_mschapv2(*this, dynamic_cast<config_method_mschapv2&>(*cfg_inner), dynamic_cast<credentials_pass&>(*cred_inner))))); break;
|
||||||
case eap_type_t::gtc : meth_inner.reset(
|
case eap_type_t::gtc : meth_inner.reset(
|
||||||
new method_eapmsg (*this, cred_inner->get_identity().c_str(),
|
new method_eapmsg (*this, cred_inner->get_identity().c_str(),
|
||||||
new method_eap (*this, eap_type_t::gtc,
|
new method_eap (*this, eap_type_t::gtc, *cred_inner,
|
||||||
new method_gtc (*this, dynamic_cast<config_method_eapgtc&>(*cfg_inner), dynamic_cast<credentials&>(*cred_inner))))); break;
|
new method_gtc (*this, dynamic_cast<config_method_eapgtc&>(*cfg_inner), dynamic_cast<credentials&>(*cred_inner))))); break;
|
||||||
default: throw invalid_argument(__FUNCTION__ " Unsupported inner authentication method.");
|
default: throw invalid_argument(__FUNCTION__ " Unsupported inner authentication method.");
|
||||||
}
|
}
|
||||||
@ -277,7 +277,7 @@ EAP_SESSION_HANDLE eap::peer_ttls::begin_session(
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
s->m_method.reset(
|
s->m_method.reset(
|
||||||
new method_eap (*this, eap_type_t::ttls,
|
new method_eap (*this, eap_type_t::ttls, *s->m_cred.m_cred,
|
||||||
new method_defrag(*this, 0, /* Schannel supports retrieving keying material for EAP-TTLSv0 only. */
|
new method_defrag(*this, 0, /* Schannel supports retrieving keying material for EAP-TTLSv0 only. */
|
||||||
new method_ttls (*this, *cfg_method, *dynamic_cast<credentials_ttls*>(s->m_cred.m_cred.get()), meth_inner.release()))));
|
new method_ttls (*this, *cfg_method, *dynamic_cast<credentials_ttls*>(s->m_cred.m_cred.get()), meth_inner.release()))));
|
||||||
|
|
||||||
@ -596,7 +596,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj)
|
|||||||
if (WaitForSingleObject(obj->m_abort, 5000) == WAIT_OBJECT_0) {
|
if (WaitForSingleObject(obj->m_abort, 5000) == WAIT_OBJECT_0) {
|
||||||
// Aborted.
|
// Aborted.
|
||||||
return 1;
|
return 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Prepare a list of certificates forming certificate chain.
|
// Prepare a list of certificates forming certificate chain.
|
||||||
list<cert_context> context_data;
|
list<cert_context> context_data;
|
||||||
@ -605,7 +605,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj)
|
|||||||
DWORD flags = 0;
|
DWORD flags = 0;
|
||||||
c = CertGetIssuerCertificateFromStore(obj->m_cert->hCertStore, context_data.back(), NULL, &flags);
|
c = CertGetIssuerCertificateFromStore(obj->m_cert->hCertStore, context_data.back(), NULL, &flags);
|
||||||
if (!c) break;
|
if (!c) break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Create an array of pointers to CERT_CONTEXT required by CertVerifyRevocation().
|
// Create an array of pointers to CERT_CONTEXT required by CertVerifyRevocation().
|
||||||
vector<PCERT_CONTEXT> context;
|
vector<PCERT_CONTEXT> context;
|
||||||
@ -623,7 +623,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj)
|
|||||||
if (!CertVerifyRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CERT_CONTEXT_REVOCATION_TYPE,
|
if (!CertVerifyRevocation(X509_ASN_ENCODING | PKCS_7_ASN_ENCODING, CERT_CONTEXT_REVOCATION_TYPE,
|
||||||
(DWORD)(c_end - c), reinterpret_cast<PVOID*>(&*c),
|
(DWORD)(c_end - c), reinterpret_cast<PVOID*>(&*c),
|
||||||
CERT_VERIFY_REV_CHAIN_FLAG, NULL, &status_rev))
|
CERT_VERIFY_REV_CHAIN_FLAG, NULL, &status_rev))
|
||||||
{
|
{
|
||||||
PCCERT_CONTEXT cert = *(c + status_rev.dwIndex);
|
PCCERT_CONTEXT cert = *(c + status_rev.dwIndex);
|
||||||
wstring subj;
|
wstring subj;
|
||||||
if (!CertGetNameStringW(cert, CERT_NAME_DNS_TYPE, CERT_NAME_STR_ENABLE_PUNYCODE_FLAG, NULL, subj))
|
if (!CertGetNameStringW(cert, CERT_NAME_DNS_TYPE, CERT_NAME_STR_ENABLE_PUNYCODE_FLAG, NULL, subj))
|
||||||
@ -639,7 +639,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj)
|
|||||||
// This really was an error, as it appeared before the root CA cerficate in the chain.
|
// This really was an error, as it appeared before the root CA cerficate in the chain.
|
||||||
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKE_SKIPPED, event_data((unsigned int)eap_type_t::ttls), event_data(subj), event_data::blank);
|
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKE_SKIPPED, event_data((unsigned int)eap_type_t::ttls), event_data(subj), event_data::blank);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case CRYPT_E_REVOKED:
|
case CRYPT_E_REVOKED:
|
||||||
// One of the certificates in the chain was revoked.
|
// One of the certificates in the chain was revoked.
|
||||||
@ -650,7 +650,7 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj)
|
|||||||
case CRL_REASON_CERTIFICATE_HOLD:
|
case CRL_REASON_CERTIFICATE_HOLD:
|
||||||
// The revocation was of administrative nature. No need to black-list.
|
// The revocation was of administrative nature. No need to black-list.
|
||||||
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKED1, event_data((unsigned int)eap_type_t::ttls), event_data(subj), event_data(status_rev.dwReason), event_data::blank);
|
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKED1, event_data((unsigned int)eap_type_t::ttls), event_data(subj), event_data(status_rev.dwReason), event_data::blank);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default: {
|
default: {
|
||||||
// One of the certificates in the chain was revoked as compromised. Black-list it.
|
// One of the certificates in the chain was revoked as compromised. Black-list it.
|
||||||
@ -669,18 +669,18 @@ DWORD WINAPI eap::peer_ttls::crl_checker::verify(_In_ crl_checker *obj)
|
|||||||
|
|
||||||
// Resume checking the rest of the chain.
|
// Resume checking the rest of the chain.
|
||||||
c += (size_t)status_rev.dwIndex + 1;
|
c += (size_t)status_rev.dwIndex + 1;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case ERROR_SUCCESS:
|
case ERROR_SUCCESS:
|
||||||
// Odd. CertVerifyRevocation() should return TRUE then. Nevertheless, we take this as a "yes".
|
// Odd. CertVerifyRevocation() should return TRUE then. Nevertheless, we take this as a "yes".
|
||||||
c = c_end;
|
c = c_end;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Checking one of the certificates in the chain for revocation failed. Resume checking the rest.
|
// Checking one of the certificates in the chain for revocation failed. Resume checking the rest.
|
||||||
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKE_FAILED, event_data((unsigned int)eap_type_t::ttls), event_data(subj), event_data(status_rev.dwError), event_data::blank);
|
obj->m_module.log_event(&EAPMETHOD_TLS_SERVER_CERT_REVOKE_FAILED, event_data((unsigned int)eap_type_t::ttls), event_data(subj), event_data(status_rev.dwError), event_data::blank);
|
||||||
c += (size_t)status_rev.dwIndex + 1;
|
c += (size_t)status_rev.dwIndex + 1;
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// Revocation check finished.
|
// Revocation check finished.
|
||||||
break;
|
break;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user