MSCHAPv2 work continues...
This commit is contained in:
parent
e4e9604297
commit
4076655e2e
@ -82,12 +82,14 @@
|
||||
<ClInclude Include="..\include\Config.h" />
|
||||
<ClInclude Include="..\include\Credentials.h" />
|
||||
<ClInclude Include="..\include\Method.h" />
|
||||
<ClInclude Include="..\include\MSCHAPv2.h" />
|
||||
<ClInclude Include="..\src\StdAfx.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\src\Config.cpp" />
|
||||
<ClCompile Include="..\src\Credentials.cpp" />
|
||||
<ClCompile Include="..\src\Method.cpp" />
|
||||
<ClCompile Include="..\src\MSCHAPv2.cpp" />
|
||||
<ClCompile Include="..\src\StdAfx.cpp">
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">Create</PrecompiledHeader>
|
||||
<PrecompiledHeader Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">Create</PrecompiledHeader>
|
||||
|
@ -23,6 +23,9 @@
|
||||
<ClInclude Include="..\include\Method.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\include\MSCHAPv2.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\src\StdAfx.cpp">
|
||||
@ -37,5 +40,8 @@
|
||||
<ClCompile Include="..\src\Method.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\src\MSCHAPv2.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
260
lib/MSCHAPv2/include/MSCHAPv2.h
Normal file
260
lib/MSCHAPv2/include/MSCHAPv2.h
Normal file
@ -0,0 +1,260 @@
|
||||
/*
|
||||
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 <WinStd/Crypt.h>
|
||||
|
||||
|
||||
namespace eap
|
||||
{
|
||||
///
|
||||
/// MSCHAPv2 Challenge
|
||||
///
|
||||
struct challenge_mschapv2;
|
||||
|
||||
///
|
||||
/// MSCHAPv2 Challenge Hash
|
||||
///
|
||||
struct challenge_hash;
|
||||
|
||||
///
|
||||
/// NT-Password Hash
|
||||
///
|
||||
struct nt_password_hash;
|
||||
|
||||
///
|
||||
/// NT-Response
|
||||
///
|
||||
struct nt_response;
|
||||
|
||||
///
|
||||
/// Authenticator Response
|
||||
///
|
||||
struct authenticator_response;
|
||||
|
||||
///
|
||||
/// Creates DES encryption key with given plaintext key
|
||||
///
|
||||
/// \param[in] cp Handle of the cryptographics provider
|
||||
/// \param[in] key The key (without parity bits)
|
||||
/// \param[in] size Size of \p key (maximum 7B)
|
||||
///
|
||||
winstd::crypt_key create_des_key(_In_ HCRYPTPROV cp, _In_count_(size) const unsigned char *key, _In_ size_t size);
|
||||
}
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "../../EAPBase/include/EAP.h"
|
||||
|
||||
|
||||
namespace eap
|
||||
{
|
||||
#pragma pack(push)
|
||||
#pragma pack(1)
|
||||
|
||||
struct __declspec(novtable) challenge_mschapv2 : public sanitizing_blob_xf<16>
|
||||
{
|
||||
///
|
||||
/// Generate challenge random
|
||||
///
|
||||
/// \param[in] cp Handle of the cryptographics provider
|
||||
///
|
||||
void randomize(_In_ HCRYPTPROV cp);
|
||||
};
|
||||
|
||||
|
||||
struct __declspec(novtable) challenge_hash : public sanitizing_blob_xf<8>
|
||||
{
|
||||
///
|
||||
/// Constructor
|
||||
///
|
||||
challenge_hash();
|
||||
|
||||
///
|
||||
/// Constructs a challenge hash
|
||||
///
|
||||
/// \sa [Microsoft PPP CHAP Extensions, Version 2 (Chapter 8.2. ChallengeHash())](https://tools.ietf.org/html/rfc2759#section-8.2)
|
||||
///
|
||||
/// \param[in] cp Handle of the cryptographics provider
|
||||
/// \param[in] challenge_server Authenticator challenge
|
||||
/// \param[in] challenge_client Peer challenge
|
||||
/// \param[in] username Username
|
||||
///
|
||||
challenge_hash(
|
||||
_In_ HCRYPTPROV cp,
|
||||
_In_ const challenge_mschapv2 &challenge_server,
|
||||
_In_ const challenge_mschapv2 &challenge_client,
|
||||
_In_z_ const char *username);
|
||||
|
||||
///
|
||||
/// Copies a challenge hash
|
||||
///
|
||||
/// \param[in] other Challenge hash to copy from
|
||||
///
|
||||
challenge_hash(_In_ const sanitizing_blob_f<8> &other);
|
||||
|
||||
#ifdef _DEBUG
|
||||
///
|
||||
/// Moves the challenge hash
|
||||
///
|
||||
/// \param[inout] other Challenge hash to move from
|
||||
///
|
||||
challenge_hash(_Inout_ sanitizing_blob_zf<8> &&other);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct __declspec(novtable) nt_password_hash : public sanitizing_blob_xf<16>
|
||||
{
|
||||
///
|
||||
/// Constructor
|
||||
///
|
||||
nt_password_hash();
|
||||
|
||||
///
|
||||
/// Constructs a NT-Password hash
|
||||
///
|
||||
/// \sa [Microsoft PPP CHAP Extensions, Version 2 (Chapter 8.3. NtPasswordHash())](https://tools.ietf.org/html/rfc2759#section-8.3)
|
||||
///
|
||||
/// \param[in] cp Handle of the cryptographics provider
|
||||
/// \param[in] password Password
|
||||
///
|
||||
nt_password_hash(
|
||||
_In_ HCRYPTPROV cp,
|
||||
_In_z_ const wchar_t *password);
|
||||
|
||||
///
|
||||
/// Constructs a hash of NT-Password hash
|
||||
///
|
||||
/// \sa [Microsoft PPP CHAP Extensions, Version 2 (Chapter 8.4. HashNtPasswordHash())](https://tools.ietf.org/html/rfc2759#section-8.4)
|
||||
///
|
||||
/// \param[in] cp Handle of the cryptographics provider
|
||||
/// \param[in] pwd_hash NT-Password hash
|
||||
///
|
||||
nt_password_hash(
|
||||
_In_ HCRYPTPROV cp,
|
||||
_In_ const nt_password_hash &pwd_hash);
|
||||
|
||||
///
|
||||
/// Copies a NT-Password hash
|
||||
///
|
||||
/// \param[in] other NT-Password to copy from
|
||||
///
|
||||
nt_password_hash(_In_ const sanitizing_blob_f<16> &other);
|
||||
|
||||
#ifdef _DEBUG
|
||||
///
|
||||
/// Moves the NT-Password hash
|
||||
///
|
||||
/// \param[inout] other NT-Password hash to move from
|
||||
///
|
||||
nt_password_hash(_Inout_ sanitizing_blob_zf<16> &&other);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct __declspec(novtable) nt_response : public sanitizing_blob_xf<24>
|
||||
{
|
||||
///
|
||||
/// Constructor
|
||||
///
|
||||
nt_response();
|
||||
|
||||
///
|
||||
/// Constructs a NT-Response
|
||||
///
|
||||
/// \sa [Microsoft PPP CHAP Extensions, Version 2 (Chapter 8.1. GenerateNTResponse())](https://tools.ietf.org/html/rfc2759#section-8.1)
|
||||
///
|
||||
/// \param[in] cp Handle of the cryptographics provider
|
||||
/// \param[in] challenge_server Authenticator challenge
|
||||
/// \param[in] challenge_client Peer challenge
|
||||
/// \param[in] username Username
|
||||
/// \param[in] password Password
|
||||
///
|
||||
nt_response(
|
||||
_In_ HCRYPTPROV cp,
|
||||
_In_ const challenge_mschapv2 &challenge_server,
|
||||
_In_ const challenge_mschapv2 &challenge_client,
|
||||
_In_z_ const char *username,
|
||||
_In_z_ const wchar_t *password);
|
||||
|
||||
///
|
||||
/// Copies a NT-Response
|
||||
///
|
||||
/// \param[in] other NT-Response to copy from
|
||||
///
|
||||
nt_response(_In_ const sanitizing_blob_f<24> &other);
|
||||
|
||||
#ifdef _DEBUG
|
||||
///
|
||||
/// Moves the NT-Response
|
||||
///
|
||||
/// \param[inout] other NT-Response to move from
|
||||
///
|
||||
nt_response(_Inout_ sanitizing_blob_zf<24> &&other);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
||||
struct __declspec(novtable) authenticator_response : public sanitizing_blob_xf<20>
|
||||
{
|
||||
///
|
||||
/// Constructor
|
||||
///
|
||||
authenticator_response();
|
||||
|
||||
///
|
||||
/// Constructs an authenticator response
|
||||
///
|
||||
/// \sa [Microsoft PPP CHAP Extensions, Version 2 (Chapter 8.7. GenerateAuthenticatorResponse())](https://tools.ietf.org/html/rfc2759#section-8.7)
|
||||
///
|
||||
/// \param[in] cp Handle of the cryptographics provider
|
||||
/// \param[in] challenge_server Authenticator challenge
|
||||
/// \param[in] challenge_client Peer challenge
|
||||
/// \param[in] username Username
|
||||
/// \param[in] password Password
|
||||
/// \param[in] nt_resp NT-Response
|
||||
///
|
||||
authenticator_response(
|
||||
_In_ HCRYPTPROV cp,
|
||||
_In_ const challenge_mschapv2 &challenge_server,
|
||||
_In_ const challenge_mschapv2 &challenge_client,
|
||||
_In_z_ const char *username,
|
||||
_In_z_ const wchar_t *password,
|
||||
_In_ const nt_response &nt_resp);
|
||||
|
||||
///
|
||||
/// Copies an authenticator response
|
||||
///
|
||||
/// \param[in] other Authenticator response to copy from
|
||||
///
|
||||
authenticator_response(_In_ const sanitizing_blob_f<20> &other);
|
||||
|
||||
#ifdef _DEBUG
|
||||
///
|
||||
/// Moves the authenticator response
|
||||
///
|
||||
/// \param[inout] other Authenticator response to move from
|
||||
///
|
||||
authenticator_response(_Inout_ sanitizing_blob_zf<20> &&other);
|
||||
#endif
|
||||
};
|
||||
|
||||
#pragma pack(pop)
|
||||
}
|
@ -26,11 +26,11 @@ namespace eap
|
||||
class method_mschapv2;
|
||||
}
|
||||
|
||||
|
||||
#pragma once
|
||||
|
||||
#include "Config.h"
|
||||
#include "Credentials.h"
|
||||
#include "MSCHAPv2.h"
|
||||
|
||||
#include "../../EAPBase/include/Method.h"
|
||||
|
||||
@ -100,12 +100,51 @@ namespace eap
|
||||
|
||||
/// @}
|
||||
|
||||
friend class method_ttls; // Setting of initial challenge derived from TLS PRF
|
||||
|
||||
protected:
|
||||
///
|
||||
/// Processes AVPs in a Diameter packet
|
||||
///
|
||||
/// \param[in] pck Packet data
|
||||
/// \param[in] size_pck \p pck size in bytes
|
||||
///
|
||||
void process_packet(_In_bytecount_(size_pck) const void *pck, _In_ size_t size_pck);
|
||||
|
||||
///
|
||||
/// Processes MS-CHAP2-Success AVP
|
||||
///
|
||||
/// \sa [Microsoft PPP CHAP Extensions, Version 2 (Chapter 5. Success Packet)](https://tools.ietf.org/html/rfc2759#section-5)
|
||||
///
|
||||
/// \param[in] pck Packet data
|
||||
/// \param[in] size_pck \p pck size in bytes
|
||||
///
|
||||
void process_success(_In_ int argc, _In_count_(argc) const wchar_t *argv[]);
|
||||
|
||||
///
|
||||
/// Processes MS-CHAP-Error AVP
|
||||
///
|
||||
/// \sa [Microsoft PPP CHAP Extensions, Version 2 (Chapter 6. Failure Packet)](https://tools.ietf.org/html/rfc2759#section-6)
|
||||
///
|
||||
/// \param[in] pck Packet data
|
||||
/// \param[in] size_pck \p pck size in bytes
|
||||
///
|
||||
void process_error(_In_ int argc, _In_count_(argc) const wchar_t *argv[]);
|
||||
|
||||
protected:
|
||||
credentials_mschapv2 &m_cred; ///< EAP-TLS user credentials
|
||||
winstd::crypt_prov m_cp; ///< Cryptography provider for general services
|
||||
|
||||
challenge_mschapv2 m_challenge_server; ///< MSCHAP server challenge
|
||||
challenge_mschapv2 m_challenge_client; ///< MSCHAP client challenge
|
||||
unsigned char m_ident; ///< Ident
|
||||
nt_response m_nt_resp; ///< NT-Response
|
||||
bool m_success; ///< Did we receive MS-CHAP2-Success?
|
||||
|
||||
enum {
|
||||
phase_unknown = -1, ///< Unknown phase
|
||||
phase_init = 0, ///< Handshake initialize
|
||||
phase_init = 0, ///< Send client challenge
|
||||
phase_challenge_server, ///< Verify server challenge
|
||||
phase_finished, ///< Connection shut down
|
||||
} m_phase, m_phase_prev; ///< What phase is our communication at?
|
||||
};
|
||||
|
336
lib/MSCHAPv2/src/MSCHAPv2.cpp
Normal file
336
lib/MSCHAPv2/src/MSCHAPv2.cpp
Normal file
@ -0,0 +1,336 @@
|
||||
/*
|
||||
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::create_des_key
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
crypt_key eap::create_des_key(_In_ HCRYPTPROV cp, _In_count_(size) const unsigned char *key, _In_ size_t size)
|
||||
{
|
||||
// Prepare exported key BLOB.
|
||||
struct key_blob_prefix {
|
||||
PUBLICKEYSTRUC header;
|
||||
DWORD size;
|
||||
} static const s_prefix = {
|
||||
{
|
||||
PLAINTEXTKEYBLOB,
|
||||
CUR_BLOB_VERSION,
|
||||
0,
|
||||
CALG_DES,
|
||||
},
|
||||
8,
|
||||
};
|
||||
sanitizing_blob key_blob;
|
||||
key_blob.reserve(sizeof(key_blob_prefix) + 8);
|
||||
key_blob.assign((const unsigned char*)&s_prefix, (const unsigned char*)(&s_prefix + 1));
|
||||
|
||||
// Inject parity bits.
|
||||
unsigned char out = 0, parity = 1;
|
||||
size_t i = 0, j = 7, bits = std::min<size_t>(size * 8, 56);
|
||||
for (; i < bits; i++) {
|
||||
unsigned char bit = (key[i/8] & (1 << (7 - i%8))) ? 1 : 0;
|
||||
parity ^= bit;
|
||||
out |= bit << j;
|
||||
if (--j == 0) {
|
||||
out |= parity;
|
||||
key_blob.push_back(out);
|
||||
out = 0; parity = 1; j = 7;
|
||||
}
|
||||
}
|
||||
for (; i < 56; i++) {
|
||||
if (--j == 0) {
|
||||
out |= parity;
|
||||
key_blob.push_back(out);
|
||||
out = 0; parity = 1; j = 7;
|
||||
}
|
||||
}
|
||||
|
||||
// Import key.
|
||||
crypt_key k;
|
||||
if (!k.import(cp, key_blob.data(), (DWORD)key_blob.size(), NULL, 0))
|
||||
throw winstd::win_runtime_error(__FUNCTION__ " Error importing key 1/3.");
|
||||
return k;
|
||||
}
|
||||
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// eap::challenge_mschapv2
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
void eap::challenge_mschapv2::randomize(_In_ HCRYPTPROV cp)
|
||||
{
|
||||
if (!CryptGenRandom(cp, sizeof(data), data))
|
||||
throw win_runtime_error(__FUNCTION__ " Error creating randomness.");
|
||||
}
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// eap::challenge_hash
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
eap::challenge_hash::challenge_hash()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
eap::challenge_hash::challenge_hash(
|
||||
_In_ HCRYPTPROV cp,
|
||||
_In_ const challenge_mschapv2 &challenge_server,
|
||||
_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))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
unsigned char hash_val[20];
|
||||
DWORD size_hash_val = sizeof(hash_val);
|
||||
if (!CryptGetHashParam(hash, HP_HASHVAL, hash_val, &size_hash_val, 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
memcpy(data, hash_val, sizeof(data));
|
||||
SecureZeroMemory(hash_val, size_hash_val);
|
||||
}
|
||||
|
||||
|
||||
eap::challenge_hash::challenge_hash(_In_ const sanitizing_blob_f<8> &other) :
|
||||
sanitizing_blob_xf<8>(other)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
eap::challenge_hash::challenge_hash(_Inout_ sanitizing_blob_zf<8> &&other) :
|
||||
sanitizing_blob_xf<8>(std::move(other))
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// eap::nt_password_hash
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
eap::nt_password_hash::nt_password_hash()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
eap::nt_password_hash::nt_password_hash(
|
||||
_In_ HCRYPTPROV cp,
|
||||
_In_z_ const wchar_t *password)
|
||||
{
|
||||
crypt_hash hash;
|
||||
if (!hash.create(cp, CALG_MD4))
|
||||
throw win_runtime_error(__FUNCTION__ " Creating MD4 hash failed.");
|
||||
if (!CryptHashData(hash, (const BYTE*)password, (DWORD)(wcslen(password) * sizeof(wchar_t)), 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
DWORD size_data = sizeof(data);
|
||||
if (!CryptGetHashParam(hash, HP_HASHVAL, data, &size_data, 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
}
|
||||
|
||||
|
||||
eap::nt_password_hash::nt_password_hash(
|
||||
_In_ HCRYPTPROV cp,
|
||||
_In_ const nt_password_hash &pwd_hash)
|
||||
{
|
||||
crypt_hash hash;
|
||||
if (!hash.create(cp, CALG_MD4))
|
||||
throw win_runtime_error(__FUNCTION__ " Creating MD4 hash failed.");
|
||||
if (!CryptHashData(hash, (const BYTE*)&pwd_hash, (DWORD)sizeof(pwd_hash), 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
DWORD size_data = sizeof(data);
|
||||
if (!CryptGetHashParam(hash, HP_HASHVAL, data, &size_data, 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
}
|
||||
|
||||
|
||||
eap::nt_password_hash::nt_password_hash(_In_ const sanitizing_blob_f<16> &other) :
|
||||
sanitizing_blob_xf<16>(other)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
eap::nt_password_hash::nt_password_hash(_Inout_ sanitizing_blob_zf<16> &&other) :
|
||||
sanitizing_blob_xf<16>(std::move(other))
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// eap::nt_response
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
eap::nt_response::nt_response()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
eap::nt_response::nt_response(
|
||||
_In_ HCRYPTPROV cp,
|
||||
_In_ const challenge_mschapv2 &challenge_server,
|
||||
_In_ const challenge_mschapv2 &challenge_client,
|
||||
_In_z_ const char *username,
|
||||
_In_z_ const wchar_t *password)
|
||||
{
|
||||
challenge_hash challenge(cp, challenge_server, challenge_client, username);
|
||||
nt_password_hash hash_pwd(cp, password);
|
||||
|
||||
// Prepare exported key BLOB.
|
||||
crypt_key key;
|
||||
DWORD size_data_enc;
|
||||
static const DWORD mode_ecb = CRYPT_MODE_ECB;
|
||||
|
||||
// DesEncrypt(Challenge, 1st 7-octets of ZPasswordHash, giving 1st 8-octets of Response)
|
||||
key = create_des_key(cp, (const unsigned char*)&hash_pwd, 7);
|
||||
if (!CryptSetKeyParam(key, KP_MODE, (const BYTE*)&mode_ecb, 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error setting ECB mode.");
|
||||
memcpy(data, &challenge, 8);
|
||||
size_data_enc = 8;
|
||||
if (!CryptEncrypt(key, NULL, FALSE, 0, data, &size_data_enc, 8))
|
||||
throw win_runtime_error(__FUNCTION__ " Error encrypting message 1/3.");
|
||||
|
||||
// DesEncrypt(Challenge, 2nd 7-octets of ZPasswordHash, giving 2nd 8-octets of Response)
|
||||
key = create_des_key(cp, (const unsigned char*)&hash_pwd + 7, 7);
|
||||
if (!CryptSetKeyParam(key, KP_MODE, (const BYTE*)&mode_ecb, 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error setting ECB mode.");
|
||||
memcpy(data + 8, &challenge, 8);
|
||||
size_data_enc = 8;
|
||||
if (!CryptEncrypt(key, NULL, FALSE, 0, data + 8, &size_data_enc, 8))
|
||||
throw win_runtime_error(__FUNCTION__ " Error encrypting message 2/3.");
|
||||
|
||||
// DesEncrypt(Challenge, 2nd 7-octets of ZPasswordHash, giving 2nd 8-octets of Response)
|
||||
key = create_des_key(cp, (const unsigned char*)&hash_pwd + 14, 2);
|
||||
if (!CryptSetKeyParam(key, KP_MODE, (const BYTE*)&mode_ecb, 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error setting ECB mode.");
|
||||
memcpy(data + 16, &challenge, 8);
|
||||
size_data_enc = 8;
|
||||
if (!CryptEncrypt(key, NULL, FALSE, 0, data + 16, &size_data_enc, 8))
|
||||
throw win_runtime_error(__FUNCTION__ " Error encrypting message 3/3.");
|
||||
}
|
||||
|
||||
|
||||
eap::nt_response::nt_response(_In_ const sanitizing_blob_f<24> &other) :
|
||||
sanitizing_blob_xf<24>(other)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
eap::nt_response::nt_response(_Inout_ sanitizing_blob_zf<24> &&other) :
|
||||
sanitizing_blob_xf<24>(std::move(other))
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
// eap::authenticator_response
|
||||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
eap::authenticator_response::authenticator_response()
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
eap::authenticator_response::authenticator_response(
|
||||
_In_ HCRYPTPROV cp,
|
||||
_In_ const challenge_mschapv2 &challenge_server,
|
||||
_In_ const challenge_mschapv2 &challenge_client,
|
||||
_In_z_ const char *username,
|
||||
_In_z_ const wchar_t *password,
|
||||
_In_ const nt_response &nt_resp)
|
||||
{
|
||||
static const unsigned char s_magic1[39] = {
|
||||
0x4d, 0x61, 0x67, 0x69, 0x63, 0x20, 0x73, 0x65, 0x72, 0x76,
|
||||
0x65, 0x72, 0x20, 0x74, 0x6f, 0x20, 0x63, 0x6c, 0x69, 0x65,
|
||||
0x6e, 0x74, 0x20, 0x73, 0x69, 0x67, 0x6e, 0x69, 0x6e, 0x67,
|
||||
0x20, 0x63, 0x6f, 0x6e, 0x73, 0x74, 0x61, 0x6e, 0x74
|
||||
};
|
||||
nt_password_hash hash_hash_pwd(cp, nt_password_hash(cp, password));
|
||||
|
||||
crypt_hash hash;
|
||||
if (!hash.create(cp, CALG_SHA))
|
||||
throw win_runtime_error(__FUNCTION__ " Creating SHA hash failed.");
|
||||
if (!CryptHashData(hash, (const BYTE*)&hash_hash_pwd, (DWORD)sizeof(hash_hash_pwd), 0) ||
|
||||
!CryptHashData(hash, (const BYTE*)&nt_resp , (DWORD)sizeof(nt_resp ), 0) ||
|
||||
!CryptHashData(hash, s_magic1 , (DWORD)sizeof(s_magic1 ), 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
unsigned char hash_val[20];
|
||||
DWORD size_hash_val = sizeof(hash_val);
|
||||
if (!CryptGetHashParam(hash, HP_HASHVAL, hash_val, &size_hash_val, 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
|
||||
static const unsigned char s_magic2[41] = {
|
||||
0x50, 0x61, 0x64, 0x20, 0x74, 0x6f, 0x20, 0x6d, 0x61, 0x6b,
|
||||
0x65, 0x20, 0x69, 0x74, 0x20, 0x64, 0x6f, 0x20, 0x6d, 0x6f,
|
||||
0x72, 0x65, 0x20, 0x74, 0x68, 0x61, 0x6e, 0x20, 0x6f, 0x6e,
|
||||
0x65, 0x20, 0x69, 0x74, 0x65, 0x72, 0x61, 0x74, 0x69, 0x6f,
|
||||
0x6e
|
||||
};
|
||||
challenge_hash challenge(cp, challenge_server, challenge_client, username);
|
||||
|
||||
if (!hash.create(cp, CALG_SHA))
|
||||
throw win_runtime_error(__FUNCTION__ " Creating SHA hash failed.");
|
||||
if (!CryptHashData(hash, hash_val , size_hash_val , 0) ||
|
||||
!CryptHashData(hash, (const BYTE*)&challenge, (DWORD)sizeof(challenge), 0) ||
|
||||
!CryptHashData(hash, s_magic2 , (DWORD)sizeof(s_magic2 ), 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
size_hash_val = sizeof(data);
|
||||
if (!CryptGetHashParam(hash, HP_HASHVAL, data, &size_hash_val, 0))
|
||||
throw win_runtime_error(__FUNCTION__ " Error hashing data.");
|
||||
}
|
||||
|
||||
|
||||
eap::authenticator_response::authenticator_response(_In_ const sanitizing_blob_f<20> &other) :
|
||||
sanitizing_blob_xf<20>(other)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
#ifdef _DEBUG
|
||||
|
||||
eap::authenticator_response::authenticator_response(_Inout_ sanitizing_blob_zf<20> &&other) :
|
||||
sanitizing_blob_xf<20>(std::move(other))
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
@ -30,6 +30,8 @@ using namespace winstd;
|
||||
|
||||
eap::method_mschapv2::method_mschapv2(_In_ module &module, _In_ config_method_mschapv2 &cfg, _In_ credentials_mschapv2 &cred) :
|
||||
m_cred(cred),
|
||||
m_ident(0),
|
||||
m_success(false),
|
||||
m_phase(phase_unknown),
|
||||
m_phase_prev(phase_unknown),
|
||||
method_noneap(module, cfg, cred)
|
||||
@ -39,6 +41,12 @@ eap::method_mschapv2::method_mschapv2(_In_ module &module, _In_ config_method_ms
|
||||
|
||||
eap::method_mschapv2::method_mschapv2(_Inout_ method_mschapv2 &&other) :
|
||||
m_cred ( other.m_cred ),
|
||||
m_cp (std::move(other.m_cp )),
|
||||
m_challenge_server(std::move(other.m_challenge_server)),
|
||||
m_challenge_client(std::move(other.m_challenge_client)),
|
||||
m_ident (std::move(other.m_ident )),
|
||||
m_nt_resp (std::move(other.m_nt_resp )),
|
||||
m_success (std::move(other.m_success )),
|
||||
m_phase (std::move(other.m_phase )),
|
||||
m_phase_prev (std::move(other.m_phase_prev )),
|
||||
method_noneap (std::move(other ))
|
||||
@ -51,6 +59,12 @@ eap::method_mschapv2& eap::method_mschapv2::operator=(_Inout_ method_mschapv2 &&
|
||||
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_cp = std::move(other.m_cp );
|
||||
m_challenge_server = std::move(other.m_challenge_server);
|
||||
m_challenge_client = std::move(other.m_challenge_client);
|
||||
m_ident = std::move(other.m_ident );
|
||||
m_nt_resp = std::move(other.m_nt_resp );
|
||||
m_success = std::move(other.m_success );
|
||||
m_phase = std::move(other.m_phase );
|
||||
m_phase_prev = std::move(other.m_phase_prev );
|
||||
}
|
||||
@ -67,6 +81,10 @@ void eap::method_mschapv2::begin_session(
|
||||
{
|
||||
method_noneap::begin_session(dwFlags, pAttributeArray, hTokenImpersonateUser, dwMaxSendPacketSize);
|
||||
|
||||
// Create cryptographics provider for support needs (client challenge ...).
|
||||
if (!m_cp.create(NULL, NULL, PROV_RSA_AES, CRYPT_VERIFYCONTEXT))
|
||||
throw win_runtime_error(__FUNCTION__ " Error creating cryptographics provider.");
|
||||
|
||||
m_module.log_event(&EAPMETHOD_METHOD_HANDSHAKE_START2, event_data((unsigned int)eap_type_legacy_mschapv2), event_data::blank);
|
||||
m_phase = phase_init;
|
||||
}
|
||||
@ -82,23 +100,36 @@ 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: {
|
||||
// Convert username and password to UTF-8.
|
||||
sanitizing_string identity_utf8, password_utf8;
|
||||
// Convert username to UTF-8.
|
||||
sanitizing_string identity_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);
|
||||
// Prepare MS-CHAP2-Response
|
||||
sanitizing_blob response;
|
||||
response.push_back(m_ident); // Ident
|
||||
response.push_back(0); // Flags
|
||||
m_challenge_client.randomize(m_cp);
|
||||
response.insert(response.end(), (unsigned char*)&m_challenge_client, (unsigned char*)(&m_challenge_client + 1)); // Peer-Challenge
|
||||
response.insert(response.end(), 8, 0); // Reserved
|
||||
m_nt_resp = nt_response(m_cp, m_challenge_server, m_challenge_client, identity_utf8.c_str(), m_cred.m_password.c_str());
|
||||
response.insert(response.end(), (unsigned char*)&m_nt_resp, (unsigned char*)(&m_nt_resp + 1)); // NT-Response
|
||||
|
||||
// Diameter AVP (User-Name=0x00000001, User-Password=0x00000002)
|
||||
append_avp(0x00000001, diameter_avp_flag_mandatory, identity_utf8.data(), (unsigned int)identity_utf8.size());
|
||||
append_avp(0x00000002, diameter_avp_flag_mandatory, password_utf8.data(), (unsigned int)password_utf8.size());
|
||||
// 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 int)sizeof(m_challenge_server));
|
||||
append_avp(25, 311, diameter_avp_flag_mandatory, response.data() , (unsigned int)response.size() );
|
||||
|
||||
m_phase = phase_finished;
|
||||
m_phase = phase_challenge_server;
|
||||
break;
|
||||
}
|
||||
|
||||
case phase_challenge_server: {
|
||||
// We do not need to do anything.
|
||||
break;
|
||||
}
|
||||
|
||||
@ -151,3 +182,90 @@ void eap::method_mschapv2::get_result(
|
||||
// Always ask EAP host to save the connection data.
|
||||
ppResult->fSaveConnectionData = TRUE;
|
||||
}
|
||||
|
||||
|
||||
void eap::method_mschapv2::process_packet(_In_bytecount_(size_pck) const void *_pck, _In_ size_t size_pck)
|
||||
{
|
||||
sanitizing_blob data;
|
||||
wstring msg_w;
|
||||
|
||||
for (const unsigned char *pck = (const unsigned char*)_pck, *pck_end = pck + size_pck; pck < pck_end; ) {
|
||||
if (pck + sizeof(diameter_avp_header) > pck_end)
|
||||
throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message header.");
|
||||
const diameter_avp_header *hdr = (const diameter_avp_header*)pck;
|
||||
unsigned int code = ntohl(*(unsigned int*)hdr->code);
|
||||
unsigned int vendor;
|
||||
const unsigned char *msg;
|
||||
if (hdr->flags & diameter_avp_flag_vendor) {
|
||||
if (pck + sizeof(diameter_avp_header_ven) > pck_end)
|
||||
throw win_runtime_error(EAP_E_EAPHOST_METHOD_INVALID_PACKET, __FUNCTION__ " Incomplete message header.");
|
||||
const diameter_avp_header_ven *hdr_ven = (const diameter_avp_header_ven*)pck;
|
||||
vendor = ntohl(*(unsigned int*)hdr_ven->vendor);
|
||||
msg = (const unsigned char*)(hdr_ven + 1);
|
||||
} else {
|
||||
vendor = 0;
|
||||
msg = (const unsigned char*)(hdr + 1);
|
||||
}
|
||||
const unsigned char *msg_end = pck + ntohs(*(unsigned short*)hdr->length);
|
||||
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);
|
||||
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);
|
||||
int argc;
|
||||
unique_ptr<LPWSTR[], LocalFree_delete<LPWSTR[]> > argv(CommandLineToArgvW(msg_w.c_str(), &argc));
|
||||
if (!argv) argc = 0;
|
||||
process_error(argc, (const wchar_t**)argv.get());
|
||||
} 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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void eap::method_mschapv2::process_success(_In_ int argc, _In_count_(argc) const wchar_t *argv[])
|
||||
{
|
||||
m_success = false;
|
||||
|
||||
for (int i = 0; i < argc; i++) {
|
||||
if ((argv[i][0] == L'S' || argv[i][0] == L's') && argv[i][1] == L'=') {
|
||||
// "S="
|
||||
hex_dec dec;
|
||||
sanitizing_blob resp;
|
||||
bool is_last;
|
||||
dec.decode(resp, is_last, argv[i] + 2, (size_t)-1);
|
||||
|
||||
// Calculate expected authenticator response.
|
||||
sanitizing_string identity_utf8;
|
||||
WideCharToMultiByte(CP_UTF8, 0, m_cred.m_identity.c_str(), (int)m_cred.m_identity.length(), identity_utf8, NULL, NULL);
|
||||
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)
|
||||
throw invalid_argument(__FUNCTION__ " MS-CHAP2-Success authentication response string failed.");
|
||||
|
||||
m_success = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!m_success)
|
||||
throw invalid_argument(__FUNCTION__ " MS-CHAP2-Success authentication response string not found.");
|
||||
|
||||
//m_module.log_event(&EAPMETHOD_TLS_ALERT, event_data((unsigned int)eap_type_tls), event_data((unsigned char)msg[0]), event_data((unsigned char)msg[1]), event_data::blank);
|
||||
}
|
||||
|
||||
|
||||
void eap::method_mschapv2::process_error(_In_ int argc, _In_count_(argc) const wchar_t *argv[])
|
||||
{
|
||||
UNREFERENCED_PARAMETER(argc);
|
||||
UNREFERENCED_PARAMETER(argv);
|
||||
}
|
||||
|
@ -23,6 +23,9 @@
|
||||
#include "../include/Config.h"
|
||||
#include "../include/Credentials.h"
|
||||
#include "../include/Method.h"
|
||||
#include "../include/MSCHAPv2.h"
|
||||
|
||||
#include <WinStd/Hex.h>
|
||||
|
||||
#include <Windows.h>
|
||||
#include <EapHostError.h> // include after Windows.h
|
||||
|
@ -245,9 +245,15 @@ 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) {
|
||||
case EapPeerMethodResponseActionSend: {
|
||||
// Retrieve inner packet and send it.
|
||||
|
@ -33,6 +33,7 @@
|
||||
#include "../../MSCHAPv2/include/Config.h"
|
||||
#include "../../MSCHAPv2/include/Credentials.h"
|
||||
#include "../../MSCHAPv2/include/Method.h"
|
||||
#include "../../MSCHAPv2/include/MSCHAPv2.h"
|
||||
|
||||
#include "../../EAPBase/include/EAPXML.h"
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user