2012-02-25 18:55:52 +04:00
|
|
|
/**
|
2012-05-22 06:48:33 +04:00
|
|
|
* WinPR: Windows Portable Runtime
|
2012-02-25 18:55:52 +04:00
|
|
|
* NTLM Security Package (Compute)
|
|
|
|
*
|
2014-06-06 06:10:08 +04:00
|
|
|
* Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
2012-02-25 18:55:52 +04:00
|
|
|
*
|
|
|
|
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
* you may not use this file except in compliance with the License.
|
|
|
|
* You may obtain a copy of the License at
|
|
|
|
*
|
|
|
|
* http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
*
|
|
|
|
* Unless required by applicable law or agreed to in writing, software
|
|
|
|
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
* See the License for the specific language governing permissions and
|
|
|
|
* limitations under the License.
|
|
|
|
*/
|
|
|
|
|
2012-08-15 01:20:53 +04:00
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
#include "config.h"
|
|
|
|
#endif
|
|
|
|
|
2012-02-25 18:55:52 +04:00
|
|
|
#include "ntlm.h"
|
|
|
|
#include "../sspi.h"
|
|
|
|
|
2012-05-22 06:04:14 +04:00
|
|
|
#include <winpr/crt.h>
|
2012-06-03 07:31:15 +04:00
|
|
|
#include <winpr/sam.h>
|
2012-06-03 02:21:04 +04:00
|
|
|
#include <winpr/ntlm.h>
|
2012-05-25 06:40:46 +04:00
|
|
|
#include <winpr/print.h>
|
2015-10-08 23:48:58 +03:00
|
|
|
#include <winpr/crypto.h>
|
2012-06-29 22:31:32 +04:00
|
|
|
#include <winpr/sysinfo.h>
|
2012-02-25 18:55:52 +04:00
|
|
|
|
|
|
|
#include "ntlm_compute.h"
|
|
|
|
|
2014-08-18 19:22:22 +04:00
|
|
|
#include "../../log.h"
|
2014-08-18 20:57:08 +04:00
|
|
|
#define TAG WINPR_TAG("sspi.NTLM")
|
2014-08-18 19:22:22 +04:00
|
|
|
|
2014-06-07 08:17:11 +04:00
|
|
|
const char LM_MAGIC[] = "KGS!@#$%";
|
2012-02-25 19:20:12 +04:00
|
|
|
|
2014-06-07 08:17:11 +04:00
|
|
|
static const char NTLM_CLIENT_SIGN_MAGIC[] = "session key to client-to-server signing key magic constant";
|
|
|
|
static const char NTLM_SERVER_SIGN_MAGIC[] = "session key to server-to-client signing key magic constant";
|
|
|
|
static const char NTLM_CLIENT_SEAL_MAGIC[] = "session key to client-to-server sealing key magic constant";
|
|
|
|
static const char NTLM_SERVER_SEAL_MAGIC[] = "session key to server-to-client sealing key magic constant";
|
2012-02-25 19:20:12 +04:00
|
|
|
|
2014-06-18 22:42:35 +04:00
|
|
|
static const BYTE NTLM_NULL_BUFFER[16] =
|
2014-08-18 19:22:22 +04:00
|
|
|
{ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
2014-06-09 23:25:00 +04:00
|
|
|
|
2012-06-29 22:31:32 +04:00
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Populate VERSION structure.
|
2012-06-29 22:31:32 +04:00
|
|
|
* VERSION @msdn{cc236654}
|
|
|
|
* @param s
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo)
|
2012-06-29 22:31:32 +04:00
|
|
|
{
|
|
|
|
OSVERSIONINFOA osVersionInfo;
|
|
|
|
osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
|
|
|
|
GetVersionExA(&osVersionInfo);
|
2012-07-29 03:30:21 +04:00
|
|
|
versionInfo->ProductMajorVersion = (UINT8) osVersionInfo.dwMajorVersion;
|
|
|
|
versionInfo->ProductMinorVersion = (UINT8) osVersionInfo.dwMinorVersion;
|
|
|
|
versionInfo->ProductBuild = (UINT16) osVersionInfo.dwBuildNumber;
|
2012-06-29 22:31:32 +04:00
|
|
|
ZeroMemory(versionInfo->Reserved, sizeof(versionInfo->Reserved));
|
|
|
|
versionInfo->NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3;
|
|
|
|
}
|
|
|
|
|
2012-07-01 22:33:36 +04:00
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Read VERSION structure.
|
2012-07-01 22:33:36 +04:00
|
|
|
* VERSION @msdn{cc236654}
|
|
|
|
* @param s
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo)
|
2012-07-01 22:33:36 +04:00
|
|
|
{
|
2014-06-07 08:17:11 +04:00
|
|
|
if (Stream_GetRemainingLength(s) < 8)
|
|
|
|
return -1;
|
|
|
|
|
2012-12-10 23:08:06 +04:00
|
|
|
Stream_Read_UINT8(s, versionInfo->ProductMajorVersion); /* ProductMajorVersion (1 byte) */
|
|
|
|
Stream_Read_UINT8(s, versionInfo->ProductMinorVersion); /* ProductMinorVersion (1 byte) */
|
|
|
|
Stream_Read_UINT16(s, versionInfo->ProductBuild); /* ProductBuild (2 bytes) */
|
|
|
|
Stream_Read(s, versionInfo->Reserved, sizeof(versionInfo->Reserved)); /* Reserved (3 bytes) */
|
|
|
|
Stream_Read_UINT8(s, versionInfo->NTLMRevisionCurrent); /* NTLMRevisionCurrent (1 byte) */
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-07-01 22:33:36 +04:00
|
|
|
}
|
|
|
|
|
2012-06-29 22:31:32 +04:00
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Write VERSION structure.
|
2012-06-29 22:31:32 +04:00
|
|
|
* VERSION @msdn{cc236654}
|
|
|
|
* @param s
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_write_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo)
|
2012-06-29 22:31:32 +04:00
|
|
|
{
|
2012-12-10 23:08:06 +04:00
|
|
|
Stream_Write_UINT8(s, versionInfo->ProductMajorVersion); /* ProductMajorVersion (1 byte) */
|
|
|
|
Stream_Write_UINT8(s, versionInfo->ProductMinorVersion); /* ProductMinorVersion (1 byte) */
|
|
|
|
Stream_Write_UINT16(s, versionInfo->ProductBuild); /* ProductBuild (2 bytes) */
|
|
|
|
Stream_Write(s, versionInfo->Reserved, sizeof(versionInfo->Reserved)); /* Reserved (3 bytes) */
|
|
|
|
Stream_Write_UINT8(s, versionInfo->NTLMRevisionCurrent); /* NTLMRevisionCurrent (1 byte) */
|
2012-07-01 22:33:36 +04:00
|
|
|
}
|
2012-06-29 22:31:32 +04:00
|
|
|
|
2012-07-01 22:33:36 +04:00
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Print VERSION structure.
|
2012-07-01 22:33:36 +04:00
|
|
|
* VERSION @msdn{cc236654}
|
|
|
|
* @param s
|
|
|
|
*/
|
2012-06-29 22:31:32 +04:00
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_print_version_info(NTLM_VERSION_INFO* versionInfo)
|
2012-07-01 22:33:36 +04:00
|
|
|
{
|
2014-08-18 19:22:22 +04:00
|
|
|
WLog_INFO(TAG, "VERSION ={");
|
|
|
|
WLog_INFO(TAG, "\tProductMajorVersion: %d", versionInfo->ProductMajorVersion);
|
|
|
|
WLog_INFO(TAG, "\tProductMinorVersion: %d", versionInfo->ProductMinorVersion);
|
|
|
|
WLog_INFO(TAG, "\tProductBuild: %d", versionInfo->ProductBuild);
|
|
|
|
WLog_INFO(TAG, "\tReserved: 0x%02X%02X%02X", versionInfo->Reserved[0],
|
|
|
|
versionInfo->Reserved[1], versionInfo->Reserved[2]);
|
|
|
|
WLog_INFO(TAG, "\tNTLMRevisionCurrent: 0x%02X", versionInfo->NTLMRevisionCurrent);
|
2012-06-29 22:31:32 +04:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_read_ntlm_v2_client_challenge(wStream* s, NTLMv2_CLIENT_CHALLENGE* challenge)
|
2012-07-02 01:05:31 +04:00
|
|
|
{
|
|
|
|
size_t size;
|
2012-12-10 23:08:06 +04:00
|
|
|
Stream_Read_UINT8(s, challenge->RespType);
|
|
|
|
Stream_Read_UINT8(s, challenge->HiRespType);
|
|
|
|
Stream_Read_UINT16(s, challenge->Reserved1);
|
|
|
|
Stream_Read_UINT32(s, challenge->Reserved2);
|
|
|
|
Stream_Read(s, challenge->Timestamp, 8);
|
|
|
|
Stream_Read(s, challenge->ClientChallenge, 8);
|
|
|
|
Stream_Read_UINT32(s, challenge->Reserved3);
|
2013-04-30 06:35:15 +04:00
|
|
|
size = Stream_Length(s) - Stream_GetPosition(s);
|
2014-08-18 21:34:47 +04:00
|
|
|
challenge->AvPairs = (NTLM_AV_PAIR*) malloc(size);
|
2014-06-07 08:17:11 +04:00
|
|
|
|
|
|
|
if (!challenge->AvPairs)
|
|
|
|
return -1;
|
|
|
|
|
2012-12-10 23:08:06 +04:00
|
|
|
Stream_Read(s, challenge->AvPairs, size);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-07-02 01:05:31 +04:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_write_ntlm_v2_client_challenge(wStream* s, NTLMv2_CLIENT_CHALLENGE* challenge)
|
2012-07-02 01:05:31 +04:00
|
|
|
{
|
|
|
|
ULONG length;
|
2012-12-10 23:08:06 +04:00
|
|
|
Stream_Write_UINT8(s, challenge->RespType);
|
|
|
|
Stream_Write_UINT8(s, challenge->HiRespType);
|
|
|
|
Stream_Write_UINT16(s, challenge->Reserved1);
|
|
|
|
Stream_Write_UINT32(s, challenge->Reserved2);
|
|
|
|
Stream_Write(s, challenge->Timestamp, 8);
|
|
|
|
Stream_Write(s, challenge->ClientChallenge, 8);
|
|
|
|
Stream_Write_UINT32(s, challenge->Reserved3);
|
2012-07-02 01:05:31 +04:00
|
|
|
length = ntlm_av_pair_list_length(challenge->AvPairs);
|
2012-12-10 23:08:06 +04:00
|
|
|
Stream_Write(s, challenge->AvPairs, length);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-07-02 01:05:31 +04:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response)
|
2012-07-02 01:05:31 +04:00
|
|
|
{
|
2012-12-10 23:08:06 +04:00
|
|
|
Stream_Read(s, response->Response, 16);
|
2014-06-07 08:17:11 +04:00
|
|
|
return ntlm_read_ntlm_v2_client_challenge(s, &(response->Challenge));
|
2012-07-02 01:05:31 +04:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_write_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response)
|
2012-07-02 01:05:31 +04:00
|
|
|
{
|
2012-12-10 23:08:06 +04:00
|
|
|
Stream_Write(s, response->Response, 16);
|
2014-06-07 08:17:11 +04:00
|
|
|
return ntlm_write_ntlm_v2_client_challenge(s, &(response->Challenge));
|
2012-07-02 01:05:31 +04:00
|
|
|
}
|
|
|
|
|
2012-02-25 18:55:52 +04:00
|
|
|
/**
|
|
|
|
* Get current time, in tenths of microseconds since midnight of January 1, 1601.
|
|
|
|
* @param[out] timestamp 64-bit little-endian timestamp
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_current_time(BYTE* timestamp)
|
2012-02-25 18:55:52 +04:00
|
|
|
{
|
2012-06-30 00:43:07 +04:00
|
|
|
FILETIME filetime;
|
|
|
|
ULARGE_INTEGER time64;
|
|
|
|
GetSystemTimeAsFileTime(&filetime);
|
|
|
|
time64.LowPart = filetime.dwLowDateTime;
|
|
|
|
time64.HighPart = filetime.dwHighDateTime;
|
|
|
|
CopyMemory(timestamp, &(time64.QuadPart), 8);
|
2012-02-25 18:55:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate timestamp for AUTHENTICATE_MESSAGE.
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_generate_timestamp(NTLM_CONTEXT* context)
|
2012-02-25 18:55:52 +04:00
|
|
|
{
|
2014-06-18 22:42:35 +04:00
|
|
|
if (memcmp(context->ChallengeTimestamp, NTLM_NULL_BUFFER, 8) != 0)
|
2012-07-02 05:40:33 +04:00
|
|
|
CopyMemory(context->Timestamp, context->ChallengeTimestamp, 8);
|
|
|
|
else
|
|
|
|
ntlm_current_time(context->Timestamp);
|
2012-02-25 18:55:52 +04:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash)
|
2012-06-04 00:30:15 +04:00
|
|
|
{
|
2014-08-18 21:34:47 +04:00
|
|
|
WINPR_SAM* sam;
|
|
|
|
WINPR_SAM_ENTRY* entry;
|
|
|
|
SSPI_CREDENTIALS* credentials = context->credentials;
|
2015-10-06 17:56:24 +03:00
|
|
|
|
2016-07-22 01:58:24 +03:00
|
|
|
sam = SamOpen(context->SamFile, TRUE);
|
2014-08-18 19:22:22 +04:00
|
|
|
|
2014-06-07 08:17:11 +04:00
|
|
|
if (!sam)
|
|
|
|
return -1;
|
2012-06-04 00:30:15 +04:00
|
|
|
|
2015-10-06 17:56:24 +03:00
|
|
|
entry = SamLookupUserW(sam, (LPWSTR) credentials->identity.User, credentials->identity.UserLength * 2,
|
|
|
|
(LPWSTR) credentials->identity.Domain, credentials->identity.DomainLength * 2);
|
2012-06-04 00:30:15 +04:00
|
|
|
|
2014-06-07 08:17:11 +04:00
|
|
|
if (entry)
|
2012-06-04 00:30:15 +04:00
|
|
|
{
|
2012-07-25 04:46:21 +04:00
|
|
|
#ifdef WITH_DEBUG_NTLM
|
2014-08-18 19:22:22 +04:00
|
|
|
WLog_DBG(TAG, "NTLM Hash:");
|
2014-08-19 13:49:25 +04:00
|
|
|
winpr_HexDump(TAG, WLOG_DEBUG, entry->NtHash, 16);
|
2012-07-25 04:46:21 +04:00
|
|
|
#endif
|
2012-07-23 07:23:23 +04:00
|
|
|
NTOWFv2FromHashW(entry->NtHash,
|
2015-10-06 17:56:24 +03:00
|
|
|
(LPWSTR) credentials->identity.User, credentials->identity.UserLength * 2,
|
|
|
|
(LPWSTR) credentials->identity.Domain, credentials->identity.DomainLength * 2,
|
|
|
|
(BYTE*) hash);
|
2012-07-23 07:23:23 +04:00
|
|
|
SamFreeEntry(sam, entry);
|
|
|
|
SamClose(sam);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-07-23 07:23:23 +04:00
|
|
|
}
|
|
|
|
|
2015-10-06 17:56:24 +03:00
|
|
|
entry = SamLookupUserW(sam, (LPWSTR) credentials->identity.User, credentials->identity.UserLength * 2, NULL, 0);
|
2012-07-23 07:23:23 +04:00
|
|
|
|
2014-06-07 08:17:11 +04:00
|
|
|
if (entry)
|
2012-07-23 07:23:23 +04:00
|
|
|
{
|
2012-07-25 04:46:21 +04:00
|
|
|
#ifdef WITH_DEBUG_NTLM
|
2014-08-18 19:22:22 +04:00
|
|
|
WLog_DBG(TAG, "NTLM Hash:");
|
2014-08-19 13:49:25 +04:00
|
|
|
winpr_HexDump(TAG, WLOG_DEBUG, entry->NtHash, 16);
|
2012-07-25 04:46:21 +04:00
|
|
|
#endif
|
2012-07-23 07:23:23 +04:00
|
|
|
NTOWFv2FromHashW(entry->NtHash,
|
2015-10-06 17:56:24 +03:00
|
|
|
(LPWSTR) credentials->identity.User, credentials->identity.UserLength * 2,
|
|
|
|
(LPWSTR) credentials->identity.Domain, credentials->identity.DomainLength * 2,
|
|
|
|
(BYTE*) hash);
|
2012-07-23 07:23:23 +04:00
|
|
|
SamFreeEntry(sam, entry);
|
|
|
|
SamClose(sam);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-06-04 00:30:15 +04:00
|
|
|
}
|
2012-06-30 02:22:39 +04:00
|
|
|
else
|
|
|
|
{
|
2014-08-18 19:22:22 +04:00
|
|
|
WLog_ERR(TAG, "Error: Could not find user in SAM database");
|
2014-06-07 08:17:11 +04:00
|
|
|
return 0;
|
2012-06-30 02:22:39 +04:00
|
|
|
}
|
2014-06-07 08:17:11 +04:00
|
|
|
|
2013-08-28 19:46:07 +04:00
|
|
|
SamClose(sam);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-06-04 00:30:15 +04:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_convert_password_hash(NTLM_CONTEXT* context, BYTE* hash)
|
2013-11-06 19:02:58 +04:00
|
|
|
{
|
2014-06-07 08:17:11 +04:00
|
|
|
int status;
|
2013-11-06 19:02:58 +04:00
|
|
|
int i, hn, ln;
|
2014-08-18 21:34:47 +04:00
|
|
|
char* PasswordHash = NULL;
|
2013-11-06 19:02:58 +04:00
|
|
|
UINT32 PasswordHashLength = 0;
|
2014-08-18 21:34:47 +04:00
|
|
|
SSPI_CREDENTIALS* credentials = context->credentials;
|
2013-11-06 19:02:58 +04:00
|
|
|
/* Password contains a password hash of length (PasswordLength / SSPI_CREDENTIALS_HASH_LENGTH_FACTOR) */
|
2014-06-09 23:25:00 +04:00
|
|
|
PasswordHashLength = credentials->identity.PasswordLength / SSPI_CREDENTIALS_HASH_LENGTH_FACTOR;
|
|
|
|
status = ConvertFromUnicode(CP_UTF8, 0, (LPCWSTR) credentials->identity.Password,
|
2014-08-18 19:22:22 +04:00
|
|
|
PasswordHashLength, &PasswordHash, 0, NULL, NULL);
|
2014-06-07 08:17:11 +04:00
|
|
|
|
|
|
|
if (status <= 0)
|
|
|
|
return -1;
|
|
|
|
|
2013-11-06 19:02:58 +04:00
|
|
|
CharUpperBuffA(PasswordHash, PasswordHashLength);
|
|
|
|
|
|
|
|
for (i = 0; i < 32; i += 2)
|
|
|
|
{
|
|
|
|
hn = PasswordHash[i] > '9' ? PasswordHash[i] - 'A' + 10 : PasswordHash[i] - '0';
|
|
|
|
ln = PasswordHash[i + 1] > '9' ? PasswordHash[i + 1] - 'A' + 10 : PasswordHash[i + 1] - '0';
|
|
|
|
hash[i / 2] = (hn << 4) | ln;
|
|
|
|
}
|
|
|
|
|
|
|
|
free(PasswordHash);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2013-11-06 19:02:58 +04:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash)
|
2012-02-25 18:55:52 +04:00
|
|
|
{
|
2014-08-18 21:34:47 +04:00
|
|
|
SSPI_CREDENTIALS* credentials = context->credentials;
|
2013-11-06 19:02:58 +04:00
|
|
|
|
2014-06-19 00:02:13 +04:00
|
|
|
if (memcmp(context->NtlmV2Hash, NTLM_NULL_BUFFER, 16) != 0)
|
|
|
|
return 1;
|
|
|
|
|
2014-06-18 22:42:35 +04:00
|
|
|
if (memcmp(context->NtlmHash, NTLM_NULL_BUFFER, 16) != 0)
|
2014-06-09 23:25:00 +04:00
|
|
|
{
|
|
|
|
NTOWFv2FromHashW(context->NtlmHash,
|
2015-10-06 17:56:24 +03:00
|
|
|
(LPWSTR) credentials->identity.User, credentials->identity.UserLength * 2,
|
|
|
|
(LPWSTR) credentials->identity.Domain, credentials->identity.DomainLength * 2,
|
|
|
|
(BYTE*) hash);
|
2014-06-09 23:25:00 +04:00
|
|
|
}
|
|
|
|
else if (credentials->identity.PasswordLength > 256)
|
|
|
|
{
|
2013-11-06 19:02:58 +04:00
|
|
|
/* Special case for WinPR: password hash */
|
2014-06-09 23:25:00 +04:00
|
|
|
if (ntlm_convert_password_hash(context, context->NtlmHash) < 0)
|
2014-06-07 08:17:11 +04:00
|
|
|
return -1;
|
2013-11-06 19:02:58 +04:00
|
|
|
|
2014-06-09 23:25:00 +04:00
|
|
|
NTOWFv2FromHashW(context->NtlmHash,
|
2015-10-06 17:56:24 +03:00
|
|
|
(LPWSTR) credentials->identity.User, credentials->identity.UserLength * 2,
|
|
|
|
(LPWSTR) credentials->identity.Domain, credentials->identity.DomainLength * 2,
|
|
|
|
(BYTE*) hash);
|
2013-11-06 19:02:58 +04:00
|
|
|
}
|
2015-10-09 20:48:41 +03:00
|
|
|
else if (credentials->identity.Password)
|
2012-05-21 04:36:57 +04:00
|
|
|
{
|
2014-06-09 23:25:00 +04:00
|
|
|
NTOWFv2W((LPWSTR) credentials->identity.Password, credentials->identity.PasswordLength * 2,
|
2015-10-06 17:56:24 +03:00
|
|
|
(LPWSTR) credentials->identity.User, credentials->identity.UserLength * 2,
|
|
|
|
(LPWSTR) credentials->identity.Domain, credentials->identity.DomainLength * 2, (BYTE*) hash);
|
2012-05-21 04:36:57 +04:00
|
|
|
}
|
2014-06-19 00:02:13 +04:00
|
|
|
else if (context->UseSamFileDatabase)
|
2012-05-21 04:36:57 +04:00
|
|
|
{
|
|
|
|
ntlm_fetch_ntlm_v2_hash(context, hash);
|
|
|
|
}
|
2014-06-07 08:17:11 +04:00
|
|
|
|
|
|
|
return 1;
|
2012-02-25 18:55:52 +04:00
|
|
|
}
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_compute_lm_v2_response(NTLM_CONTEXT* context)
|
2012-02-25 18:55:52 +04:00
|
|
|
{
|
2014-08-18 21:34:47 +04:00
|
|
|
BYTE* response;
|
2016-02-24 23:45:09 +03:00
|
|
|
BYTE value[WINPR_MD5_DIGEST_LENGTH];
|
2012-02-25 18:55:52 +04:00
|
|
|
|
2012-06-30 02:22:39 +04:00
|
|
|
if (context->LmCompatibilityLevel < 2)
|
|
|
|
{
|
2014-06-07 08:17:11 +04:00
|
|
|
if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24))
|
|
|
|
return -1;
|
|
|
|
|
2012-06-30 02:22:39 +04:00
|
|
|
ZeroMemory(context->LmChallengeResponse.pvBuffer, 24);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-06-30 02:22:39 +04:00
|
|
|
}
|
|
|
|
|
2012-02-25 18:55:52 +04:00
|
|
|
/* Compute the NTLMv2 hash */
|
2014-06-07 08:17:11 +04:00
|
|
|
|
2014-06-09 23:25:00 +04:00
|
|
|
if (ntlm_compute_ntlm_v2_hash(context, context->NtlmV2Hash) < 0)
|
2014-06-07 08:17:11 +04:00
|
|
|
return -1;
|
2012-02-25 18:55:52 +04:00
|
|
|
|
|
|
|
/* Concatenate the server and client challenges */
|
2012-05-25 01:05:12 +04:00
|
|
|
CopyMemory(value, context->ServerChallenge, 8);
|
|
|
|
CopyMemory(&value[8], context->ClientChallenge, 8);
|
2012-02-25 18:55:52 +04:00
|
|
|
|
2014-06-07 08:17:11 +04:00
|
|
|
if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24))
|
|
|
|
return -1;
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
response = (BYTE*) context->LmChallengeResponse.pvBuffer;
|
2012-02-25 18:55:52 +04:00
|
|
|
/* Compute the HMAC-MD5 hash of the resulting value using the NTLMv2 hash as the key */
|
2016-02-24 23:45:09 +03:00
|
|
|
winpr_HMAC(WINPR_MD_MD5, (void*) context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH,
|
2016-02-28 13:12:17 +03:00
|
|
|
(BYTE*) value, WINPR_MD5_DIGEST_LENGTH,
|
|
|
|
(BYTE*) response, WINPR_MD5_DIGEST_LENGTH);
|
2012-02-25 18:55:52 +04:00
|
|
|
/* Concatenate the resulting HMAC-MD5 hash and the client challenge, giving us the LMv2 response (24 bytes) */
|
2012-05-25 01:05:12 +04:00
|
|
|
CopyMemory(&response[16], context->ClientChallenge, 8);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-02-25 18:55:52 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Compute NTLMv2 Response.
|
|
|
|
* NTLMv2_RESPONSE @msdn{cc236653}
|
2012-02-25 18:55:52 +04:00
|
|
|
* NTLMv2 Authentication @msdn{cc236700}
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context)
|
2012-02-25 18:55:52 +04:00
|
|
|
{
|
2014-08-18 21:34:47 +04:00
|
|
|
BYTE* blob;
|
2016-02-24 23:45:09 +03:00
|
|
|
BYTE nt_proof_str[WINPR_MD5_DIGEST_LENGTH];
|
2012-03-16 04:37:38 +04:00
|
|
|
SecBuffer ntlm_v2_temp;
|
|
|
|
SecBuffer ntlm_v2_temp_chal;
|
2013-01-31 05:39:57 +04:00
|
|
|
PSecBuffer TargetInfo;
|
2014-08-18 21:34:47 +04:00
|
|
|
SSPI_CREDENTIALS* credentials;
|
2014-06-18 22:42:35 +04:00
|
|
|
credentials = context->credentials;
|
2013-01-31 05:39:57 +04:00
|
|
|
TargetInfo = &context->ChallengeTargetInfo;
|
|
|
|
|
2014-06-07 08:17:11 +04:00
|
|
|
if (!sspi_SecBufferAlloc(&ntlm_v2_temp, TargetInfo->cbBuffer + 28))
|
|
|
|
return -1;
|
2012-02-25 18:55:52 +04:00
|
|
|
|
2012-06-30 00:43:07 +04:00
|
|
|
ZeroMemory(ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
|
2014-08-18 21:34:47 +04:00
|
|
|
blob = (BYTE*) ntlm_v2_temp.pvBuffer;
|
2012-02-25 18:55:52 +04:00
|
|
|
|
|
|
|
/* Compute the NTLMv2 hash */
|
2014-06-07 08:17:11 +04:00
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
if (ntlm_compute_ntlm_v2_hash(context, (BYTE*) context->NtlmV2Hash) < 0)
|
2014-06-07 08:17:11 +04:00
|
|
|
return -1;
|
2012-02-25 18:55:52 +04:00
|
|
|
|
|
|
|
#ifdef WITH_DEBUG_NTLM
|
2014-08-18 19:22:22 +04:00
|
|
|
WLog_DBG(TAG, "Password (length = %d)", credentials->identity.PasswordLength * 2);
|
2014-08-19 13:49:25 +04:00
|
|
|
winpr_HexDump(TAG, WLOG_DEBUG, (BYTE*) credentials->identity.Password, credentials->identity.PasswordLength * 2);
|
2014-08-18 19:22:22 +04:00
|
|
|
WLog_DBG(TAG, "Username (length = %d)", credentials->identity.UserLength * 2);
|
2014-08-19 13:49:25 +04:00
|
|
|
winpr_HexDump(TAG, WLOG_DEBUG, (BYTE*) credentials->identity.User, credentials->identity.UserLength * 2);
|
2014-08-18 19:22:22 +04:00
|
|
|
WLog_DBG(TAG, "Domain (length = %d)", credentials->identity.DomainLength * 2);
|
2014-08-19 13:49:25 +04:00
|
|
|
winpr_HexDump(TAG, WLOG_DEBUG, (BYTE*) credentials->identity.Domain, credentials->identity.DomainLength * 2);
|
2014-08-18 19:22:22 +04:00
|
|
|
WLog_DBG(TAG, "Workstation (length = %d)", context->Workstation.Length);
|
2014-08-19 13:49:25 +04:00
|
|
|
winpr_HexDump(TAG, WLOG_DEBUG, (BYTE*) context->Workstation.Buffer, context->Workstation.Length);
|
2014-08-18 19:22:22 +04:00
|
|
|
WLog_DBG(TAG, "NTOWFv2, NTLMv2 Hash");
|
2016-02-24 23:45:09 +03:00
|
|
|
winpr_HexDump(TAG, WLOG_DEBUG, context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH);
|
2012-02-25 18:55:52 +04:00
|
|
|
#endif
|
|
|
|
/* Construct temp */
|
|
|
|
blob[0] = 1; /* RespType (1 byte) */
|
|
|
|
blob[1] = 1; /* HighRespType (1 byte) */
|
|
|
|
/* Reserved1 (2 bytes) */
|
|
|
|
/* Reserved2 (4 bytes) */
|
2012-07-02 05:40:33 +04:00
|
|
|
CopyMemory(&blob[8], context->Timestamp, 8); /* Timestamp (8 bytes) */
|
2012-05-25 01:05:12 +04:00
|
|
|
CopyMemory(&blob[16], context->ClientChallenge, 8); /* ClientChallenge (8 bytes) */
|
2012-02-25 18:55:52 +04:00
|
|
|
/* Reserved3 (4 bytes) */
|
2013-01-31 05:39:57 +04:00
|
|
|
CopyMemory(&blob[28], TargetInfo->pvBuffer, TargetInfo->cbBuffer);
|
2012-02-25 18:55:52 +04:00
|
|
|
#ifdef WITH_DEBUG_NTLM
|
2014-08-18 19:22:22 +04:00
|
|
|
WLog_DBG(TAG, "NTLMv2 Response Temp Blob");
|
2014-08-19 13:49:25 +04:00
|
|
|
winpr_HexDump(TAG, WLOG_DEBUG, ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
|
2012-02-25 18:55:52 +04:00
|
|
|
#endif
|
|
|
|
|
|
|
|
/* Concatenate server challenge with temp */
|
2014-06-07 08:17:11 +04:00
|
|
|
|
|
|
|
if (!sspi_SecBufferAlloc(&ntlm_v2_temp_chal, ntlm_v2_temp.cbBuffer + 8))
|
|
|
|
return -1;
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
blob = (BYTE*) ntlm_v2_temp_chal.pvBuffer;
|
2012-05-25 01:05:12 +04:00
|
|
|
CopyMemory(blob, context->ServerChallenge, 8);
|
|
|
|
CopyMemory(&blob[8], ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
|
2016-02-24 23:45:09 +03:00
|
|
|
winpr_HMAC(WINPR_MD_MD5, (BYTE*) context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH,
|
2016-02-28 13:12:17 +03:00
|
|
|
(BYTE*) ntlm_v2_temp_chal.pvBuffer, ntlm_v2_temp_chal.cbBuffer,
|
|
|
|
(BYTE*) nt_proof_str, WINPR_MD5_DIGEST_LENGTH);
|
2012-02-25 18:55:52 +04:00
|
|
|
|
|
|
|
/* NtChallengeResponse, Concatenate NTProofStr with temp */
|
2014-08-18 19:22:22 +04:00
|
|
|
|
2014-06-07 08:17:11 +04:00
|
|
|
if (!sspi_SecBufferAlloc(&context->NtChallengeResponse, ntlm_v2_temp.cbBuffer + 16))
|
|
|
|
return -1;
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
blob = (BYTE*) context->NtChallengeResponse.pvBuffer;
|
2012-05-25 01:05:12 +04:00
|
|
|
CopyMemory(blob, nt_proof_str, 16);
|
|
|
|
CopyMemory(&blob[16], ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
|
2012-02-25 18:55:52 +04:00
|
|
|
/* Compute SessionBaseKey, the HMAC-MD5 hash of NTProofStr using the NTLMv2 hash as the key */
|
2016-02-24 23:45:09 +03:00
|
|
|
winpr_HMAC(WINPR_MD_MD5, (BYTE*) context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH,
|
2016-02-28 13:12:17 +03:00
|
|
|
(BYTE*) nt_proof_str, WINPR_MD5_DIGEST_LENGTH,
|
|
|
|
(BYTE*) context->SessionBaseKey, WINPR_MD5_DIGEST_LENGTH);
|
2012-02-25 18:55:52 +04:00
|
|
|
sspi_SecBufferFree(&ntlm_v2_temp);
|
|
|
|
sspi_SecBufferFree(&ntlm_v2_temp_chal);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-02-25 18:55:52 +04:00
|
|
|
}
|
2012-02-25 19:20:12 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* Encrypt the given plain text using RC4 and the given key.
|
|
|
|
* @param key RC4 key
|
|
|
|
* @param length text length
|
|
|
|
* @param plaintext plain text
|
|
|
|
* @param ciphertext cipher text
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_rc4k(BYTE* key, int length, BYTE* plaintext, BYTE* ciphertext)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
2016-02-29 11:00:02 +03:00
|
|
|
WINPR_RC4_CTX* rc4 = winpr_RC4_New(key, 16);
|
|
|
|
if (rc4)
|
2016-02-28 13:12:17 +03:00
|
|
|
{
|
2016-02-29 11:00:02 +03:00
|
|
|
winpr_RC4_Update(rc4, length, plaintext, ciphertext);
|
2016-02-28 13:12:17 +03:00
|
|
|
winpr_RC4_Free(rc4);
|
|
|
|
}
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate client challenge (8-byte nonce).
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_generate_client_challenge(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
|
|
|
/* ClientChallenge is used in computation of LMv2 and NTLMv2 responses */
|
2014-06-18 22:42:35 +04:00
|
|
|
if (memcmp(context->ClientChallenge, NTLM_NULL_BUFFER, 8) == 0)
|
2015-10-08 23:48:58 +03:00
|
|
|
winpr_RAND(context->ClientChallenge, 8);
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate server challenge (8-byte nonce).
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_generate_server_challenge(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
2014-06-18 22:42:35 +04:00
|
|
|
if (memcmp(context->ServerChallenge, NTLM_NULL_BUFFER, 8) == 0)
|
2015-10-08 23:48:58 +03:00
|
|
|
winpr_RAND(context->ServerChallenge, 8);
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Generate KeyExchangeKey (the 128-bit SessionBaseKey).
|
2012-02-25 19:20:12 +04:00
|
|
|
* @msdn{cc236710}
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_generate_key_exchange_key(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
|
|
|
/* In NTLMv2, KeyExchangeKey is the 128-bit SessionBaseKey */
|
2012-05-25 01:05:12 +04:00
|
|
|
CopyMemory(context->KeyExchangeKey, context->SessionBaseKey, 16);
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate RandomSessionKey (16-byte nonce).
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_generate_random_session_key(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
2015-10-08 23:48:58 +03:00
|
|
|
winpr_RAND(context->RandomSessionKey, 16);
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Generate ExportedSessionKey (the RandomSessionKey, exported)
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_generate_exported_session_key(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
2012-05-25 01:05:12 +04:00
|
|
|
CopyMemory(context->ExportedSessionKey, context->RandomSessionKey, 16);
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Encrypt RandomSessionKey (RC4-encrypted RandomSessionKey, using KeyExchangeKey as the key).
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_encrypt_random_session_key(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
|
|
|
/* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the KeyExchangeKey */
|
|
|
|
ntlm_rc4k(context->KeyExchangeKey, 16, context->RandomSessionKey, context->EncryptedRandomSessionKey);
|
|
|
|
}
|
|
|
|
|
2012-03-19 06:02:23 +04:00
|
|
|
/**
|
|
|
|
* Decrypt RandomSessionKey (RC4-encrypted RandomSessionKey, using KeyExchangeKey as the key).
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context)
|
2012-03-19 06:02:23 +04:00
|
|
|
{
|
|
|
|
/* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the KeyExchangeKey */
|
2014-06-18 22:42:35 +04:00
|
|
|
|
|
|
|
/**
|
|
|
|
* if (NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
|
|
|
|
* Set RandomSessionKey to RC4K(KeyExchangeKey, AUTHENTICATE_MESSAGE.EncryptedRandomSessionKey)
|
|
|
|
* else
|
|
|
|
* Set RandomSessionKey to KeyExchangeKey
|
|
|
|
*/
|
|
|
|
if (context->NegotiateKeyExchange)
|
|
|
|
ntlm_rc4k(context->KeyExchangeKey, 16, context->EncryptedRandomSessionKey, context->RandomSessionKey);
|
|
|
|
else
|
|
|
|
CopyMemory(context->RandomSessionKey, context->KeyExchangeKey, 16);
|
2012-03-19 06:02:23 +04:00
|
|
|
}
|
|
|
|
|
2012-02-25 19:20:12 +04:00
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Generate signing key.
|
2012-02-25 19:20:12 +04:00
|
|
|
* @msdn{cc236711}
|
|
|
|
* @param exported_session_key ExportedSessionKey
|
|
|
|
* @param sign_magic Sign magic string
|
|
|
|
* @param signing_key Destination signing key
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_generate_signing_key(BYTE* exported_session_key, PSecBuffer sign_magic, BYTE* signing_key)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
|
|
|
int length;
|
2014-08-18 21:34:47 +04:00
|
|
|
BYTE* value;
|
2015-10-06 17:56:24 +03:00
|
|
|
|
2016-02-24 23:45:09 +03:00
|
|
|
length = WINPR_MD5_DIGEST_LENGTH + sign_magic->cbBuffer;
|
2014-08-18 21:34:47 +04:00
|
|
|
value = (BYTE*) malloc(length);
|
2012-02-25 19:20:12 +04:00
|
|
|
|
2014-06-07 08:17:11 +04:00
|
|
|
if (!value)
|
|
|
|
return -1;
|
|
|
|
|
2012-02-25 19:20:12 +04:00
|
|
|
/* Concatenate ExportedSessionKey with sign magic */
|
2016-02-24 23:45:09 +03:00
|
|
|
CopyMemory(value, exported_session_key, WINPR_MD5_DIGEST_LENGTH);
|
|
|
|
CopyMemory(&value[WINPR_MD5_DIGEST_LENGTH], sign_magic->pvBuffer, sign_magic->cbBuffer);
|
2016-11-21 19:28:54 +03:00
|
|
|
|
2016-11-24 16:53:19 +03:00
|
|
|
if (!winpr_Digest(WINPR_MD_MD5, value, length, signing_key, WINPR_MD5_DIGEST_LENGTH))
|
2016-11-21 19:28:54 +03:00
|
|
|
{
|
|
|
|
free(value);
|
|
|
|
return -1;
|
|
|
|
}
|
2012-05-06 06:39:00 +04:00
|
|
|
free(value);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Generate client signing key (ClientSigningKey).
|
2012-02-25 19:20:12 +04:00
|
|
|
* @msdn{cc236711}
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_generate_client_signing_key(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
2014-06-07 08:17:11 +04:00
|
|
|
SecBuffer signMagic;
|
2014-08-18 21:34:47 +04:00
|
|
|
signMagic.pvBuffer = (void*) NTLM_CLIENT_SIGN_MAGIC;
|
2014-06-07 08:17:11 +04:00
|
|
|
signMagic.cbBuffer = sizeof(NTLM_CLIENT_SIGN_MAGIC);
|
|
|
|
ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic, context->ClientSigningKey);
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Generate server signing key (ServerSigningKey).
|
2012-02-25 19:20:12 +04:00
|
|
|
* @msdn{cc236711}
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_generate_server_signing_key(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
2014-06-07 08:17:11 +04:00
|
|
|
SecBuffer signMagic;
|
2014-08-18 21:34:47 +04:00
|
|
|
signMagic.pvBuffer = (void*) NTLM_SERVER_SIGN_MAGIC;
|
2014-06-07 08:17:11 +04:00
|
|
|
signMagic.cbBuffer = sizeof(NTLM_SERVER_SIGN_MAGIC);
|
|
|
|
ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic, context->ServerSigningKey);
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Generate sealing key.
|
2012-02-25 19:20:12 +04:00
|
|
|
* @msdn{cc236712}
|
|
|
|
* @param exported_session_key ExportedSessionKey
|
|
|
|
* @param seal_magic Seal magic string
|
|
|
|
* @param sealing_key Destination sealing key
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
int ntlm_generate_sealing_key(BYTE* exported_session_key, PSecBuffer seal_magic, BYTE* sealing_key)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
2014-08-18 21:34:47 +04:00
|
|
|
BYTE* p;
|
2012-03-16 04:37:38 +04:00
|
|
|
SecBuffer buffer;
|
2012-02-25 19:20:12 +04:00
|
|
|
|
2016-02-24 23:45:09 +03:00
|
|
|
if (!sspi_SecBufferAlloc(&buffer, WINPR_MD5_DIGEST_LENGTH + seal_magic->cbBuffer))
|
2014-06-07 08:17:11 +04:00
|
|
|
return -1;
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
p = (BYTE*) buffer.pvBuffer;
|
2012-02-25 19:20:12 +04:00
|
|
|
/* Concatenate ExportedSessionKey with seal magic */
|
2016-02-24 23:45:09 +03:00
|
|
|
CopyMemory(p, exported_session_key, WINPR_MD5_DIGEST_LENGTH);
|
|
|
|
CopyMemory(&p[WINPR_MD5_DIGEST_LENGTH], seal_magic->pvBuffer, seal_magic->cbBuffer);
|
2016-11-21 19:28:54 +03:00
|
|
|
|
2016-11-24 16:53:19 +03:00
|
|
|
if (!winpr_Digest(WINPR_MD_MD5, buffer.pvBuffer, buffer.cbBuffer, sealing_key, WINPR_MD5_DIGEST_LENGTH))
|
2016-11-21 19:28:54 +03:00
|
|
|
{
|
|
|
|
sspi_SecBufferFree(&buffer);
|
|
|
|
return -1;
|
|
|
|
}
|
2016-11-24 16:53:19 +03:00
|
|
|
|
2012-02-25 19:20:12 +04:00
|
|
|
sspi_SecBufferFree(&buffer);
|
2014-06-07 08:17:11 +04:00
|
|
|
return 1;
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Generate client sealing key (ClientSealingKey).
|
2012-02-25 19:20:12 +04:00
|
|
|
* @msdn{cc236712}
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_generate_client_sealing_key(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
2014-06-07 08:17:11 +04:00
|
|
|
SecBuffer sealMagic;
|
2014-08-18 21:34:47 +04:00
|
|
|
sealMagic.pvBuffer = (void*) NTLM_CLIENT_SEAL_MAGIC;
|
2014-06-07 08:17:11 +04:00
|
|
|
sealMagic.cbBuffer = sizeof(NTLM_CLIENT_SEAL_MAGIC);
|
|
|
|
ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic, context->ClientSealingKey);
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
2014-08-18 19:22:22 +04:00
|
|
|
* Generate server sealing key (ServerSealingKey).
|
2012-02-25 19:20:12 +04:00
|
|
|
* @msdn{cc236712}
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_generate_server_sealing_key(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
2014-06-07 08:17:11 +04:00
|
|
|
SecBuffer sealMagic;
|
2014-08-18 21:34:47 +04:00
|
|
|
sealMagic.pvBuffer = (void*) NTLM_SERVER_SEAL_MAGIC;
|
2014-06-07 08:17:11 +04:00
|
|
|
sealMagic.cbBuffer = sizeof(NTLM_SERVER_SEAL_MAGIC);
|
|
|
|
ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic, context->ServerSealingKey);
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Initialize RC4 stream cipher states for sealing.
|
|
|
|
* @param NTLM context
|
|
|
|
*/
|
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_init_rc4_seal_states(NTLM_CONTEXT* context)
|
2012-02-25 19:20:12 +04:00
|
|
|
{
|
2012-03-19 06:24:49 +04:00
|
|
|
if (context->server)
|
|
|
|
{
|
|
|
|
context->SendSigningKey = context->ServerSigningKey;
|
|
|
|
context->RecvSigningKey = context->ClientSigningKey;
|
|
|
|
context->SendSealingKey = context->ClientSealingKey;
|
|
|
|
context->RecvSealingKey = context->ServerSealingKey;
|
2016-02-29 11:00:02 +03:00
|
|
|
context->SendRc4Seal = winpr_RC4_New(context->ServerSealingKey, 16);
|
|
|
|
context->RecvRc4Seal = winpr_RC4_New(context->ClientSealingKey, 16);
|
2012-03-19 06:24:49 +04:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
context->SendSigningKey = context->ClientSigningKey;
|
|
|
|
context->RecvSigningKey = context->ServerSigningKey;
|
|
|
|
context->SendSealingKey = context->ServerSealingKey;
|
|
|
|
context->RecvSealingKey = context->ClientSealingKey;
|
2016-02-29 11:00:02 +03:00
|
|
|
context->SendRc4Seal = winpr_RC4_New(context->ClientSealingKey, 16);
|
|
|
|
context->RecvRc4Seal = winpr_RC4_New(context->ServerSealingKey, 16);
|
2012-03-19 06:24:49 +04:00
|
|
|
}
|
2012-02-25 19:20:12 +04:00
|
|
|
}
|
2012-02-25 20:48:08 +04:00
|
|
|
|
2014-08-18 21:34:47 +04:00
|
|
|
void ntlm_compute_message_integrity_check(NTLM_CONTEXT* context)
|
2012-02-25 20:48:08 +04:00
|
|
|
{
|
|
|
|
/*
|
|
|
|
* Compute the HMAC-MD5 hash of ConcatenationOf(NEGOTIATE_MESSAGE,
|
|
|
|
* CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE) using the ExportedSessionKey
|
|
|
|
*/
|
2015-10-08 23:48:58 +03:00
|
|
|
|
2016-11-24 16:53:19 +03:00
|
|
|
WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
|
|
|
|
|
|
|
|
if (!hmac)
|
|
|
|
return;
|
2016-11-21 19:28:54 +03:00
|
|
|
|
2016-11-24 16:53:19 +03:00
|
|
|
if (winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->ExportedSessionKey, WINPR_MD5_DIGEST_LENGTH))
|
2016-11-21 19:28:54 +03:00
|
|
|
{
|
|
|
|
winpr_HMAC_Update(hmac, (BYTE*) context->NegotiateMessage.pvBuffer, context->NegotiateMessage.cbBuffer);
|
|
|
|
winpr_HMAC_Update(hmac, (BYTE*) context->ChallengeMessage.pvBuffer, context->ChallengeMessage.cbBuffer);
|
|
|
|
winpr_HMAC_Update(hmac, (BYTE*) context->AuthenticateMessage.pvBuffer, context->AuthenticateMessage.cbBuffer);
|
|
|
|
winpr_HMAC_Final(hmac, context->MessageIntegrityCheck, WINPR_MD5_DIGEST_LENGTH);
|
|
|
|
}
|
2016-11-24 16:53:19 +03:00
|
|
|
winpr_HMAC_Free(hmac);
|
2012-02-25 20:48:08 +04:00
|
|
|
}
|