Milestone 5: deliver embedded RDP sessions and lifecycle hardening
This commit is contained in:
1584
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm.c
vendored
Normal file
1584
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
301
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm.h
vendored
Normal file
301
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm.h
vendored
Normal file
@@ -0,0 +1,301 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* NTLM Security Package
|
||||
*
|
||||
* Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WINPR_SSPI_NTLM_PRIVATE_H
|
||||
#define WINPR_SSPI_NTLM_PRIVATE_H
|
||||
|
||||
#include <winpr/sspi.h>
|
||||
#include <winpr/windows.h>
|
||||
|
||||
#include <winpr/nt.h>
|
||||
#include <winpr/crypto.h>
|
||||
|
||||
#include "../sspi.h"
|
||||
|
||||
#define MESSAGE_TYPE_NEGOTIATE 1
|
||||
#define MESSAGE_TYPE_CHALLENGE 2
|
||||
#define MESSAGE_TYPE_AUTHENTICATE 3
|
||||
|
||||
#define NTLMSSP_NEGOTIATE_56 0x80000000 /* W (0) */
|
||||
#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 /* V (1) */
|
||||
#define NTLMSSP_NEGOTIATE_128 0x20000000 /* U (2) */
|
||||
#define NTLMSSP_RESERVED1 0x10000000 /* r1 (3) */
|
||||
#define NTLMSSP_RESERVED2 0x08000000 /* r2 (4) */
|
||||
#define NTLMSSP_RESERVED3 0x04000000 /* r3 (5) */
|
||||
#define NTLMSSP_NEGOTIATE_VERSION 0x02000000 /* T (6) */
|
||||
#define NTLMSSP_RESERVED4 0x01000000 /* r4 (7) */
|
||||
#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 /* S (8) */
|
||||
#define NTLMSSP_REQUEST_NON_NT_SESSION_KEY 0x00400000 /* R (9) */
|
||||
#define NTLMSSP_RESERVED5 0x00200000 /* r5 (10) */
|
||||
#define NTLMSSP_NEGOTIATE_IDENTIFY 0x00100000 /* Q (11) */
|
||||
#define NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY 0x00080000 /* P (12) */
|
||||
#define NTLMSSP_RESERVED6 0x00040000 /* r6 (13) */
|
||||
#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 /* O (14) */
|
||||
#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 /* N (15) */
|
||||
#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 /* M (16) */
|
||||
#define NTLMSSP_RESERVED7 0x00004000 /* r7 (17) */
|
||||
#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000 /* L (18) */
|
||||
#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000 /* K (19) */
|
||||
#define NTLMSSP_NEGOTIATE_ANONYMOUS 0x00000800 /* J (20) */
|
||||
#define NTLMSSP_RESERVED8 0x00000400 /* r8 (21) */
|
||||
#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 /* H (22) */
|
||||
#define NTLMSSP_RESERVED9 0x00000100 /* r9 (23) */
|
||||
#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 /* G (24) */
|
||||
#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 /* F (25) */
|
||||
#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* E (26) */
|
||||
#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* D (27) */
|
||||
#define NTLMSSP_RESERVED10 0x00000008 /* r10 (28) */
|
||||
#define NTLMSSP_REQUEST_TARGET 0x00000004 /* C (29) */
|
||||
#define NTLMSSP_NEGOTIATE_OEM 0x00000002 /* B (30) */
|
||||
#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 /* A (31) */
|
||||
|
||||
typedef enum
|
||||
{
|
||||
NTLM_STATE_INITIAL,
|
||||
NTLM_STATE_NEGOTIATE,
|
||||
NTLM_STATE_CHALLENGE,
|
||||
NTLM_STATE_AUTHENTICATE,
|
||||
NTLM_STATE_FINAL
|
||||
} NTLM_STATE;
|
||||
|
||||
#ifdef __MINGW32__
|
||||
typedef MSV1_0_AVID NTLM_AV_ID;
|
||||
|
||||
#if __MINGW64_VERSION_MAJOR < 9
|
||||
enum
|
||||
{
|
||||
MsvAvTimestamp = MsvAvFlags + 1,
|
||||
MsvAvRestrictions,
|
||||
MsvAvTargetName,
|
||||
MsvAvChannelBindings,
|
||||
MsvAvSingleHost = MsvAvRestrictions
|
||||
};
|
||||
|
||||
#else
|
||||
#ifndef MsvAvSingleHost
|
||||
#define MsvAvSingleHost MsvAvRestrictions
|
||||
#endif
|
||||
#endif
|
||||
#else
|
||||
typedef enum
|
||||
{
|
||||
MsvAvEOL,
|
||||
MsvAvNbComputerName,
|
||||
MsvAvNbDomainName,
|
||||
MsvAvDnsComputerName,
|
||||
MsvAvDnsDomainName,
|
||||
MsvAvDnsTreeName,
|
||||
MsvAvFlags,
|
||||
MsvAvTimestamp,
|
||||
MsvAvSingleHost,
|
||||
MsvAvTargetName,
|
||||
MsvAvChannelBindings
|
||||
} NTLM_AV_ID;
|
||||
#endif /* __MINGW32__ */
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT16 AvId;
|
||||
UINT16 AvLen;
|
||||
} NTLM_AV_PAIR;
|
||||
|
||||
#define MSV_AV_FLAGS_AUTHENTICATION_CONSTRAINED 0x00000001
|
||||
#define MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK 0x00000002
|
||||
#define MSV_AV_FLAGS_TARGET_SPN_UNTRUSTED_SOURCE 0x00000004
|
||||
|
||||
#define WINDOWS_MAJOR_VERSION_5 0x05
|
||||
#define WINDOWS_MAJOR_VERSION_6 0x06
|
||||
#define WINDOWS_MINOR_VERSION_0 0x00
|
||||
#define WINDOWS_MINOR_VERSION_1 0x01
|
||||
#define WINDOWS_MINOR_VERSION_2 0x02
|
||||
#define NTLMSSP_REVISION_W2K3 0x0F
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT8 ProductMajorVersion;
|
||||
UINT8 ProductMinorVersion;
|
||||
UINT16 ProductBuild;
|
||||
BYTE Reserved[3];
|
||||
UINT8 NTLMRevisionCurrent;
|
||||
} NTLM_VERSION_INFO;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT32 Size;
|
||||
UINT32 Z4;
|
||||
UINT32 DataPresent;
|
||||
UINT32 CustomData;
|
||||
BYTE MachineID[32];
|
||||
} NTLM_SINGLE_HOST_DATA;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BYTE Response[24];
|
||||
} NTLM_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT8 RespType;
|
||||
UINT8 HiRespType;
|
||||
UINT16 Reserved1;
|
||||
UINT32 Reserved2;
|
||||
BYTE Timestamp[8];
|
||||
BYTE ClientChallenge[8];
|
||||
UINT32 Reserved3;
|
||||
NTLM_AV_PAIR* AvPairs;
|
||||
UINT32 cbAvPairs;
|
||||
} NTLMv2_CLIENT_CHALLENGE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BYTE Response[16];
|
||||
NTLMv2_CLIENT_CHALLENGE Challenge;
|
||||
} NTLMv2_RESPONSE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
UINT16 Len;
|
||||
UINT16 MaxLen;
|
||||
PBYTE Buffer;
|
||||
UINT32 BufferOffset;
|
||||
} NTLM_MESSAGE_FIELDS;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BYTE Signature[8];
|
||||
UINT32 MessageType;
|
||||
} NTLM_MESSAGE_HEADER;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NTLM_MESSAGE_HEADER header;
|
||||
UINT32 NegotiateFlags;
|
||||
NTLM_VERSION_INFO Version;
|
||||
NTLM_MESSAGE_FIELDS DomainName;
|
||||
NTLM_MESSAGE_FIELDS Workstation;
|
||||
} NTLM_NEGOTIATE_MESSAGE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NTLM_MESSAGE_HEADER header;
|
||||
UINT32 NegotiateFlags;
|
||||
BYTE ServerChallenge[8];
|
||||
BYTE Reserved[8];
|
||||
NTLM_VERSION_INFO Version;
|
||||
NTLM_MESSAGE_FIELDS TargetName;
|
||||
NTLM_MESSAGE_FIELDS TargetInfo;
|
||||
} NTLM_CHALLENGE_MESSAGE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NTLM_MESSAGE_HEADER header;
|
||||
UINT32 NegotiateFlags;
|
||||
NTLM_VERSION_INFO Version;
|
||||
NTLM_MESSAGE_FIELDS DomainName;
|
||||
NTLM_MESSAGE_FIELDS UserName;
|
||||
NTLM_MESSAGE_FIELDS Workstation;
|
||||
NTLM_MESSAGE_FIELDS LmChallengeResponse;
|
||||
NTLM_MESSAGE_FIELDS NtChallengeResponse;
|
||||
NTLM_MESSAGE_FIELDS EncryptedRandomSessionKey;
|
||||
BYTE MessageIntegrityCheck[16];
|
||||
} NTLM_AUTHENTICATE_MESSAGE;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
BOOL server;
|
||||
BOOL NTLMv2;
|
||||
BOOL UseMIC;
|
||||
NTLM_STATE state;
|
||||
int SendSeqNum;
|
||||
int RecvSeqNum;
|
||||
char* SamFile;
|
||||
BYTE NtlmHash[16];
|
||||
BYTE NtlmV2Hash[16];
|
||||
BYTE MachineID[32];
|
||||
BOOL SendVersionInfo;
|
||||
BOOL confidentiality;
|
||||
WINPR_RC4_CTX* SendRc4Seal;
|
||||
WINPR_RC4_CTX* RecvRc4Seal;
|
||||
BYTE* SendSigningKey;
|
||||
BYTE* RecvSigningKey;
|
||||
BYTE* SendSealingKey;
|
||||
BYTE* RecvSealingKey;
|
||||
UINT32 NegotiateFlags;
|
||||
BOOL UseSamFileDatabase;
|
||||
int LmCompatibilityLevel;
|
||||
int SuppressExtendedProtection;
|
||||
BOOL SendWorkstationName;
|
||||
UNICODE_STRING Workstation;
|
||||
UNICODE_STRING ServicePrincipalName;
|
||||
SSPI_CREDENTIALS* credentials;
|
||||
BYTE* ChannelBindingToken;
|
||||
BYTE ChannelBindingsHash[16];
|
||||
SecPkgContext_Bindings Bindings;
|
||||
BOOL SendSingleHostData;
|
||||
BOOL NegotiateKeyExchange;
|
||||
NTLM_SINGLE_HOST_DATA SingleHostData;
|
||||
NTLM_NEGOTIATE_MESSAGE NEGOTIATE_MESSAGE;
|
||||
NTLM_CHALLENGE_MESSAGE CHALLENGE_MESSAGE;
|
||||
NTLM_AUTHENTICATE_MESSAGE AUTHENTICATE_MESSAGE;
|
||||
size_t MessageIntegrityCheckOffset;
|
||||
SecBuffer NegotiateMessage;
|
||||
SecBuffer ChallengeMessage;
|
||||
SecBuffer AuthenticateMessage;
|
||||
SecBuffer ChallengeTargetInfo;
|
||||
SecBuffer AuthenticateTargetInfo;
|
||||
SecBuffer TargetName;
|
||||
SecBuffer NtChallengeResponse;
|
||||
SecBuffer LmChallengeResponse;
|
||||
NTLMv2_RESPONSE NTLMv2Response;
|
||||
BYTE NtProofString[16];
|
||||
BYTE Timestamp[8];
|
||||
BYTE ChallengeTimestamp[8];
|
||||
BYTE ServerChallenge[8];
|
||||
BYTE ClientChallenge[8];
|
||||
BYTE SessionBaseKey[16];
|
||||
BYTE KeyExchangeKey[16];
|
||||
BYTE RandomSessionKey[16];
|
||||
BYTE ExportedSessionKey[16];
|
||||
BYTE EncryptedRandomSessionKey[16];
|
||||
BYTE ClientSigningKey[16];
|
||||
BYTE ClientSealingKey[16];
|
||||
BYTE ServerSigningKey[16];
|
||||
BYTE ServerSealingKey[16];
|
||||
psSspiNtlmHashCallback HashCallback;
|
||||
void* HashCallbackArg;
|
||||
} NTLM_CONTEXT;
|
||||
|
||||
char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags);
|
||||
const char* ntlm_message_type_string(UINT32 messageType);
|
||||
|
||||
const char* ntlm_state_string(NTLM_STATE state);
|
||||
void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state);
|
||||
NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm);
|
||||
BOOL ntlm_reset_cipher_state(PSecHandle phContext);
|
||||
|
||||
SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof);
|
||||
SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT* ntlm, SecBuffer* micvalue);
|
||||
|
||||
#ifdef WITH_DEBUG_NLA
|
||||
#define WITH_DEBUG_NTLM
|
||||
#endif
|
||||
|
||||
BOOL NTLM_init(void);
|
||||
|
||||
#endif /* WINPR_SSPI_NTLM_PRIVATE_H */
|
||||
808
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c
vendored
Normal file
808
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c
vendored
Normal file
@@ -0,0 +1,808 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* NTLM Security Package (AV_PAIRs)
|
||||
*
|
||||
* Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include "ntlm.h"
|
||||
#include "../sspi.h"
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
#include <winpr/tchar.h>
|
||||
#include <winpr/crypto.h>
|
||||
|
||||
#include "ntlm_compute.h"
|
||||
|
||||
#include "ntlm_av_pairs.h"
|
||||
|
||||
#if defined(WITH_DEBUG_NTLM)
|
||||
#include "../../log.h"
|
||||
#define TAG WINPR_TAG("sspi.NTLM")
|
||||
#endif
|
||||
|
||||
static BOOL ntlm_av_pair_get_next_offset(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pOffset);
|
||||
|
||||
static BOOL ntlm_av_pair_check_data(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair, size_t size)
|
||||
{
|
||||
size_t offset = 0;
|
||||
if (!pAvPair || cbAvPair < sizeof(NTLM_AV_PAIR) + size)
|
||||
return FALSE;
|
||||
if (!ntlm_av_pair_get_next_offset(pAvPair, cbAvPair, &offset))
|
||||
return FALSE;
|
||||
return cbAvPair >= offset;
|
||||
}
|
||||
|
||||
#ifdef WITH_DEBUG_NTLM
|
||||
static const char* get_av_pair_string(UINT16 pair)
|
||||
{
|
||||
switch (pair)
|
||||
{
|
||||
case MsvAvEOL:
|
||||
return "MsvAvEOL";
|
||||
case MsvAvNbComputerName:
|
||||
return "MsvAvNbComputerName";
|
||||
case MsvAvNbDomainName:
|
||||
return "MsvAvNbDomainName";
|
||||
case MsvAvDnsComputerName:
|
||||
return "MsvAvDnsComputerName";
|
||||
case MsvAvDnsDomainName:
|
||||
return "MsvAvDnsDomainName";
|
||||
case MsvAvDnsTreeName:
|
||||
return "MsvAvDnsTreeName";
|
||||
case MsvAvFlags:
|
||||
return "MsvAvFlags";
|
||||
case MsvAvTimestamp:
|
||||
return "MsvAvTimestamp";
|
||||
case MsvAvSingleHost:
|
||||
return "MsvAvSingleHost";
|
||||
case MsvAvTargetName:
|
||||
return "MsvAvTargetName";
|
||||
case MsvAvChannelBindings:
|
||||
return "MsvAvChannelBindings";
|
||||
default:
|
||||
return "UNKNOWN";
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static BOOL ntlm_av_pair_check(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair);
|
||||
static NTLM_AV_PAIR* ntlm_av_pair_next(NTLM_AV_PAIR* pAvPairList, size_t* pcbAvPairList);
|
||||
|
||||
static inline void ntlm_av_pair_set_id(NTLM_AV_PAIR* pAvPair, UINT16 id)
|
||||
{
|
||||
WINPR_ASSERT(pAvPair);
|
||||
winpr_Data_Write_UINT16(&pAvPair->AvId, id);
|
||||
}
|
||||
|
||||
static inline void ntlm_av_pair_set_len(NTLM_AV_PAIR* pAvPair, UINT16 len)
|
||||
{
|
||||
WINPR_ASSERT(pAvPair);
|
||||
winpr_Data_Write_UINT16(&pAvPair->AvLen, len);
|
||||
}
|
||||
|
||||
static BOOL ntlm_av_pair_list_init(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList)
|
||||
{
|
||||
NTLM_AV_PAIR* pAvPair = pAvPairList;
|
||||
|
||||
if (!pAvPair || (cbAvPairList < sizeof(NTLM_AV_PAIR)))
|
||||
return FALSE;
|
||||
|
||||
ntlm_av_pair_set_id(pAvPair, MsvAvEOL);
|
||||
ntlm_av_pair_set_len(pAvPair, 0);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static inline BOOL ntlm_av_pair_get_id(const NTLM_AV_PAIR* pAvPair, size_t size, UINT16* pair)
|
||||
{
|
||||
if (!pAvPair || !pair)
|
||||
return FALSE;
|
||||
|
||||
if (size < sizeof(NTLM_AV_PAIR))
|
||||
return FALSE;
|
||||
|
||||
const UINT16 AvId = winpr_Data_Get_UINT16(&pAvPair->AvId);
|
||||
|
||||
*pair = AvId;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList)
|
||||
{
|
||||
size_t cbAvPair = 0;
|
||||
NTLM_AV_PAIR* pAvPair = nullptr;
|
||||
|
||||
pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair);
|
||||
if (!pAvPair)
|
||||
return 0;
|
||||
|
||||
if (pAvPair < pAvPairList)
|
||||
return 0;
|
||||
|
||||
const size_t size = WINPR_ASSERTING_INT_CAST(size_t, ((PBYTE)pAvPair - (PBYTE)pAvPairList)) +
|
||||
sizeof(NTLM_AV_PAIR);
|
||||
WINPR_ASSERT(size <= UINT32_MAX);
|
||||
WINPR_ASSERT(size >= 0);
|
||||
return (ULONG)size;
|
||||
}
|
||||
|
||||
static inline BOOL ntlm_av_pair_get_len(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pAvLen)
|
||||
{
|
||||
if (!pAvPair)
|
||||
return FALSE;
|
||||
|
||||
if (size < sizeof(NTLM_AV_PAIR))
|
||||
return FALSE;
|
||||
|
||||
const UINT16 AvLen = winpr_Data_Get_UINT16(&pAvPair->AvLen);
|
||||
|
||||
*pAvLen = AvLen;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
#ifdef WITH_DEBUG_NTLM
|
||||
void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList)
|
||||
{
|
||||
UINT16 pair = 0;
|
||||
size_t cbAvPair = cbAvPairList;
|
||||
NTLM_AV_PAIR* pAvPair = pAvPairList;
|
||||
|
||||
if (!ntlm_av_pair_check(pAvPair, cbAvPair))
|
||||
return;
|
||||
|
||||
WLog_VRB(TAG, "AV_PAIRs =");
|
||||
|
||||
while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair) && (pair != MsvAvEOL))
|
||||
{
|
||||
size_t cbLen = 0;
|
||||
ntlm_av_pair_get_len(pAvPair, cbAvPair, &cbLen);
|
||||
|
||||
WLog_VRB(TAG, "\t%s AvId: %" PRIu16 " AvLen: %" PRIu16 "", get_av_pair_string(pair), pair);
|
||||
winpr_HexDump(TAG, WLOG_TRACE, ntlm_av_pair_get_value_pointer(pAvPair), cbLen);
|
||||
|
||||
pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static size_t ntlm_av_pair_list_size(size_t AvPairsCount, size_t AvPairsValueLength)
|
||||
{
|
||||
/* size of headers + value lengths + terminating MsvAvEOL AV_PAIR */
|
||||
return ((AvPairsCount + 1) * 4ULL) + AvPairsValueLength;
|
||||
}
|
||||
|
||||
PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair)
|
||||
{
|
||||
WINPR_ASSERT(pAvPair);
|
||||
return (PBYTE)pAvPair + sizeof(NTLM_AV_PAIR);
|
||||
}
|
||||
|
||||
static BOOL ntlm_av_pair_get_next_offset(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pOffset)
|
||||
{
|
||||
size_t avLen = 0;
|
||||
if (!pOffset)
|
||||
return FALSE;
|
||||
|
||||
if (!ntlm_av_pair_get_len(pAvPair, size, &avLen))
|
||||
return FALSE;
|
||||
*pOffset = avLen + sizeof(NTLM_AV_PAIR);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ntlm_av_pair_check(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair)
|
||||
{
|
||||
return ntlm_av_pair_check_data(pAvPair, cbAvPair, 0);
|
||||
}
|
||||
|
||||
static NTLM_AV_PAIR* ntlm_av_pair_next(NTLM_AV_PAIR* pAvPair, size_t* pcbAvPair)
|
||||
{
|
||||
size_t offset = 0;
|
||||
|
||||
if (!pcbAvPair)
|
||||
return nullptr;
|
||||
if (!ntlm_av_pair_check(pAvPair, *pcbAvPair))
|
||||
return nullptr;
|
||||
|
||||
if (!ntlm_av_pair_get_next_offset(pAvPair, *pcbAvPair, &offset))
|
||||
return nullptr;
|
||||
|
||||
*pcbAvPair -= offset;
|
||||
return (NTLM_AV_PAIR*)((PBYTE)pAvPair + offset);
|
||||
}
|
||||
|
||||
NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTLM_AV_ID AvId,
|
||||
size_t* pcbAvPairListRemaining)
|
||||
{
|
||||
UINT16 id = 0;
|
||||
size_t cbAvPair = cbAvPairList;
|
||||
NTLM_AV_PAIR* pAvPair = pAvPairList;
|
||||
|
||||
if (!ntlm_av_pair_check(pAvPair, cbAvPair))
|
||||
pAvPair = nullptr;
|
||||
|
||||
while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &id))
|
||||
{
|
||||
if (id == AvId)
|
||||
break;
|
||||
if (id == MsvAvEOL)
|
||||
{
|
||||
pAvPair = nullptr;
|
||||
break;
|
||||
}
|
||||
|
||||
pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
|
||||
}
|
||||
|
||||
if (!pAvPair)
|
||||
cbAvPair = 0;
|
||||
if (pcbAvPairListRemaining)
|
||||
*pcbAvPairListRemaining = cbAvPair;
|
||||
|
||||
return pAvPair;
|
||||
}
|
||||
|
||||
static BOOL ntlm_av_pair_add(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTLM_AV_ID AvId,
|
||||
PBYTE Value, UINT16 AvLen)
|
||||
{
|
||||
size_t cbAvPair = 0;
|
||||
NTLM_AV_PAIR* pAvPair = nullptr;
|
||||
|
||||
pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair);
|
||||
|
||||
/* size of header + value length + terminating MsvAvEOL AV_PAIR */
|
||||
if (!pAvPair || cbAvPair < 2 * sizeof(NTLM_AV_PAIR) + AvLen)
|
||||
return FALSE;
|
||||
|
||||
ntlm_av_pair_set_id(pAvPair, (UINT16)AvId);
|
||||
ntlm_av_pair_set_len(pAvPair, AvLen);
|
||||
if (AvLen)
|
||||
{
|
||||
WINPR_ASSERT(Value != nullptr);
|
||||
CopyMemory(ntlm_av_pair_get_value_pointer(pAvPair), Value, AvLen);
|
||||
}
|
||||
|
||||
pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
|
||||
return ntlm_av_pair_list_init(pAvPair, cbAvPair);
|
||||
}
|
||||
|
||||
static BOOL ntlm_av_pair_valid(UINT16 pair)
|
||||
{
|
||||
switch (pair)
|
||||
{
|
||||
case MsvAvEOL:
|
||||
case MsvAvNbComputerName:
|
||||
case MsvAvNbDomainName:
|
||||
case MsvAvDnsComputerName:
|
||||
case MsvAvDnsDomainName:
|
||||
case MsvAvDnsTreeName:
|
||||
case MsvAvFlags:
|
||||
case MsvAvTimestamp:
|
||||
case MsvAvSingleHost:
|
||||
case MsvAvTargetName:
|
||||
case MsvAvChannelBindings:
|
||||
return TRUE;
|
||||
default:
|
||||
return FALSE;
|
||||
}
|
||||
}
|
||||
|
||||
static BOOL ntlm_av_pair_add_copy(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList,
|
||||
NTLM_AV_PAIR* pAvPair, size_t cbAvPair)
|
||||
{
|
||||
UINT16 pair = 0;
|
||||
size_t avLen = 0;
|
||||
|
||||
if (!ntlm_av_pair_check(pAvPair, cbAvPair))
|
||||
return FALSE;
|
||||
|
||||
if (!ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair))
|
||||
return FALSE;
|
||||
|
||||
if (!ntlm_av_pair_get_len(pAvPair, cbAvPair, &avLen))
|
||||
return FALSE;
|
||||
|
||||
if (!ntlm_av_pair_valid(pair))
|
||||
return FALSE;
|
||||
|
||||
WINPR_ASSERT(avLen <= UINT16_MAX);
|
||||
return ntlm_av_pair_add(pAvPairList, cbAvPairList, WINPR_ASSERTING_INT_CAST(NTLM_AV_ID, pair),
|
||||
ntlm_av_pair_get_value_pointer(pAvPair), (UINT16)avLen);
|
||||
}
|
||||
|
||||
static char* get_name(COMPUTER_NAME_FORMAT type)
|
||||
{
|
||||
DWORD nSize = 0;
|
||||
|
||||
if (GetComputerNameExA(type, nullptr, &nSize))
|
||||
return nullptr;
|
||||
|
||||
if (GetLastError() != ERROR_MORE_DATA)
|
||||
return nullptr;
|
||||
|
||||
char* computerName = calloc(1, nSize);
|
||||
|
||||
if (!computerName)
|
||||
return nullptr;
|
||||
|
||||
if (!GetComputerNameExA(type, computerName, &nSize))
|
||||
{
|
||||
free(computerName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return computerName;
|
||||
}
|
||||
|
||||
static int ntlm_get_target_computer_name(PUNICODE_STRING pName,
|
||||
WINPR_ATTR_UNUSED COMPUTER_NAME_FORMAT type)
|
||||
{
|
||||
int status = -1;
|
||||
|
||||
WINPR_ASSERT(pName);
|
||||
|
||||
char* name = get_name(ComputerNameNetBIOS);
|
||||
if (!name)
|
||||
return -1;
|
||||
|
||||
CharUpperA(name);
|
||||
|
||||
size_t len = 0;
|
||||
pName->Buffer = ConvertUtf8ToWCharAlloc(name, &len);
|
||||
free(name);
|
||||
|
||||
if (!pName->Buffer || (len == 0) || (len > UINT16_MAX / sizeof(WCHAR)))
|
||||
{
|
||||
free(pName->Buffer);
|
||||
pName->Buffer = nullptr;
|
||||
return status;
|
||||
}
|
||||
|
||||
pName->Length = (USHORT)((len) * sizeof(WCHAR));
|
||||
pName->MaximumLength = pName->Length;
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void ntlm_free_unicode_string(PUNICODE_STRING string)
|
||||
{
|
||||
if (string)
|
||||
{
|
||||
if (string->Length > 0)
|
||||
{
|
||||
free(string->Buffer);
|
||||
string->Buffer = nullptr;
|
||||
string->Length = 0;
|
||||
string->MaximumLength = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From http://www.ietf.org/proceedings/72/slides/sasl-2.pdf:
|
||||
*
|
||||
* tls-server-end-point:
|
||||
*
|
||||
* The hash of the TLS server's end entity certificate as it appears, octet for octet,
|
||||
* in the server's Certificate message (note that the Certificate message contains a
|
||||
* certificate_list, the first element of which is the server's end entity certificate.)
|
||||
* The hash function to be selected is as follows: if the certificate's signature hash
|
||||
* algorithm is either MD5 or SHA-1, then use SHA-256, otherwise use the certificate's
|
||||
* signature hash algorithm.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Channel Bindings sample usage:
|
||||
* https://raw.github.com/mozilla/mozilla-central/master/extensions/auth/nsAuthSSPI.cpp
|
||||
*/
|
||||
|
||||
/*
|
||||
typedef struct gss_channel_bindings_struct {
|
||||
OM_uint32 initiator_addrtype;
|
||||
gss_buffer_desc initiator_address;
|
||||
OM_uint32 acceptor_addrtype;
|
||||
gss_buffer_desc acceptor_address;
|
||||
gss_buffer_desc application_data;
|
||||
} *gss_channel_bindings_t;
|
||||
*/
|
||||
|
||||
static BOOL ntlm_md5_update_uint32_be(WINPR_DIGEST_CTX* md5, UINT32 num)
|
||||
{
|
||||
BYTE be32[4];
|
||||
be32[0] = (num >> 0) & 0xFF;
|
||||
be32[1] = (num >> 8) & 0xFF;
|
||||
be32[2] = (num >> 16) & 0xFF;
|
||||
be32[3] = (num >> 24) & 0xFF;
|
||||
return winpr_Digest_Update(md5, be32, 4);
|
||||
}
|
||||
|
||||
static void ntlm_compute_channel_bindings(NTLM_CONTEXT* context)
|
||||
{
|
||||
WINPR_DIGEST_CTX* md5 = nullptr;
|
||||
BYTE* ChannelBindingToken = nullptr;
|
||||
UINT32 ChannelBindingTokenLength = 0;
|
||||
SEC_CHANNEL_BINDINGS* ChannelBindings = nullptr;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
ZeroMemory(context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH);
|
||||
ChannelBindings = context->Bindings.Bindings;
|
||||
|
||||
if (!ChannelBindings)
|
||||
return;
|
||||
|
||||
if (!(md5 = winpr_Digest_New()))
|
||||
return;
|
||||
|
||||
if (!winpr_Digest_Init(md5, WINPR_MD_MD5))
|
||||
goto out;
|
||||
|
||||
ChannelBindingTokenLength = context->Bindings.BindingsLength - sizeof(SEC_CHANNEL_BINDINGS);
|
||||
ChannelBindingToken = &((BYTE*)ChannelBindings)[ChannelBindings->dwApplicationDataOffset];
|
||||
|
||||
if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwInitiatorAddrType))
|
||||
goto out;
|
||||
|
||||
if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbInitiatorLength))
|
||||
goto out;
|
||||
|
||||
if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwAcceptorAddrType))
|
||||
goto out;
|
||||
|
||||
if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbAcceptorLength))
|
||||
goto out;
|
||||
|
||||
if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbApplicationDataLength))
|
||||
goto out;
|
||||
|
||||
if (!winpr_Digest_Update(md5, (void*)ChannelBindingToken, ChannelBindingTokenLength))
|
||||
goto out;
|
||||
|
||||
if (!winpr_Digest_Final(md5, context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH))
|
||||
goto out;
|
||||
|
||||
out:
|
||||
winpr_Digest_Free(md5);
|
||||
}
|
||||
|
||||
static void ntlm_compute_single_host_data(NTLM_CONTEXT* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
/**
|
||||
* The Single_Host_Data structure allows a client to send machine-specific information
|
||||
* within an authentication exchange to services on the same machine. The client can
|
||||
* produce additional information to be processed in an implementation-specific way when
|
||||
* the client and server are on the same host. If the server and client platforms are
|
||||
* different or if they are on different hosts, then the information MUST be ignored.
|
||||
* Any fields after the MachineID field MUST be ignored on receipt.
|
||||
*/
|
||||
winpr_Data_Write_UINT32(&context->SingleHostData.Size, 48);
|
||||
winpr_Data_Write_UINT32(&context->SingleHostData.Z4, 0);
|
||||
winpr_Data_Write_UINT32(&context->SingleHostData.DataPresent, 1);
|
||||
winpr_Data_Write_UINT32(&context->SingleHostData.CustomData, SECURITY_MANDATORY_MEDIUM_RID);
|
||||
FillMemory(context->SingleHostData.MachineID, 32, 0xAA);
|
||||
}
|
||||
|
||||
BOOL ntlm_construct_challenge_target_info(NTLM_CONTEXT* context)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
ULONG AvPairsCount = 0;
|
||||
ULONG AvPairsLength = 0;
|
||||
NTLM_AV_PAIR* pAvPairList = nullptr;
|
||||
size_t cbAvPairList = 0;
|
||||
UNICODE_STRING NbDomainName = WINPR_C_ARRAY_INIT;
|
||||
UNICODE_STRING NbComputerName = WINPR_C_ARRAY_INIT;
|
||||
UNICODE_STRING DnsDomainName = WINPR_C_ARRAY_INIT;
|
||||
UNICODE_STRING DnsComputerName = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (ntlm_get_target_computer_name(&NbDomainName, ComputerNameNetBIOS) < 0)
|
||||
goto fail;
|
||||
|
||||
NbComputerName.Buffer = nullptr;
|
||||
|
||||
if (ntlm_get_target_computer_name(&NbComputerName, ComputerNameNetBIOS) < 0)
|
||||
goto fail;
|
||||
|
||||
DnsDomainName.Buffer = nullptr;
|
||||
|
||||
if (ntlm_get_target_computer_name(&DnsDomainName, ComputerNameDnsDomain) < 0)
|
||||
goto fail;
|
||||
|
||||
DnsComputerName.Buffer = nullptr;
|
||||
|
||||
if (ntlm_get_target_computer_name(&DnsComputerName, ComputerNameDnsHostname) < 0)
|
||||
goto fail;
|
||||
|
||||
AvPairsCount = 5;
|
||||
AvPairsLength = NbDomainName.Length + NbComputerName.Length + DnsDomainName.Length +
|
||||
DnsComputerName.Length + 8;
|
||||
{
|
||||
const size_t length = ntlm_av_pair_list_size(AvPairsCount, AvPairsLength);
|
||||
if (!sspi_SecBufferAlloc(&context->ChallengeTargetInfo,
|
||||
WINPR_ASSERTING_INT_CAST(uint32_t, length)))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
pAvPairList = (NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer;
|
||||
cbAvPairList = context->ChallengeTargetInfo.cbBuffer;
|
||||
|
||||
if (!ntlm_av_pair_list_init(pAvPairList, cbAvPairList))
|
||||
goto fail;
|
||||
|
||||
if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbDomainName, (PBYTE)NbDomainName.Buffer,
|
||||
NbDomainName.Length))
|
||||
goto fail;
|
||||
|
||||
if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbComputerName,
|
||||
(PBYTE)NbComputerName.Buffer, NbComputerName.Length))
|
||||
goto fail;
|
||||
|
||||
if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsDomainName,
|
||||
(PBYTE)DnsDomainName.Buffer, DnsDomainName.Length))
|
||||
goto fail;
|
||||
|
||||
if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsComputerName,
|
||||
(PBYTE)DnsComputerName.Buffer, DnsComputerName.Length))
|
||||
goto fail;
|
||||
|
||||
if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvTimestamp, context->Timestamp,
|
||||
sizeof(context->Timestamp)))
|
||||
goto fail;
|
||||
|
||||
rc = TRUE;
|
||||
fail:
|
||||
ntlm_free_unicode_string(&NbDomainName);
|
||||
ntlm_free_unicode_string(&NbComputerName);
|
||||
ntlm_free_unicode_string(&DnsDomainName);
|
||||
ntlm_free_unicode_string(&DnsComputerName);
|
||||
return rc;
|
||||
}
|
||||
|
||||
BOOL ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context)
|
||||
{
|
||||
ULONG AvPairsCount = 0;
|
||||
size_t AvPairsValueLength = 0;
|
||||
NTLM_AV_PAIR* AvTimestamp = nullptr;
|
||||
NTLM_AV_PAIR* AvNbDomainName = nullptr;
|
||||
NTLM_AV_PAIR* AvNbComputerName = nullptr;
|
||||
NTLM_AV_PAIR* AvDnsDomainName = nullptr;
|
||||
NTLM_AV_PAIR* AvDnsComputerName = nullptr;
|
||||
NTLM_AV_PAIR* AvDnsTreeName = nullptr;
|
||||
NTLM_AV_PAIR* ChallengeTargetInfo = nullptr;
|
||||
NTLM_AV_PAIR* AuthenticateTargetInfo = nullptr;
|
||||
size_t cbAvTimestamp = 0;
|
||||
size_t cbAvNbDomainName = 0;
|
||||
size_t cbAvNbComputerName = 0;
|
||||
size_t cbAvDnsDomainName = 0;
|
||||
size_t cbAvDnsComputerName = 0;
|
||||
size_t cbAvDnsTreeName = 0;
|
||||
size_t cbChallengeTargetInfo = 0;
|
||||
size_t cbAuthenticateTargetInfo = 0;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
AvPairsCount = 1;
|
||||
ChallengeTargetInfo = (NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer;
|
||||
cbChallengeTargetInfo = context->ChallengeTargetInfo.cbBuffer;
|
||||
AvNbDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvNbDomainName,
|
||||
&cbAvNbDomainName);
|
||||
AvNbComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
|
||||
MsvAvNbComputerName, &cbAvNbComputerName);
|
||||
AvDnsDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
|
||||
MsvAvDnsDomainName, &cbAvDnsDomainName);
|
||||
AvDnsComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
|
||||
MsvAvDnsComputerName, &cbAvDnsComputerName);
|
||||
AvDnsTreeName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvDnsTreeName,
|
||||
&cbAvDnsTreeName);
|
||||
AvTimestamp = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvTimestamp,
|
||||
&cbAvTimestamp);
|
||||
|
||||
if (AvNbDomainName)
|
||||
{
|
||||
size_t avLen = 0;
|
||||
if (!ntlm_av_pair_get_len(AvNbDomainName, cbAvNbDomainName, &avLen))
|
||||
goto fail;
|
||||
AvPairsCount++; /* MsvAvNbDomainName */
|
||||
AvPairsValueLength += avLen;
|
||||
}
|
||||
|
||||
if (AvNbComputerName)
|
||||
{
|
||||
size_t avLen = 0;
|
||||
if (!ntlm_av_pair_get_len(AvNbComputerName, cbAvNbComputerName, &avLen))
|
||||
goto fail;
|
||||
AvPairsCount++; /* MsvAvNbComputerName */
|
||||
AvPairsValueLength += avLen;
|
||||
}
|
||||
|
||||
if (AvDnsDomainName)
|
||||
{
|
||||
size_t avLen = 0;
|
||||
if (!ntlm_av_pair_get_len(AvDnsDomainName, cbAvDnsDomainName, &avLen))
|
||||
goto fail;
|
||||
AvPairsCount++; /* MsvAvDnsDomainName */
|
||||
AvPairsValueLength += avLen;
|
||||
}
|
||||
|
||||
if (AvDnsComputerName)
|
||||
{
|
||||
size_t avLen = 0;
|
||||
if (!ntlm_av_pair_get_len(AvDnsComputerName, cbAvDnsComputerName, &avLen))
|
||||
goto fail;
|
||||
AvPairsCount++; /* MsvAvDnsComputerName */
|
||||
AvPairsValueLength += avLen;
|
||||
}
|
||||
|
||||
if (AvDnsTreeName)
|
||||
{
|
||||
size_t avLen = 0;
|
||||
if (!ntlm_av_pair_get_len(AvDnsTreeName, cbAvDnsTreeName, &avLen))
|
||||
goto fail;
|
||||
AvPairsCount++; /* MsvAvDnsTreeName */
|
||||
AvPairsValueLength += avLen;
|
||||
}
|
||||
|
||||
AvPairsCount++; /* MsvAvTimestamp */
|
||||
AvPairsValueLength += 8;
|
||||
|
||||
if (context->UseMIC)
|
||||
{
|
||||
AvPairsCount++; /* MsvAvFlags */
|
||||
AvPairsValueLength += 4;
|
||||
}
|
||||
|
||||
if (context->SendSingleHostData)
|
||||
{
|
||||
AvPairsCount++; /* MsvAvSingleHost */
|
||||
ntlm_compute_single_host_data(context);
|
||||
AvPairsValueLength += context->SingleHostData.Size;
|
||||
}
|
||||
|
||||
/**
|
||||
* Extended Protection for Authentication:
|
||||
* http://blogs.technet.com/b/srd/archive/2009/12/08/extended-protection-for-authentication.aspx
|
||||
*/
|
||||
|
||||
if (!context->SuppressExtendedProtection)
|
||||
{
|
||||
/**
|
||||
* SEC_CHANNEL_BINDINGS structure
|
||||
* http://msdn.microsoft.com/en-us/library/windows/desktop/dd919963/
|
||||
*/
|
||||
AvPairsCount++; /* MsvAvChannelBindings */
|
||||
AvPairsValueLength += 16;
|
||||
ntlm_compute_channel_bindings(context);
|
||||
|
||||
if (context->ServicePrincipalName.Length > 0)
|
||||
{
|
||||
AvPairsCount++; /* MsvAvTargetName */
|
||||
AvPairsValueLength += context->ServicePrincipalName.Length;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
size_t size = ntlm_av_pair_list_size(AvPairsCount, AvPairsValueLength);
|
||||
if (context->NTLMv2)
|
||||
size += 8; /* unknown 8-byte padding */
|
||||
|
||||
if (!sspi_SecBufferAlloc(&context->AuthenticateTargetInfo,
|
||||
WINPR_ASSERTING_INT_CAST(uint32_t, size)))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
AuthenticateTargetInfo = (NTLM_AV_PAIR*)context->AuthenticateTargetInfo.pvBuffer;
|
||||
cbAuthenticateTargetInfo = context->AuthenticateTargetInfo.cbBuffer;
|
||||
|
||||
if (!ntlm_av_pair_list_init(AuthenticateTargetInfo, cbAuthenticateTargetInfo))
|
||||
goto fail;
|
||||
|
||||
if (AvNbDomainName)
|
||||
{
|
||||
if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvNbDomainName,
|
||||
cbAvNbDomainName))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (AvNbComputerName)
|
||||
{
|
||||
if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
|
||||
AvNbComputerName, cbAvNbComputerName))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (AvDnsDomainName)
|
||||
{
|
||||
if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
|
||||
AvDnsDomainName, cbAvDnsDomainName))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (AvDnsComputerName)
|
||||
{
|
||||
if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
|
||||
AvDnsComputerName, cbAvDnsComputerName))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (AvDnsTreeName)
|
||||
{
|
||||
if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvDnsTreeName,
|
||||
cbAvDnsTreeName))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (AvTimestamp)
|
||||
{
|
||||
if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvTimestamp,
|
||||
cbAvTimestamp))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (context->UseMIC)
|
||||
{
|
||||
UINT32 flags = 0;
|
||||
winpr_Data_Write_UINT32(&flags, MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK);
|
||||
|
||||
if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvFlags,
|
||||
(PBYTE)&flags, 4))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (context->SendSingleHostData)
|
||||
{
|
||||
WINPR_ASSERT(context->SingleHostData.Size <= UINT16_MAX);
|
||||
if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvSingleHost,
|
||||
(PBYTE)&context->SingleHostData,
|
||||
(UINT16)context->SingleHostData.Size))
|
||||
goto fail;
|
||||
}
|
||||
|
||||
if (!context->SuppressExtendedProtection)
|
||||
{
|
||||
if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
|
||||
MsvAvChannelBindings, context->ChannelBindingsHash, 16))
|
||||
goto fail;
|
||||
|
||||
if (context->ServicePrincipalName.Length > 0)
|
||||
{
|
||||
if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvTargetName,
|
||||
(PBYTE)context->ServicePrincipalName.Buffer,
|
||||
context->ServicePrincipalName.Length))
|
||||
goto fail;
|
||||
}
|
||||
}
|
||||
|
||||
if (context->NTLMv2)
|
||||
{
|
||||
NTLM_AV_PAIR* AvEOL = nullptr;
|
||||
AvEOL = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvEOL, nullptr);
|
||||
|
||||
if (!AvEOL)
|
||||
goto fail;
|
||||
|
||||
ZeroMemory(AvEOL, sizeof(NTLM_AV_PAIR));
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
fail:
|
||||
sspi_SecBufferFree(&context->AuthenticateTargetInfo);
|
||||
return FALSE;
|
||||
}
|
||||
42
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h
vendored
Normal file
42
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* NTLM Security Package (AV_PAIRs)
|
||||
*
|
||||
* Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WINPR_SSPI_NTLM_AV_PAIRS_H
|
||||
#define WINPR_SSPI_NTLM_AV_PAIRS_H
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include "ntlm.h"
|
||||
|
||||
#include <winpr/stream.h>
|
||||
|
||||
ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList);
|
||||
|
||||
#ifdef WITH_DEBUG_NTLM
|
||||
void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList);
|
||||
#endif
|
||||
|
||||
PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair);
|
||||
NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTLM_AV_ID AvId,
|
||||
size_t* pcbAvPairListRemaining);
|
||||
|
||||
BOOL ntlm_construct_challenge_target_info(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context);
|
||||
|
||||
#endif /* WINPR_SSPI_NTLM_AV_PAIRS_H */
|
||||
960
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_compute.c
vendored
Normal file
960
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_compute.c
vendored
Normal file
@@ -0,0 +1,960 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* NTLM Security Package (Compute)
|
||||
*
|
||||
* Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#include <winpr/config.h>
|
||||
|
||||
#include <winpr/assert.h>
|
||||
|
||||
#include "ntlm.h"
|
||||
#include "../sspi.h"
|
||||
|
||||
#include <winpr/crt.h>
|
||||
#include <winpr/sam.h>
|
||||
#include <winpr/ntlm.h>
|
||||
#include <winpr/print.h>
|
||||
#include <winpr/crypto.h>
|
||||
#include <winpr/sysinfo.h>
|
||||
|
||||
#include "ntlm_compute.h"
|
||||
|
||||
#include "../../log.h"
|
||||
#define TAG WINPR_TAG("sspi.NTLM")
|
||||
|
||||
#define NTLM_CheckAndLogRequiredCapacity(tag, s, nmemb, what) \
|
||||
Stream_CheckAndLogRequiredCapacityEx(tag, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ") " what, \
|
||||
__func__, __FILE__, (size_t)__LINE__)
|
||||
|
||||
static char NTLM_CLIENT_SIGN_MAGIC[] = "session key to client-to-server signing key magic constant";
|
||||
static char NTLM_SERVER_SIGN_MAGIC[] = "session key to server-to-client signing key magic constant";
|
||||
static char NTLM_CLIENT_SEAL_MAGIC[] = "session key to client-to-server sealing key magic constant";
|
||||
static char NTLM_SERVER_SEAL_MAGIC[] = "session key to server-to-client sealing key magic constant";
|
||||
|
||||
static const BYTE NTLM_NULL_BUFFER[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
|
||||
|
||||
/**
|
||||
* Populate VERSION structure msdn{cc236654}
|
||||
* @param versionInfo A pointer to the version struct
|
||||
*
|
||||
* @return \b TRUE for success, \b FALSE for failure
|
||||
*/
|
||||
|
||||
BOOL ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo)
|
||||
{
|
||||
WINPR_ASSERT(versionInfo);
|
||||
|
||||
#if defined(WITH_WINPR_DEPRECATED)
|
||||
OSVERSIONINFOA osVersionInfo = WINPR_C_ARRAY_INIT;
|
||||
osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
|
||||
if (!GetVersionExA(&osVersionInfo))
|
||||
return FALSE;
|
||||
versionInfo->ProductMajorVersion = (UINT8)osVersionInfo.dwMajorVersion;
|
||||
versionInfo->ProductMinorVersion = (UINT8)osVersionInfo.dwMinorVersion;
|
||||
versionInfo->ProductBuild = (UINT16)osVersionInfo.dwBuildNumber;
|
||||
#else
|
||||
/* Always return fixed version number.
|
||||
*
|
||||
* ProductVersion is fixed since windows 10 to Major 10, Minor 0
|
||||
* ProductBuild taken from https://en.wikipedia.org/wiki/Windows_11_version_history
|
||||
* with most recent (pre) release build number
|
||||
*/
|
||||
versionInfo->ProductMajorVersion = 10;
|
||||
versionInfo->ProductMinorVersion = 0;
|
||||
versionInfo->ProductBuild = 22631;
|
||||
#endif
|
||||
ZeroMemory(versionInfo->Reserved, sizeof(versionInfo->Reserved));
|
||||
versionInfo->NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3;
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Read VERSION structure. msdn{cc236654}
|
||||
* @param s A pointer to a stream to read
|
||||
* @param versionInfo A pointer to the struct to read data to
|
||||
*
|
||||
* @return \b TRUE for success, \b FALSE for failure
|
||||
*/
|
||||
|
||||
BOOL ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo)
|
||||
{
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(versionInfo);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
|
||||
return FALSE;
|
||||
|
||||
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) */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Write VERSION structure. msdn{cc236654}
|
||||
* @param s A pointer to the stream to write to
|
||||
* @param versionInfo A pointer to the buffer to read the data from
|
||||
*
|
||||
* @return \b TRUE for success, \b FALSE for failure
|
||||
*/
|
||||
|
||||
BOOL ntlm_write_version_info(wStream* s, const NTLM_VERSION_INFO* versionInfo)
|
||||
{
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(versionInfo);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredCapacityEx(
|
||||
TAG, WLOG_WARN, s, 5ull + sizeof(versionInfo->Reserved), 1ull,
|
||||
"%s(%s:%" PRIuz ") NTLM_VERSION_INFO", __func__, __FILE__, (size_t)__LINE__))
|
||||
return FALSE;
|
||||
|
||||
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) */
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print VERSION structure. msdn{cc236654}
|
||||
* @param versionInfo A pointer to the struct containing the data to print
|
||||
*/
|
||||
#ifdef WITH_DEBUG_NTLM
|
||||
void ntlm_print_version_info(const NTLM_VERSION_INFO* versionInfo)
|
||||
{
|
||||
WINPR_ASSERT(versionInfo);
|
||||
|
||||
WLog_VRB(TAG, "VERSION ={");
|
||||
WLog_VRB(TAG, "\tProductMajorVersion: %" PRIu8 "", versionInfo->ProductMajorVersion);
|
||||
WLog_VRB(TAG, "\tProductMinorVersion: %" PRIu8 "", versionInfo->ProductMinorVersion);
|
||||
WLog_VRB(TAG, "\tProductBuild: %" PRIu16 "", versionInfo->ProductBuild);
|
||||
WLog_VRB(TAG, "\tReserved: 0x%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "", versionInfo->Reserved[0],
|
||||
versionInfo->Reserved[1], versionInfo->Reserved[2]);
|
||||
WLog_VRB(TAG, "\tNTLMRevisionCurrent: 0x%02" PRIX8 "", versionInfo->NTLMRevisionCurrent);
|
||||
}
|
||||
#endif
|
||||
|
||||
static BOOL ntlm_read_ntlm_v2_client_challenge(wStream* s, NTLMv2_CLIENT_CHALLENGE* challenge)
|
||||
{
|
||||
size_t size = 0;
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(challenge);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 28))
|
||||
return FALSE;
|
||||
|
||||
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);
|
||||
size = Stream_Length(s) - Stream_GetPosition(s);
|
||||
|
||||
if (size > UINT32_MAX)
|
||||
{
|
||||
WLog_ERR(TAG, "NTLMv2_CLIENT_CHALLENGE::cbAvPairs too large, got %" PRIuz "bytes", size);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
challenge->cbAvPairs = (UINT32)size;
|
||||
challenge->AvPairs = (NTLM_AV_PAIR*)malloc(challenge->cbAvPairs);
|
||||
|
||||
if (!challenge->AvPairs)
|
||||
{
|
||||
WLog_ERR(TAG, "NTLMv2_CLIENT_CHALLENGE::AvPairs failed to allocate %" PRIu32 "bytes",
|
||||
challenge->cbAvPairs);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
Stream_Read(s, challenge->AvPairs, size);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
static BOOL ntlm_write_ntlm_v2_client_challenge(wStream* s,
|
||||
const NTLMv2_CLIENT_CHALLENGE* challenge)
|
||||
{
|
||||
ULONG length = 0;
|
||||
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(challenge);
|
||||
|
||||
if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 28, "NTLMv2_CLIENT_CHALLENGE"))
|
||||
return FALSE;
|
||||
|
||||
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);
|
||||
length = ntlm_av_pair_list_length(challenge->AvPairs, challenge->cbAvPairs);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write(s, challenge->AvPairs, length);
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response)
|
||||
{
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(response);
|
||||
|
||||
if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
|
||||
return FALSE;
|
||||
|
||||
Stream_Read(s, response->Response, 16);
|
||||
return ntlm_read_ntlm_v2_client_challenge(s, &(response->Challenge));
|
||||
}
|
||||
|
||||
BOOL ntlm_write_ntlm_v2_response(wStream* s, const NTLMv2_RESPONSE* response)
|
||||
{
|
||||
WINPR_ASSERT(s);
|
||||
WINPR_ASSERT(response);
|
||||
|
||||
if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 16ull, "NTLMv2_RESPONSE"))
|
||||
return FALSE;
|
||||
|
||||
Stream_Write(s, response->Response, 16);
|
||||
return ntlm_write_ntlm_v2_client_challenge(s, &(response->Challenge));
|
||||
}
|
||||
|
||||
/**
|
||||
* Get current time, in tenths of microseconds since midnight of January 1, 1601.
|
||||
* @param[out] timestamp 64-bit little-endian timestamp
|
||||
*/
|
||||
|
||||
static void ntlm_current_time(BYTE* timestamp, WINPR_ATTR_UNUSED size_t size)
|
||||
{
|
||||
FILETIME ft = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(timestamp);
|
||||
WINPR_ASSERT(size >= sizeof(ft));
|
||||
|
||||
GetSystemTimeAsFileTime(&ft);
|
||||
CopyMemory(timestamp, &(ft), sizeof(ft));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate timestamp for AUTHENTICATE_MESSAGE.
|
||||
*
|
||||
* @param context A pointer to the NTLM context
|
||||
*/
|
||||
|
||||
void ntlm_generate_timestamp(NTLM_CONTEXT* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (memcmp(context->ChallengeTimestamp, NTLM_NULL_BUFFER, 8) != 0)
|
||||
CopyMemory(context->Timestamp, context->ChallengeTimestamp, 8);
|
||||
else
|
||||
ntlm_current_time(context->Timestamp, sizeof(context->Timestamp));
|
||||
}
|
||||
|
||||
static BOOL ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
WINPR_SAM* sam = nullptr;
|
||||
WINPR_SAM_ENTRY* entry = nullptr;
|
||||
SSPI_CREDENTIALS* credentials = nullptr;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(hash);
|
||||
|
||||
credentials = context->credentials;
|
||||
sam = SamOpen(context->SamFile, TRUE);
|
||||
|
||||
if (!sam)
|
||||
goto fail;
|
||||
|
||||
entry = SamLookupUserW(
|
||||
sam, (LPWSTR)credentials->identity.User, credentials->identity.UserLength * sizeof(WCHAR),
|
||||
(LPWSTR)credentials->identity.Domain, credentials->identity.DomainLength * sizeof(WCHAR));
|
||||
|
||||
if (!entry)
|
||||
{
|
||||
entry = SamLookupUserW(sam, (LPWSTR)credentials->identity.User,
|
||||
credentials->identity.UserLength * sizeof(WCHAR), nullptr, 0);
|
||||
}
|
||||
|
||||
if (!entry)
|
||||
goto fail;
|
||||
|
||||
#ifdef WITH_DEBUG_NTLM
|
||||
WLog_VRB(TAG, "NTLM Hash:");
|
||||
winpr_HexDump(TAG, WLOG_DEBUG, entry->NtHash, 16);
|
||||
#endif
|
||||
NTOWFv2FromHashW(entry->NtHash, (LPWSTR)credentials->identity.User,
|
||||
credentials->identity.UserLength * sizeof(WCHAR),
|
||||
(LPWSTR)credentials->identity.Domain,
|
||||
credentials->identity.DomainLength * sizeof(WCHAR), hash);
|
||||
|
||||
rc = TRUE;
|
||||
|
||||
fail:
|
||||
SamFreeEntry(sam, entry);
|
||||
SamClose(sam);
|
||||
if (!rc)
|
||||
WLog_ERR(TAG, "Error: Could not find user in SAM database");
|
||||
|
||||
return rc;
|
||||
}
|
||||
|
||||
static int hexchar2nibble(WCHAR wc)
|
||||
{
|
||||
#if defined(__BIG_ENDIAN__)
|
||||
union
|
||||
{
|
||||
BYTE b[2];
|
||||
WCHAR w;
|
||||
} cnv;
|
||||
cnv.w = wc;
|
||||
const BYTE b = cnv.b[0];
|
||||
cnv.b[0] = cnv.b[1];
|
||||
cnv.b[1] = b;
|
||||
wc = cnv.w;
|
||||
#endif
|
||||
|
||||
switch (wc)
|
||||
{
|
||||
case L'0':
|
||||
case L'1':
|
||||
case L'2':
|
||||
case L'3':
|
||||
case L'4':
|
||||
case L'5':
|
||||
case L'6':
|
||||
case L'7':
|
||||
case L'8':
|
||||
case L'9':
|
||||
return wc - L'0';
|
||||
case L'a':
|
||||
case L'b':
|
||||
case L'c':
|
||||
case L'd':
|
||||
case L'e':
|
||||
case L'f':
|
||||
return wc - L'a' + 10;
|
||||
case L'A':
|
||||
case L'B':
|
||||
case L'C':
|
||||
case L'D':
|
||||
case L'E':
|
||||
case L'F':
|
||||
return wc - L'A' + 10;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
static int ntlm_convert_password_hash(NTLM_CONTEXT* context, BYTE* hash, size_t hashlen)
|
||||
{
|
||||
const size_t required_len = 2ull * hashlen;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(hash);
|
||||
|
||||
SSPI_CREDENTIALS* credentials = context->credentials;
|
||||
/* Password contains a password hash of length (PasswordLength -
|
||||
* SSPI_CREDENTIALS_HASH_LENGTH_OFFSET) */
|
||||
const ULONG PasswordHashLength = credentials->identity.PasswordLength -
|
||||
/* Macro [globalScope] */ SSPI_CREDENTIALS_HASH_LENGTH_OFFSET;
|
||||
|
||||
if (PasswordHashLength != required_len)
|
||||
{
|
||||
WLog_ERR(TAG,
|
||||
"PasswordHash has invalid length %" PRIu32 " must be exactly %" PRIuz " bytes",
|
||||
PasswordHashLength, required_len);
|
||||
return -1;
|
||||
}
|
||||
|
||||
const WCHAR* PasswordHash = credentials->identity.Password;
|
||||
for (size_t x = 0; x < hashlen; x++)
|
||||
{
|
||||
const int hi = hexchar2nibble(PasswordHash[2 * x]);
|
||||
if (hi < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "PasswordHash has an invalid value at position %" PRIuz, 2 * x);
|
||||
return -1;
|
||||
}
|
||||
const int lo = hexchar2nibble(PasswordHash[2 * x + 1]);
|
||||
if (lo < 0)
|
||||
{
|
||||
WLog_ERR(TAG, "PasswordHash has an invalid value at position %" PRIuz, 2 * x + 1);
|
||||
return -1;
|
||||
}
|
||||
const BYTE val = (BYTE)((hi << 4) | lo);
|
||||
hash[x] = val;
|
||||
}
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static BOOL ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash)
|
||||
{
|
||||
SSPI_CREDENTIALS* credentials = nullptr;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(hash);
|
||||
|
||||
credentials = context->credentials;
|
||||
#ifdef WITH_DEBUG_NTLM
|
||||
|
||||
if (credentials)
|
||||
{
|
||||
WLog_VRB(TAG, "Password (length = %" PRIu32 ")", credentials->identity.PasswordLength * 2);
|
||||
winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.Password,
|
||||
credentials->identity.PasswordLength * 2);
|
||||
WLog_VRB(TAG, "Username (length = %" PRIu32 ")", credentials->identity.UserLength * 2);
|
||||
winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.User,
|
||||
credentials->identity.UserLength * 2);
|
||||
WLog_VRB(TAG, "Domain (length = %" PRIu32 ")", credentials->identity.DomainLength * 2);
|
||||
winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.Domain,
|
||||
credentials->identity.DomainLength * 2);
|
||||
}
|
||||
else
|
||||
WLog_VRB(TAG, "Strange, NTLM_CONTEXT is missing valid credentials...");
|
||||
|
||||
WLog_VRB(TAG, "Workstation (length = %" PRIu16 ")", context->Workstation.Length);
|
||||
winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)context->Workstation.Buffer, context->Workstation.Length);
|
||||
WLog_VRB(TAG, "NTOWFv2, NTLMv2 Hash");
|
||||
winpr_HexDump(TAG, WLOG_TRACE, context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH);
|
||||
#endif
|
||||
|
||||
if (memcmp(context->NtlmV2Hash, NTLM_NULL_BUFFER, 16) != 0)
|
||||
return TRUE;
|
||||
|
||||
if (!credentials)
|
||||
return FALSE;
|
||||
else if (memcmp(context->NtlmHash, NTLM_NULL_BUFFER, 16) != 0)
|
||||
{
|
||||
return NTOWFv2FromHashW(context->NtlmHash, (LPWSTR)credentials->identity.User,
|
||||
credentials->identity.UserLength * 2,
|
||||
(LPWSTR)credentials->identity.Domain,
|
||||
credentials->identity.DomainLength * 2, hash);
|
||||
}
|
||||
else if (credentials->identity.PasswordLength > SSPI_CREDENTIALS_HASH_LENGTH_OFFSET)
|
||||
{
|
||||
/* Special case for WinPR: password hash */
|
||||
if (ntlm_convert_password_hash(context, context->NtlmHash, sizeof(context->NtlmHash)) < 0)
|
||||
return FALSE;
|
||||
|
||||
return NTOWFv2FromHashW(context->NtlmHash, (LPWSTR)credentials->identity.User,
|
||||
credentials->identity.UserLength * 2,
|
||||
(LPWSTR)credentials->identity.Domain,
|
||||
credentials->identity.DomainLength * 2, hash);
|
||||
}
|
||||
else if (credentials->identity.Password)
|
||||
{
|
||||
return NTOWFv2W(
|
||||
(LPWSTR)credentials->identity.Password, credentials->identity.PasswordLength * 2,
|
||||
(LPWSTR)credentials->identity.User, credentials->identity.UserLength * 2,
|
||||
(LPWSTR)credentials->identity.Domain, credentials->identity.DomainLength * 2, hash);
|
||||
}
|
||||
else if (context->HashCallback)
|
||||
{
|
||||
SecBuffer proofValue = WINPR_C_ARRAY_INIT;
|
||||
SecBuffer micValue = WINPR_C_ARRAY_INIT;
|
||||
|
||||
if (ntlm_computeProofValue(context, &proofValue) != SEC_E_OK)
|
||||
return FALSE;
|
||||
|
||||
if (ntlm_computeMicValue(context, &micValue) != SEC_E_OK)
|
||||
{
|
||||
sspi_SecBufferFree(&proofValue);
|
||||
return FALSE;
|
||||
}
|
||||
|
||||
const SECURITY_STATUS ret = context->HashCallback(
|
||||
context->HashCallbackArg, &credentials->identity, &proofValue,
|
||||
context->EncryptedRandomSessionKey, context->AUTHENTICATE_MESSAGE.MessageIntegrityCheck,
|
||||
&micValue, hash);
|
||||
sspi_SecBufferFree(&proofValue);
|
||||
sspi_SecBufferFree(&micValue);
|
||||
return ret == SEC_E_OK;
|
||||
}
|
||||
else if (context->UseSamFileDatabase)
|
||||
{
|
||||
return ntlm_fetch_ntlm_v2_hash(context, hash);
|
||||
}
|
||||
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
SECURITY_STATUS ntlm_compute_lm_v2_response(NTLM_CONTEXT* context)
|
||||
{
|
||||
BYTE* response = nullptr;
|
||||
BYTE value[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (context->LmCompatibilityLevel < 2)
|
||||
{
|
||||
if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24))
|
||||
return SEC_E_INSUFFICIENT_MEMORY;
|
||||
|
||||
ZeroMemory(context->LmChallengeResponse.pvBuffer, 24);
|
||||
return SEC_E_OK;
|
||||
}
|
||||
|
||||
/* Compute the NTLMv2 hash */
|
||||
|
||||
if (!ntlm_compute_ntlm_v2_hash(context, context->NtlmV2Hash))
|
||||
return SEC_E_NO_CREDENTIALS;
|
||||
|
||||
/* Concatenate the server and client challenges */
|
||||
CopyMemory(value, context->ServerChallenge, 8);
|
||||
CopyMemory(&value[8], context->ClientChallenge, 8);
|
||||
|
||||
if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24))
|
||||
return SEC_E_INSUFFICIENT_MEMORY;
|
||||
|
||||
response = (BYTE*)context->LmChallengeResponse.pvBuffer;
|
||||
/* Compute the HMAC-MD5 hash of the resulting value using the NTLMv2 hash as the key */
|
||||
winpr_HMAC(WINPR_MD_MD5, (void*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH, (BYTE*)value,
|
||||
WINPR_MD5_DIGEST_LENGTH, response, WINPR_MD5_DIGEST_LENGTH);
|
||||
/* Concatenate the resulting HMAC-MD5 hash and the client challenge, giving us the LMv2 response
|
||||
* (24 bytes) */
|
||||
CopyMemory(&response[16], context->ClientChallenge, 8);
|
||||
return SEC_E_OK;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute NTLMv2 Response.
|
||||
*
|
||||
* NTLMv2_RESPONSE msdn{cc236653}
|
||||
* NTLMv2 Authentication msdn{cc236700}
|
||||
*
|
||||
* @param context A pointer to the NTLM context
|
||||
* @return \b TRUE for success, \b FALSE for failure
|
||||
*/
|
||||
|
||||
SECURITY_STATUS ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context)
|
||||
{
|
||||
SecBuffer ntlm_v2_temp = WINPR_C_ARRAY_INIT;
|
||||
SecBuffer ntlm_v2_temp_chal = WINPR_C_ARRAY_INIT;
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
PSecBuffer TargetInfo = &context->ChallengeTargetInfo;
|
||||
SECURITY_STATUS ret = SEC_E_INSUFFICIENT_MEMORY;
|
||||
|
||||
if (!sspi_SecBufferAlloc(&ntlm_v2_temp, TargetInfo->cbBuffer + 28))
|
||||
goto exit;
|
||||
|
||||
ZeroMemory(ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
|
||||
{
|
||||
BYTE* blob = (BYTE*)ntlm_v2_temp.pvBuffer;
|
||||
|
||||
/* Compute the NTLMv2 hash */
|
||||
ret = SEC_E_NO_CREDENTIALS;
|
||||
if (!ntlm_compute_ntlm_v2_hash(context, (BYTE*)context->NtlmV2Hash))
|
||||
goto exit;
|
||||
|
||||
/* Construct temp */
|
||||
blob[0] = 1; /* RespType (1 byte) */
|
||||
blob[1] = 1; /* HighRespType (1 byte) */
|
||||
/* Reserved1 (2 bytes) */
|
||||
/* Reserved2 (4 bytes) */
|
||||
CopyMemory(&blob[8], context->Timestamp, 8); /* Timestamp (8 bytes) */
|
||||
CopyMemory(&blob[16], context->ClientChallenge, 8); /* ClientChallenge (8 bytes) */
|
||||
/* Reserved3 (4 bytes) */
|
||||
CopyMemory(&blob[28], TargetInfo->pvBuffer, TargetInfo->cbBuffer);
|
||||
#ifdef WITH_DEBUG_NTLM
|
||||
WLog_VRB(TAG, "NTLMv2 Response Temp Blob");
|
||||
winpr_HexDump(TAG, WLOG_TRACE, ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
|
||||
#endif
|
||||
}
|
||||
/* Concatenate server challenge with temp */
|
||||
ret = SEC_E_INSUFFICIENT_MEMORY;
|
||||
if (!sspi_SecBufferAlloc(&ntlm_v2_temp_chal, ntlm_v2_temp.cbBuffer + 8))
|
||||
goto exit;
|
||||
|
||||
{
|
||||
BYTE* blob = (BYTE*)ntlm_v2_temp_chal.pvBuffer;
|
||||
CopyMemory(blob, context->ServerChallenge, 8);
|
||||
CopyMemory(&blob[8], ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
|
||||
winpr_HMAC(WINPR_MD_MD5, (BYTE*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH,
|
||||
(BYTE*)ntlm_v2_temp_chal.pvBuffer, ntlm_v2_temp_chal.cbBuffer,
|
||||
context->NtProofString, WINPR_MD5_DIGEST_LENGTH);
|
||||
}
|
||||
|
||||
/* NtChallengeResponse, Concatenate NTProofStr with temp */
|
||||
|
||||
if (!sspi_SecBufferAlloc(&context->NtChallengeResponse, ntlm_v2_temp.cbBuffer + 16))
|
||||
goto exit;
|
||||
|
||||
{
|
||||
BYTE* blob = (BYTE*)context->NtChallengeResponse.pvBuffer;
|
||||
CopyMemory(blob, context->NtProofString, WINPR_MD5_DIGEST_LENGTH);
|
||||
CopyMemory(&blob[16], ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
|
||||
}
|
||||
/* Compute SessionBaseKey, the HMAC-MD5 hash of NTProofStr using the NTLMv2 hash as the key */
|
||||
winpr_HMAC(WINPR_MD_MD5, (BYTE*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH,
|
||||
context->NtProofString, WINPR_MD5_DIGEST_LENGTH, context->SessionBaseKey,
|
||||
WINPR_MD5_DIGEST_LENGTH);
|
||||
ret = SEC_E_OK;
|
||||
exit:
|
||||
sspi_SecBufferFree(&ntlm_v2_temp);
|
||||
sspi_SecBufferFree(&ntlm_v2_temp_chal);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
|
||||
BOOL ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext)
|
||||
{
|
||||
WINPR_RC4_CTX* rc4 = winpr_RC4_New(key, 16);
|
||||
|
||||
if (!rc4)
|
||||
return FALSE;
|
||||
|
||||
const BOOL rc = winpr_RC4_Update(rc4, length, plaintext, ciphertext);
|
||||
winpr_RC4_Free(rc4);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate client challenge (8-byte nonce).
|
||||
* @param context A pointer to the NTLM context
|
||||
*/
|
||||
|
||||
BOOL ntlm_generate_client_challenge(NTLM_CONTEXT* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
/* ClientChallenge is used in computation of LMv2 and NTLMv2 responses */
|
||||
if (memcmp(context->ClientChallenge, NTLM_NULL_BUFFER, sizeof(context->ClientChallenge)) != 0)
|
||||
return TRUE;
|
||||
|
||||
return winpr_RAND(context->ClientChallenge, sizeof(context->ClientChallenge)) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate server challenge (8-byte nonce).
|
||||
* @param context A pointer to the NTLM context
|
||||
*/
|
||||
|
||||
BOOL ntlm_generate_server_challenge(NTLM_CONTEXT* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
if (memcmp(context->ServerChallenge, NTLM_NULL_BUFFER, sizeof(context->ServerChallenge)) != 0)
|
||||
return TRUE;
|
||||
|
||||
return winpr_RAND(context->ServerChallenge, sizeof(context->ServerChallenge)) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate KeyExchangeKey (the 128-bit SessionBaseKey). msdn{cc236710}
|
||||
* @param context A pointer to the NTLM context
|
||||
*/
|
||||
|
||||
BOOL ntlm_generate_key_exchange_key(NTLM_CONTEXT* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(sizeof(context->KeyExchangeKey) == sizeof(context->SessionBaseKey));
|
||||
|
||||
/* In NTLMv2, KeyExchangeKey is the 128-bit SessionBaseKey */
|
||||
CopyMemory(context->KeyExchangeKey, context->SessionBaseKey, sizeof(context->KeyExchangeKey));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate RandomSessionKey (16-byte nonce).
|
||||
* @param context A pointer to the NTLM context
|
||||
*/
|
||||
|
||||
BOOL ntlm_generate_random_session_key(NTLM_CONTEXT* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
return winpr_RAND(context->RandomSessionKey, sizeof(context->RandomSessionKey)) >= 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate ExportedSessionKey (the RandomSessionKey, exported)
|
||||
* @param context A pointer to the NTLM context
|
||||
*/
|
||||
|
||||
BOOL ntlm_generate_exported_session_key(NTLM_CONTEXT* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(sizeof(context->ExportedSessionKey) >= sizeof(context->RandomSessionKey));
|
||||
|
||||
CopyMemory(context->ExportedSessionKey, context->RandomSessionKey,
|
||||
sizeof(context->ExportedSessionKey));
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Encrypt RandomSessionKey (RC4-encrypted RandomSessionKey, using KeyExchangeKey as the key).
|
||||
* @param context A pointer to the NTLM context
|
||||
*/
|
||||
|
||||
BOOL ntlm_encrypt_random_session_key(NTLM_CONTEXT* context)
|
||||
{
|
||||
/* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the
|
||||
* KeyExchangeKey */
|
||||
WINPR_ASSERT(context);
|
||||
return ntlm_rc4k(context->KeyExchangeKey, 16, context->RandomSessionKey,
|
||||
context->EncryptedRandomSessionKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Decrypt RandomSessionKey (RC4-encrypted RandomSessionKey, using KeyExchangeKey as the key).
|
||||
* @param context A pointer to the NTLM context
|
||||
*/
|
||||
|
||||
BOOL ntlm_decrypt_random_session_key(NTLM_CONTEXT* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
|
||||
/* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the
|
||||
* KeyExchangeKey */
|
||||
|
||||
/**
|
||||
* if (NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
|
||||
* Set RandomSessionKey to RC4K(KeyExchangeKey,
|
||||
* AUTHENTICATE_MESSAGE.EncryptedRandomSessionKey) else Set RandomSessionKey to KeyExchangeKey
|
||||
*/
|
||||
if (context->NegotiateKeyExchange)
|
||||
{
|
||||
WINPR_ASSERT(sizeof(context->EncryptedRandomSessionKey) ==
|
||||
sizeof(context->RandomSessionKey));
|
||||
return ntlm_rc4k(context->KeyExchangeKey, sizeof(context->EncryptedRandomSessionKey),
|
||||
context->EncryptedRandomSessionKey, context->RandomSessionKey);
|
||||
}
|
||||
else
|
||||
{
|
||||
WINPR_ASSERT(sizeof(context->RandomSessionKey) == sizeof(context->KeyExchangeKey));
|
||||
CopyMemory(context->RandomSessionKey, context->KeyExchangeKey,
|
||||
sizeof(context->RandomSessionKey));
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate signing key msdn{cc236711}
|
||||
*
|
||||
* @param exported_session_key ExportedSessionKey
|
||||
* @param sign_magic Sign magic string
|
||||
* @param signing_key Destination signing key
|
||||
*
|
||||
* @return \b TRUE for success, \b FALSE for failure
|
||||
*/
|
||||
|
||||
static BOOL ntlm_generate_signing_key(BYTE* exported_session_key, const SecBuffer* sign_magic,
|
||||
BYTE* signing_key)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
size_t length = 0;
|
||||
BYTE* value = nullptr;
|
||||
|
||||
WINPR_ASSERT(exported_session_key);
|
||||
WINPR_ASSERT(sign_magic);
|
||||
WINPR_ASSERT(signing_key);
|
||||
|
||||
length = WINPR_MD5_DIGEST_LENGTH + sign_magic->cbBuffer;
|
||||
value = (BYTE*)malloc(length);
|
||||
|
||||
if (!value)
|
||||
goto out;
|
||||
|
||||
/* Concatenate ExportedSessionKey with sign magic */
|
||||
CopyMemory(value, exported_session_key, WINPR_MD5_DIGEST_LENGTH);
|
||||
CopyMemory(&value[WINPR_MD5_DIGEST_LENGTH], sign_magic->pvBuffer, sign_magic->cbBuffer);
|
||||
|
||||
rc = winpr_Digest(WINPR_MD_MD5, value, length, signing_key, WINPR_MD5_DIGEST_LENGTH);
|
||||
|
||||
out:
|
||||
free(value);
|
||||
return rc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate client signing key (ClientSigningKey). msdn{cc236711}
|
||||
* @param context A pointer to the NTLM context
|
||||
*
|
||||
* @return \b TRUE for success, \b FALSE for failure
|
||||
*/
|
||||
|
||||
BOOL ntlm_generate_client_signing_key(NTLM_CONTEXT* context)
|
||||
{
|
||||
const SecBuffer signMagic = { sizeof(NTLM_CLIENT_SIGN_MAGIC), 0, NTLM_CLIENT_SIGN_MAGIC };
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
return ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic,
|
||||
context->ClientSigningKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate server signing key (ServerSigningKey). msdn{cc236711}
|
||||
* @param context A pointer to the NTLM context
|
||||
*
|
||||
* @return \b TRUE for success, \b FALSE for failure
|
||||
*/
|
||||
|
||||
BOOL ntlm_generate_server_signing_key(NTLM_CONTEXT* context)
|
||||
{
|
||||
const SecBuffer signMagic = { sizeof(NTLM_SERVER_SIGN_MAGIC), 0, NTLM_SERVER_SIGN_MAGIC };
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
return ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic,
|
||||
context->ServerSigningKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate client sealing key (ClientSealingKey). msdn{cc236712}
|
||||
* @param context A pointer to the NTLM context
|
||||
*
|
||||
* @return \b TRUE for success, \b FALSE for failure
|
||||
*/
|
||||
|
||||
BOOL ntlm_generate_client_sealing_key(NTLM_CONTEXT* context)
|
||||
{
|
||||
const SecBuffer sealMagic = { sizeof(NTLM_CLIENT_SEAL_MAGIC), 0, NTLM_CLIENT_SEAL_MAGIC };
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
return ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic,
|
||||
context->ClientSealingKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate server sealing key (ServerSealingKey). msdn{cc236712}
|
||||
* @param context A pointer to the NTLM context
|
||||
*
|
||||
* @return \b TRUE for success, \b FALSE for failure
|
||||
*/
|
||||
|
||||
BOOL ntlm_generate_server_sealing_key(NTLM_CONTEXT* context)
|
||||
{
|
||||
const SecBuffer sealMagic = { sizeof(NTLM_SERVER_SEAL_MAGIC), 0, NTLM_SERVER_SEAL_MAGIC };
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
return ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic,
|
||||
context->ServerSealingKey);
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize RC4 stream cipher states for sealing.
|
||||
* @param context A pointer to the NTLM context
|
||||
*/
|
||||
|
||||
BOOL ntlm_init_rc4_seal_states(NTLM_CONTEXT* context)
|
||||
{
|
||||
WINPR_ASSERT(context);
|
||||
if (context->server)
|
||||
{
|
||||
context->SendSigningKey = context->ServerSigningKey;
|
||||
context->RecvSigningKey = context->ClientSigningKey;
|
||||
context->SendSealingKey = context->ClientSealingKey;
|
||||
context->RecvSealingKey = context->ServerSealingKey;
|
||||
context->SendRc4Seal =
|
||||
winpr_RC4_New(context->ServerSealingKey, sizeof(context->ServerSealingKey));
|
||||
context->RecvRc4Seal =
|
||||
winpr_RC4_New(context->ClientSealingKey, sizeof(context->ClientSealingKey));
|
||||
}
|
||||
else
|
||||
{
|
||||
context->SendSigningKey = context->ClientSigningKey;
|
||||
context->RecvSigningKey = context->ServerSigningKey;
|
||||
context->SendSealingKey = context->ServerSealingKey;
|
||||
context->RecvSealingKey = context->ClientSealingKey;
|
||||
context->SendRc4Seal =
|
||||
winpr_RC4_New(context->ClientSealingKey, sizeof(context->ClientSealingKey));
|
||||
context->RecvRc4Seal =
|
||||
winpr_RC4_New(context->ServerSealingKey, sizeof(context->ServerSealingKey));
|
||||
}
|
||||
if (!context->SendRc4Seal)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to allocate context->SendRc4Seal");
|
||||
return FALSE;
|
||||
}
|
||||
if (!context->RecvRc4Seal)
|
||||
{
|
||||
WLog_ERR(TAG, "Failed to allocate context->RecvRc4Seal");
|
||||
return FALSE;
|
||||
}
|
||||
return TRUE;
|
||||
}
|
||||
|
||||
BOOL ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT32 size)
|
||||
{
|
||||
BOOL rc = FALSE;
|
||||
/*
|
||||
* Compute the HMAC-MD5 hash of ConcatenationOf(NEGOTIATE_MESSAGE,
|
||||
* CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE) using the ExportedSessionKey
|
||||
*/
|
||||
WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
|
||||
|
||||
WINPR_ASSERT(context);
|
||||
WINPR_ASSERT(mic);
|
||||
WINPR_ASSERT(size >= WINPR_MD5_DIGEST_LENGTH);
|
||||
|
||||
memset(mic, 0, size);
|
||||
if (!hmac)
|
||||
return FALSE;
|
||||
|
||||
if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->ExportedSessionKey, WINPR_MD5_DIGEST_LENGTH))
|
||||
goto fail;
|
||||
|
||||
if (!winpr_HMAC_Update(hmac, (BYTE*)context->NegotiateMessage.pvBuffer,
|
||||
context->NegotiateMessage.cbBuffer))
|
||||
goto fail;
|
||||
if (!winpr_HMAC_Update(hmac, (BYTE*)context->ChallengeMessage.pvBuffer,
|
||||
context->ChallengeMessage.cbBuffer))
|
||||
goto fail;
|
||||
|
||||
if (context->MessageIntegrityCheckOffset > 0)
|
||||
{
|
||||
const BYTE* auth = (BYTE*)context->AuthenticateMessage.pvBuffer;
|
||||
const BYTE data[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
|
||||
const size_t rest = context->MessageIntegrityCheckOffset + sizeof(data);
|
||||
|
||||
WINPR_ASSERT(rest <= context->AuthenticateMessage.cbBuffer);
|
||||
if (!winpr_HMAC_Update(hmac, &auth[0], context->MessageIntegrityCheckOffset))
|
||||
goto fail;
|
||||
if (!winpr_HMAC_Update(hmac, data, sizeof(data)))
|
||||
goto fail;
|
||||
if (!winpr_HMAC_Update(hmac, &auth[rest], context->AuthenticateMessage.cbBuffer - rest))
|
||||
goto fail;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!winpr_HMAC_Update(hmac, (BYTE*)context->AuthenticateMessage.pvBuffer,
|
||||
context->AuthenticateMessage.cbBuffer))
|
||||
goto fail;
|
||||
}
|
||||
rc = winpr_HMAC_Final(hmac, mic, WINPR_MD5_DIGEST_LENGTH);
|
||||
|
||||
fail:
|
||||
winpr_HMAC_Free(hmac);
|
||||
return rc;
|
||||
}
|
||||
63
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_compute.h
vendored
Normal file
63
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_compute.h
vendored
Normal file
@@ -0,0 +1,63 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* NTLM Security Package (Compute)
|
||||
*
|
||||
* Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WINPR_SSPI_NTLM_COMPUTE_H
|
||||
#define WINPR_SSPI_NTLM_COMPUTE_H
|
||||
|
||||
#include "ntlm.h"
|
||||
|
||||
#include "ntlm_av_pairs.h"
|
||||
|
||||
BOOL ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo);
|
||||
BOOL ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo);
|
||||
BOOL ntlm_write_version_info(wStream* s, const NTLM_VERSION_INFO* versionInfo);
|
||||
|
||||
#ifdef WITH_DEBUG_NTLM
|
||||
void ntlm_print_version_info(const NTLM_VERSION_INFO* versionInfo);
|
||||
#endif
|
||||
|
||||
BOOL ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response);
|
||||
BOOL ntlm_write_ntlm_v2_response(wStream* s, const NTLMv2_RESPONSE* response);
|
||||
|
||||
void ntlm_output_target_name(NTLM_CONTEXT* context);
|
||||
void ntlm_output_channel_bindings(NTLM_CONTEXT* context);
|
||||
|
||||
void ntlm_generate_timestamp(NTLM_CONTEXT* context);
|
||||
|
||||
SECURITY_STATUS ntlm_compute_lm_v2_response(NTLM_CONTEXT* context);
|
||||
SECURITY_STATUS ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context);
|
||||
|
||||
BOOL ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext);
|
||||
BOOL ntlm_generate_client_challenge(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_generate_server_challenge(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_generate_key_exchange_key(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_generate_random_session_key(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_generate_exported_session_key(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_encrypt_random_session_key(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_decrypt_random_session_key(NTLM_CONTEXT* context);
|
||||
|
||||
BOOL ntlm_generate_client_signing_key(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_generate_server_signing_key(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_generate_client_sealing_key(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_generate_server_sealing_key(NTLM_CONTEXT* context);
|
||||
BOOL ntlm_init_rc4_seal_states(NTLM_CONTEXT* context);
|
||||
|
||||
BOOL ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT32 size);
|
||||
|
||||
#endif /* WINPR_AUTH_NTLM_COMPUTE_H */
|
||||
40
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_export.h
vendored
Normal file
40
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_export.h
vendored
Normal file
@@ -0,0 +1,40 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* NTLM Security Package
|
||||
*
|
||||
* Copyright 2021 Armin Novak <armin.novak@thincast.com>
|
||||
* Copyright 2021 Thincast Technologies GmbH
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WINPR_SSPI_NTLM_EXPORT_H
|
||||
#define WINPR_SSPI_NTLM_EXPORT_H
|
||||
|
||||
#include <winpr/sspi.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C"
|
||||
{
|
||||
#endif
|
||||
|
||||
extern const SecPkgInfoA NTLM_SecPkgInfoA;
|
||||
extern const SecPkgInfoW NTLM_SecPkgInfoW;
|
||||
extern const SecurityFunctionTableA NTLM_SecurityFunctionTableA;
|
||||
extern const SecurityFunctionTableW NTLM_SecurityFunctionTableW;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif
|
||||
1437
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_message.c
vendored
Normal file
1437
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_message.c
vendored
Normal file
File diff suppressed because it is too large
Load Diff
36
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_message.h
vendored
Normal file
36
third_party/FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_message.h
vendored
Normal file
@@ -0,0 +1,36 @@
|
||||
/**
|
||||
* WinPR: Windows Portable Runtime
|
||||
* NTLM Security Package (Message)
|
||||
*
|
||||
* Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WINPR_SSPI_NTLM_MESSAGE_H
|
||||
#define WINPR_SSPI_NTLM_MESSAGE_H
|
||||
|
||||
#include "ntlm.h"
|
||||
|
||||
SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
|
||||
SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
|
||||
SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
|
||||
SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
|
||||
SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
|
||||
SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
|
||||
|
||||
SECURITY_STATUS ntlm_server_AuthenticateComplete(NTLM_CONTEXT* context);
|
||||
|
||||
const char* ntlm_get_negotiate_string(UINT32 flag);
|
||||
|
||||
#endif /* WINPR_SSPI_NTLM_MESSAGE_H */
|
||||
Reference in New Issue
Block a user