MSCHAPv2 almost finished...
This commit is contained in:
parent
b2382a0bdb
commit
d83f5422d7
@ -445,6 +445,15 @@ inline unsigned __int64 htonll(unsigned __int64 val);
|
||||
///
|
||||
inline void hton24(_In_ unsigned int val, _Out_ unsigned char out[3]);
|
||||
|
||||
///
|
||||
/// Converts an 24-bit integer from TCP/IP network to host byte order.
|
||||
///
|
||||
/// \param[in] val A 24-bit unsigned number in network byte order
|
||||
///
|
||||
/// \returns A 24-bit unsigned number in host byte order
|
||||
///
|
||||
inline unsigned int ntoh24(_In_ const unsigned char val[3]);
|
||||
|
||||
#pragma once
|
||||
|
||||
|
||||
@ -1113,4 +1122,13 @@ inline void hton24(_In_ unsigned int val, _Out_ unsigned char out[3])
|
||||
out[2] = (val ) & 0xff;
|
||||
}
|
||||
|
||||
|
||||
inline unsigned int ntoh24(_In_ const unsigned char val[3])
|
||||
{
|
||||
return
|
||||
(((unsigned int)val[0]) << 16) |
|
||||
(((unsigned int)val[1]) << 8) |
|
||||
(((unsigned int)val[2]) );
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -103,15 +103,12 @@ eap::challenge_hash::challenge_hash(
|
||||
_In_ const challenge_mschapv2 &challenge_client,
|
||||
_In_z_ const char *username)
|
||||
{
|
||||
const char *domain = strchr(username, '@');
|
||||
size_t len_username = domain ? domain - username : strlen(username);
|
||||
|
||||
crypt_hash hash;
|
||||
if (!hash.create(cp, CALG_SHA))
|
||||
throw win_runtime_error(__FUNCTION__ " Creating SHA hash failed.");
|
||||
if (!CryptHashData(hash, (const BYTE*)&challenge_client, (DWORD)sizeof(challenge_client), 0) ||
|
||||
!CryptHashData(hash, (const BYTE*)&challenge_server, (DWORD)sizeof(challenge_server), 0) ||
|
||||
!CryptHashData(hash, (const BYTE*)username , (DWORD)len_username , 0))
|
||||
!CryptHashData(hash, (const BYTE*)username , (DWORD)strlen(username) , 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
unsigned char hash_val[20];
|
||||
DWORD size_hash_val = sizeof(hash_val);
|
||||
|
@ -100,8 +100,6 @@ void eap::method_mschapv2::process_request_packet(
|
||||
|
||||
m_module.log_event(&EAPMETHOD_PACKET_RECV, event_data((unsigned int)eap_type_legacy_mschapv2), event_data((unsigned int)dwReceivedPacketSize), event_data::blank);
|
||||
|
||||
process_packet(pReceivedPacket, dwReceivedPacketSize);
|
||||
|
||||
m_phase_prev = m_phase;
|
||||
switch (m_phase) {
|
||||
case phase_init: {
|
||||
@ -109,8 +107,10 @@ void eap::method_mschapv2::process_request_packet(
|
||||
sanitizing_string identity_utf8;
|
||||
WideCharToMultiByte(CP_UTF8, 0, m_cred.m_identity.c_str(), (int)m_cred.m_identity.length(), identity_utf8, NULL, NULL);
|
||||
|
||||
// Calculate Peer-Challenge and Response
|
||||
// Randomize Peer-Challenge
|
||||
m_challenge_client.randomize(m_cp);
|
||||
|
||||
// Calculate NT-Response
|
||||
m_nt_resp = nt_response(m_cp, m_challenge_server, m_challenge_client, identity_utf8.c_str(), m_cred.m_password.c_str());
|
||||
|
||||
// Prepare MS-CHAP2-Response
|
||||
@ -128,16 +128,18 @@ void eap::method_mschapv2::process_request_packet(
|
||||
response.insert(response.end(), (unsigned char*)&m_nt_resp, (unsigned char*)(&m_nt_resp + 1)); // NT-Response
|
||||
|
||||
// Diameter AVP (User-Name=1, MS-CHAP-Challenge=11/311, MS-CHAP2-Response=25/311)
|
||||
append_avp( 1, diameter_avp_flag_mandatory, identity_utf8.data(), (unsigned int )identity_utf8.size() );
|
||||
append_avp(11, 311, diameter_avp_flag_mandatory, (unsigned char*)&m_challenge_server , (unsigned char)sizeof(m_challenge_server));
|
||||
append_avp(25, 311, diameter_avp_flag_mandatory, response.data() , (unsigned char)response.size() );
|
||||
append_avp( 1, diameter_avp_flag_mandatory, identity_utf8.data(), (unsigned int)identity_utf8.size() );
|
||||
append_avp(11, 311, diameter_avp_flag_mandatory, (unsigned char*)&m_challenge_server , (unsigned int)sizeof(m_challenge_server));
|
||||
append_avp(25, 311, diameter_avp_flag_mandatory, response.data() , (unsigned int)response.size() );
|
||||
|
||||
m_phase = phase_challenge_server;
|
||||
break;
|
||||
}
|
||||
|
||||
case phase_challenge_server: {
|
||||
// We do not need to do anything.
|
||||
process_packet(pReceivedPacket, dwReceivedPacketSize);
|
||||
if (m_success)
|
||||
m_phase = phase_finished;
|
||||
break;
|
||||
}
|
||||
|
||||
@ -214,20 +216,29 @@ void eap::method_mschapv2::process_packet(_In_bytecount_(size_pck) const void *_
|
||||
vendor = 0;
|
||||
msg = (const unsigned char*)(hdr + 1);
|
||||
}
|
||||
const unsigned char *msg_end = pck + ntohs(*(unsigned short*)hdr->length);
|
||||
unsigned int length = ntoh24(hdr->length);
|
||||
const unsigned char
|
||||
*msg_end = pck + length,
|
||||
*msg_next = msg_end + (4 - length) % 4;
|
||||
if (msg_end > pck_end)
|
||||
throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message data.");
|
||||
|
||||
if (code == 26 && vendor == 311) {
|
||||
// MS-CHAP2-Success
|
||||
MultiByteToWideChar(CP_UTF8, 0, (const char*)msg, (int)(msg_end - msg), msg_w);
|
||||
if (msg[0] != m_ident)
|
||||
throw invalid_argument(string_printf(__FUNCTION__ " Wrong MSCHAPv2 ident (expected: %u, received: %u).", m_ident, msg[0]).c_str());
|
||||
const char *str = (const char*)(msg + 1);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str, (int)((const char*)msg_end - str), msg_w);
|
||||
int argc;
|
||||
unique_ptr<LPWSTR[], LocalFree_delete<LPWSTR[]> > argv(CommandLineToArgvW(msg_w.c_str(), &argc));
|
||||
if (!argv) argc = 0;
|
||||
process_success(argc, (const wchar_t**)argv.get());
|
||||
} else if (code == 2 && vendor == 311) {
|
||||
// MS-CHAP2-Error
|
||||
MultiByteToWideChar(CP_UTF8, 0, (const char*)msg, (int)(msg_end - msg), msg_w);
|
||||
if (msg[0] != m_ident)
|
||||
throw invalid_argument(string_printf(__FUNCTION__ " Wrong MSCHAPv2 ident (expected: %u, received: %u).", m_ident, msg[0]).c_str());
|
||||
const char *str = (const char*)(msg + 1);
|
||||
MultiByteToWideChar(CP_UTF8, 0, str, (int)((const char*)msg_end - str), msg_w);
|
||||
int argc;
|
||||
unique_ptr<LPWSTR[], LocalFree_delete<LPWSTR[]> > argv(CommandLineToArgvW(msg_w.c_str(), &argc));
|
||||
if (!argv) argc = 0;
|
||||
@ -235,7 +246,7 @@ void eap::method_mschapv2::process_packet(_In_bytecount_(size_pck) const void *_
|
||||
} else if (hdr->flags & diameter_avp_flag_mandatory)
|
||||
throw win_runtime_error(ERROR_NOT_SUPPORTED, string_printf(__FUNCTION__ " Server sent mandatory Diameter AVP we do not support (code: %u, vendor: %u).", code, vendor).c_str());
|
||||
|
||||
pck = msg_end;
|
||||
pck = msg_next;
|
||||
}
|
||||
}
|
||||
|
||||
@ -258,7 +269,7 @@ void eap::method_mschapv2::process_success(_In_ int argc, _In_count_(argc) const
|
||||
authenticator_response resp_exp(m_cp, m_challenge_server, m_challenge_client, identity_utf8.c_str(), m_cred.m_password.c_str(), m_nt_resp);
|
||||
|
||||
// Compare against provided authemticator response.
|
||||
if (resp.size() != sizeof(resp_exp) || memcpy(resp.data(), &resp_exp, sizeof(resp_exp)) != 0)
|
||||
if (resp.size() != sizeof(resp_exp) || memcmp(resp.data(), &resp_exp, sizeof(resp_exp)) != 0)
|
||||
throw invalid_argument(__FUNCTION__ " MS-CHAP2-Success authentication response string failed.");
|
||||
|
||||
m_success = true;
|
||||
|
@ -235,6 +235,11 @@ namespace eap
|
||||
///
|
||||
virtual void derive_msk();
|
||||
|
||||
///
|
||||
/// Generates keying material for inner authentication
|
||||
///
|
||||
virtual void derive_challenge();
|
||||
|
||||
/// @}
|
||||
|
||||
#if EAP_TLS < EAP_TLS_SCHANNEL
|
||||
|
@ -98,31 +98,6 @@ namespace eap
|
||||
class packet_tls;
|
||||
}
|
||||
|
||||
/////
|
||||
///// Packs a TLS connection state
|
||||
/////
|
||||
///// \param[inout] cursor Memory cursor
|
||||
///// \param[in] val Variable with data to pack
|
||||
/////
|
||||
//inline void operator<<(_Inout_ eap::cursor_out &cursor, _In_ const eap::tls_conn_state &val);
|
||||
//
|
||||
/////
|
||||
///// Returns packed size of TLS connection state
|
||||
/////
|
||||
///// \param[in] val Data to pack
|
||||
/////
|
||||
///// \returns Size of data when packed (in bytes)
|
||||
/////
|
||||
//inline size_t pksizeof(_In_ const eap::tls_conn_state &val);
|
||||
//
|
||||
/////
|
||||
///// Unpacks a TLS connection state
|
||||
/////
|
||||
///// \param[inout] cursor Memory cursor
|
||||
///// \param[out] val Variable to receive unpacked value
|
||||
/////
|
||||
//inline void operator>>(_Inout_ eap::cursor_in &cursor, _Out_ eap::tls_conn_state &val);
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
|
@ -435,6 +435,9 @@ void eap::method_tls::process_request_packet(
|
||||
sanitizing_blob msg_finished(make_message(tls_message_type_handshake, make_finished()));
|
||||
m_packet_res.m_data.insert(m_packet_res.m_data.end(), msg_finished.begin(), msg_finished.end());
|
||||
|
||||
// Derive challenge for inner authentication (if any).
|
||||
derive_challenge();
|
||||
|
||||
if (m_handshake[tls_handshake_type_finished]) {
|
||||
// Go to application data phase. And allow piggybacking of the first data message.
|
||||
m_phase = phase_application_data;
|
||||
@ -831,6 +834,12 @@ void eap::method_tls::derive_msk()
|
||||
_key_block += sizeof(tls_random);
|
||||
}
|
||||
|
||||
|
||||
void eap::method_tls::derive_challenge()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#if EAP_TLS < EAP_TLS_SCHANNEL
|
||||
|
||||
void eap::method_tls::process_packet(_In_bytecount_(size_pck) const void *_pck, _In_ size_t size_pck)
|
||||
@ -1230,6 +1239,9 @@ void eap::method_tls::process_handshake()
|
||||
else
|
||||
m_module.log_event(&EAPMETHOD_TLS_QUERY_FAILED, event_data((unsigned int)SECPKG_ATTR_CONNECTION_INFO), event_data(status), event_data::blank);
|
||||
|
||||
// Derive challenge for inner authentication (if any).
|
||||
derive_challenge();
|
||||
|
||||
m_phase = phase_application_data;
|
||||
process_application_data(m_sc_queue.data(), m_sc_queue.size());
|
||||
} else
|
||||
@ -1262,13 +1274,14 @@ void eap::method_tls::process_application_data()
|
||||
|
||||
// Prepare input/output buffer(s).
|
||||
SecBuffer buf[] = {
|
||||
{ 0, SECBUFFER_TOKEN, NULL },
|
||||
{ 0, SECBUFFER_ALERT, NULL },
|
||||
{
|
||||
(unsigned long)m_sc_queue.size(),
|
||||
SECBUFFER_DATA,
|
||||
m_sc_queue.data()
|
||||
},
|
||||
{ 0, SECBUFFER_EMPTY, NULL },
|
||||
{ 0, SECBUFFER_EMPTY, NULL },
|
||||
{ 0, SECBUFFER_EMPTY, NULL },
|
||||
};
|
||||
SecBufferDesc buf_desc = {
|
||||
SECBUFFER_VERSION,
|
||||
@ -1279,8 +1292,17 @@ void eap::method_tls::process_application_data()
|
||||
// Decrypt the message.
|
||||
SECURITY_STATUS status = DecryptMessage(m_sc_ctx, &buf_desc, 0, NULL);
|
||||
if (status == SEC_E_OK) {
|
||||
assert(buf[2].BufferType == SECBUFFER_DATA);
|
||||
process_application_data(buf[2].pvBuffer, buf[2].cbBuffer);
|
||||
// Find SECBUFFER_DATA buffer(s) and process data.
|
||||
for (size_t i = 0; i < _countof(buf); i++)
|
||||
if (buf[i].BufferType == SECBUFFER_DATA)
|
||||
process_application_data(buf[i].pvBuffer, buf[i].cbBuffer);
|
||||
|
||||
// Find SECBUFFER_EXTRA buffer(s) and queue data for the next time.
|
||||
std::vector<unsigned char> extra;
|
||||
for (size_t i = 0; i < _countof(buf); i++)
|
||||
if (buf[i].BufferType == SECBUFFER_EXTRA)
|
||||
extra.insert(extra.end(), (unsigned char*)buf[i].pvBuffer, (unsigned char*)buf[i].pvBuffer + buf[i].cbBuffer);
|
||||
m_sc_queue = std::move(extra);
|
||||
} else if (status == SEC_E_INCOMPLETE_MESSAGE) {
|
||||
// Schannel neeeds more data. Send ACK packet to server to send more.
|
||||
} else if (status == SEC_I_CONTEXT_EXPIRED) {
|
||||
@ -1292,16 +1314,8 @@ void eap::method_tls::process_application_data()
|
||||
m_sc_queue.clear();
|
||||
m_phase = phase_handshake_init;
|
||||
process_handshake();
|
||||
} else if (FAILED(status)) {
|
||||
if (m_sc_ctx.m_attrib & ISC_RET_EXTENDED_ERROR) {
|
||||
// Send alert via EAP. Not that EAP will transmit it once we throw this is an error...
|
||||
assert(buf[1].BufferType == SECBUFFER_ALERT);
|
||||
assert(m_sc_ctx.m_attrib & ISC_RET_ALLOCATED_MEMORY);
|
||||
m_packet_res.m_data.assign((const unsigned char*)buf[1].pvBuffer, (const unsigned char*)buf[1].pvBuffer + buf[1].cbBuffer);
|
||||
}
|
||||
|
||||
} else if (FAILED(status))
|
||||
throw sec_runtime_error(status, __FUNCTION__ " Schannel error.");
|
||||
}
|
||||
}
|
||||
|
||||
#endif
|
||||
|
@ -128,11 +128,7 @@ namespace eap
|
||||
///
|
||||
/// Generates keying material for inner authentication
|
||||
///
|
||||
/// \param[in] size Number of bytes of keying material required
|
||||
///
|
||||
/// \returns Keing material
|
||||
///
|
||||
virtual sanitizing_blob derive_challenge(_In_ size_t size);
|
||||
virtual void derive_challenge();
|
||||
|
||||
///
|
||||
/// Processes an application message
|
||||
|
@ -188,37 +188,34 @@ void eap::method_ttls::derive_msk()
|
||||
}
|
||||
|
||||
|
||||
eap::sanitizing_blob eap::method_ttls::derive_challenge(_In_ size_t size)
|
||||
void eap::method_ttls::derive_challenge()
|
||||
{
|
||||
method_mschapv2 *inner_mschapv2 = dynamic_cast<method_mschapv2*>(m_inner.get());
|
||||
if (inner_mschapv2) {
|
||||
#if EAP_TLS < EAP_TLS_SCHANNEL
|
||||
static const unsigned char s_label[] = "ttls challenge";
|
||||
sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1);
|
||||
seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1));
|
||||
seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1));
|
||||
return prf(m_cp, CALG_TLS1PRF, m_master_secret, seed, size);
|
||||
static const unsigned char s_label[] = "ttls challenge";
|
||||
sanitizing_blob seed(s_label, s_label + _countof(s_label) - 1);
|
||||
seed.insert(seed.end(), (const unsigned char*)&m_random_client, (const unsigned char*)(&m_random_client + 1));
|
||||
seed.insert(seed.end(), (const unsigned char*)&m_random_server, (const unsigned char*)(&m_random_server + 1));
|
||||
sanitizing_blob keying(prf(m_cp, CALG_TLS1PRF, m_master_secret, seed, sizeof(challenge_mschapv2) + 1));
|
||||
memcpy(&inner_mschapv2->m_challenge_server, keying.data(), sizeof(challenge_mschapv2));
|
||||
inner_mschapv2->m_ident = keying[sizeof(challenge_mschapv2) + 0];
|
||||
#else
|
||||
static const DWORD s_key_id = 0x02; // EAP-TTLSv0 Challenge Data
|
||||
static const SecPkgContext_EapPrfInfo s_prf_info = { 0, sizeof(s_key_id), (PBYTE)&s_key_id };
|
||||
SECURITY_STATUS status = SetContextAttributes(m_sc_ctx, SECPKG_ATTR_EAP_PRF_INFO, (void*)&s_prf_info, sizeof(s_prf_info));
|
||||
if (FAILED(status))
|
||||
throw sec_runtime_error(status, __FUNCTION__ " Error setting EAP-TTLS PRF in Schannel.");
|
||||
static const DWORD s_key_id = 0x02; // EAP-TTLSv0 Challenge Data
|
||||
static const SecPkgContext_EapPrfInfo s_prf_info = { 0, sizeof(s_key_id), (PBYTE)&s_key_id };
|
||||
SECURITY_STATUS status = SetContextAttributes(m_sc_ctx, SECPKG_ATTR_EAP_PRF_INFO, (void*)&s_prf_info, sizeof(s_prf_info));
|
||||
if (FAILED(status))
|
||||
throw sec_runtime_error(status, __FUNCTION__ " Error setting EAP-TTLS PRF in Schannel.");
|
||||
|
||||
SecPkgContext_EapKeyBlock key_block;
|
||||
status = QueryContextAttributes(m_sc_ctx, SECPKG_ATTR_EAP_KEY_BLOCK, &key_block);
|
||||
if (FAILED(status))
|
||||
throw sec_runtime_error(status, __FUNCTION__ " Error generating PRF in Schannel.");
|
||||
sanitizing_blob key;
|
||||
key.reserve(size);
|
||||
if (size <= sizeof(key_block.rgbKeys))
|
||||
key.assign(key_block.rgbKeys, key_block.rgbKeys + size);
|
||||
else if (size <= sizeof(key_block.rgbKeys) + sizeof(key_block.rgbIVs)) {
|
||||
key.assign( key_block.rgbKeys, key_block.rgbKeys + sizeof(key_block.rgbKeys));
|
||||
key.insert(key.end(), key_block.rgbIVs , key_block.rgbIVs + size - sizeof(key_block.rgbKeys));
|
||||
} else
|
||||
throw invalid_argument(string_printf(__FUNCTION__ " Schannel cannot generate PRF this big (maximum: %u, requested: %u).", sizeof(key_block.rgbKeys) + sizeof(key_block.rgbIVs), size).c_str());
|
||||
SecureZeroMemory(&key_block, sizeof(key_block));
|
||||
return key;
|
||||
SecPkgContext_EapKeyBlock key_block;
|
||||
status = QueryContextAttributes(m_sc_ctx, SECPKG_ATTR_EAP_KEY_BLOCK, &key_block);
|
||||
if (FAILED(status))
|
||||
throw sec_runtime_error(status, __FUNCTION__ " Error generating PRF in Schannel.");
|
||||
|
||||
memcpy(&inner_mschapv2->m_challenge_server, key_block.rgbKeys, sizeof(challenge_mschapv2));
|
||||
inner_mschapv2->m_ident = key_block.rgbKeys[sizeof(challenge_mschapv2) + 0];
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -245,13 +242,6 @@ void eap::method_ttls::process_application_data(_In_bytecount_(size_msg) const v
|
||||
}
|
||||
#endif
|
||||
|
||||
method_mschapv2 *inner_mschapv2 = dynamic_cast<method_mschapv2*>(m_inner.get());
|
||||
if (inner_mschapv2) {
|
||||
sanitizing_blob keying(derive_challenge(sizeof(challenge_mschapv2) + 1));
|
||||
memcpy(&inner_mschapv2->m_challenge_server, keying.data(), sizeof(challenge_mschapv2));
|
||||
inner_mschapv2->m_ident = keying[sizeof(challenge_mschapv2) + 0];
|
||||
}
|
||||
|
||||
EapPeerMethodOutput eap_output = {};
|
||||
m_inner->process_request_packet(msg, (DWORD)size_msg, &eap_output);
|
||||
switch (eap_output.action) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user