summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/sspi/NTLM/ntlm_message.c
diff options
context:
space:
mode:
Diffstat (limited to 'winpr/libwinpr/sspi/NTLM/ntlm_message.c')
-rw-r--r--winpr/libwinpr/sspi/NTLM/ntlm_message.c1397
1 files changed, 1397 insertions, 0 deletions
diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_message.c b/winpr/libwinpr/sspi/NTLM/ntlm_message.c
new file mode 100644
index 0000000..511ca47
--- /dev/null
+++ b/winpr/libwinpr/sspi/NTLM/ntlm_message.c
@@ -0,0 +1,1397 @@
+/**
+ * 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.
+ */
+
+#include <winpr/config.h>
+
+#include "ntlm.h"
+#include "../sspi.h"
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/print.h>
+#include <winpr/stream.h>
+#include <winpr/sysinfo.h>
+
+#include "ntlm_compute.h"
+
+#include "ntlm_message.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 const char NTLM_SIGNATURE[8] = { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' };
+
+static void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields);
+
+const char* ntlm_get_negotiate_string(UINT32 flag)
+{
+ if (flag & NTLMSSP_NEGOTIATE_56)
+ return "NTLMSSP_NEGOTIATE_56";
+ if (flag & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ return "NTLMSSP_NEGOTIATE_KEY_EXCH";
+ if (flag & NTLMSSP_NEGOTIATE_128)
+ return "NTLMSSP_NEGOTIATE_128";
+ if (flag & NTLMSSP_RESERVED1)
+ return "NTLMSSP_RESERVED1";
+ if (flag & NTLMSSP_RESERVED2)
+ return "NTLMSSP_RESERVED2";
+ if (flag & NTLMSSP_RESERVED3)
+ return "NTLMSSP_RESERVED3";
+ if (flag & NTLMSSP_NEGOTIATE_VERSION)
+ return "NTLMSSP_NEGOTIATE_VERSION";
+ if (flag & NTLMSSP_RESERVED4)
+ return "NTLMSSP_RESERVED4";
+ if (flag & NTLMSSP_NEGOTIATE_TARGET_INFO)
+ return "NTLMSSP_NEGOTIATE_TARGET_INFO";
+ if (flag & NTLMSSP_REQUEST_NON_NT_SESSION_KEY)
+ return "NTLMSSP_REQUEST_NON_NT_SESSION_KEY";
+ if (flag & NTLMSSP_RESERVED5)
+ return "NTLMSSP_RESERVED5";
+ if (flag & NTLMSSP_NEGOTIATE_IDENTIFY)
+ return "NTLMSSP_NEGOTIATE_IDENTIFY";
+ if (flag & NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY)
+ return "NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY";
+ if (flag & NTLMSSP_RESERVED6)
+ return "NTLMSSP_RESERVED6";
+ if (flag & NTLMSSP_TARGET_TYPE_SERVER)
+ return "NTLMSSP_TARGET_TYPE_SERVER";
+ if (flag & NTLMSSP_TARGET_TYPE_DOMAIN)
+ return "NTLMSSP_TARGET_TYPE_DOMAIN";
+ if (flag & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
+ return "NTLMSSP_NEGOTIATE_ALWAYS_SIGN";
+ if (flag & NTLMSSP_RESERVED7)
+ return "NTLMSSP_RESERVED7";
+ if (flag & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ return "NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED";
+ if (flag & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
+ return "NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED";
+ if (flag & NTLMSSP_NEGOTIATE_ANONYMOUS)
+ return "NTLMSSP_NEGOTIATE_ANONYMOUS";
+ if (flag & NTLMSSP_RESERVED8)
+ return "NTLMSSP_RESERVED8";
+ if (flag & NTLMSSP_NEGOTIATE_NTLM)
+ return "NTLMSSP_NEGOTIATE_NTLM";
+ if (flag & NTLMSSP_RESERVED9)
+ return "NTLMSSP_RESERVED9";
+ if (flag & NTLMSSP_NEGOTIATE_LM_KEY)
+ return "NTLMSSP_NEGOTIATE_LM_KEY";
+ if (flag & NTLMSSP_NEGOTIATE_DATAGRAM)
+ return "NTLMSSP_NEGOTIATE_DATAGRAM";
+ if (flag & NTLMSSP_NEGOTIATE_SEAL)
+ return "NTLMSSP_NEGOTIATE_SEAL";
+ if (flag & NTLMSSP_NEGOTIATE_SIGN)
+ return "NTLMSSP_NEGOTIATE_SIGN";
+ if (flag & NTLMSSP_RESERVED10)
+ return "NTLMSSP_RESERVED10";
+ if (flag & NTLMSSP_REQUEST_TARGET)
+ return "NTLMSSP_REQUEST_TARGET";
+ if (flag & NTLMSSP_NEGOTIATE_OEM)
+ return "NTLMSSP_NEGOTIATE_OEM";
+ if (flag & NTLMSSP_NEGOTIATE_UNICODE)
+ return "NTLMSSP_NEGOTIATE_UNICODE";
+ return "NTLMSSP_NEGOTIATE_UNKNOWN";
+}
+
+#if defined(WITH_DEBUG_NTLM)
+static void ntlm_print_message_fields(const NTLM_MESSAGE_FIELDS* fields, const char* name)
+{
+ WINPR_ASSERT(fields);
+ WINPR_ASSERT(name);
+
+ WLog_VRB(TAG, "%s (Len: %" PRIu16 " MaxLen: %" PRIu16 " BufferOffset: %" PRIu32 ")", name,
+ fields->Len, fields->MaxLen, fields->BufferOffset);
+
+ if (fields->Len > 0)
+ winpr_HexDump(TAG, WLOG_TRACE, fields->Buffer, fields->Len);
+}
+
+static void ntlm_print_negotiate_flags(UINT32 flags)
+{
+ WLog_VRB(TAG, "negotiateFlags \"0x%08" PRIX32 "\"", flags);
+
+ for (int i = 31; i >= 0; i--)
+ {
+ if ((flags >> i) & 1)
+ {
+ const char* str = ntlm_get_negotiate_string(1 << i);
+ WLog_VRB(TAG, "\t%s (%d),", str, (31 - i));
+ }
+ }
+}
+
+static void ntlm_print_negotiate_message(const SecBuffer* NegotiateMessage,
+ const NTLM_NEGOTIATE_MESSAGE* message)
+{
+ WINPR_ASSERT(NegotiateMessage);
+ WINPR_ASSERT(message);
+
+ WLog_VRB(TAG, "NEGOTIATE_MESSAGE (length = %" PRIu32 ")", NegotiateMessage->cbBuffer);
+ winpr_HexDump(TAG, WLOG_TRACE, NegotiateMessage->pvBuffer, NegotiateMessage->cbBuffer);
+ ntlm_print_negotiate_flags(message->NegotiateFlags);
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ ntlm_print_version_info(&(message->Version));
+}
+
+static void ntlm_print_challenge_message(const SecBuffer* ChallengeMessage,
+ const NTLM_CHALLENGE_MESSAGE* message,
+ const SecBuffer* ChallengeTargetInfo)
+{
+ WINPR_ASSERT(ChallengeMessage);
+ WINPR_ASSERT(message);
+
+ WLog_VRB(TAG, "CHALLENGE_MESSAGE (length = %" PRIu32 ")", ChallengeMessage->cbBuffer);
+ winpr_HexDump(TAG, WLOG_TRACE, ChallengeMessage->pvBuffer, ChallengeMessage->cbBuffer);
+ ntlm_print_negotiate_flags(message->NegotiateFlags);
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ ntlm_print_version_info(&(message->Version));
+
+ ntlm_print_message_fields(&(message->TargetName), "TargetName");
+ ntlm_print_message_fields(&(message->TargetInfo), "TargetInfo");
+
+ if (ChallengeTargetInfo && (ChallengeTargetInfo->cbBuffer > 0))
+ {
+ WLog_VRB(TAG, "ChallengeTargetInfo (%" PRIu32 "):", ChallengeTargetInfo->cbBuffer);
+ ntlm_print_av_pair_list(ChallengeTargetInfo->pvBuffer, ChallengeTargetInfo->cbBuffer);
+ }
+}
+
+static void ntlm_print_authenticate_message(const SecBuffer* AuthenticateMessage,
+ const NTLM_AUTHENTICATE_MESSAGE* message, UINT32 flags,
+ const SecBuffer* AuthenticateTargetInfo)
+{
+ WINPR_ASSERT(AuthenticateMessage);
+ WINPR_ASSERT(message);
+
+ WLog_VRB(TAG, "AUTHENTICATE_MESSAGE (length = %" PRIu32 ")", AuthenticateMessage->cbBuffer);
+ winpr_HexDump(TAG, WLOG_TRACE, AuthenticateMessage->pvBuffer, AuthenticateMessage->cbBuffer);
+ ntlm_print_negotiate_flags(message->NegotiateFlags);
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ ntlm_print_version_info(&(message->Version));
+
+ if (AuthenticateTargetInfo && (AuthenticateTargetInfo->cbBuffer > 0))
+ {
+ WLog_VRB(TAG, "AuthenticateTargetInfo (%" PRIu32 "):", AuthenticateTargetInfo->cbBuffer);
+ ntlm_print_av_pair_list(AuthenticateTargetInfo->pvBuffer, AuthenticateTargetInfo->cbBuffer);
+ }
+
+ ntlm_print_message_fields(&(message->DomainName), "DomainName");
+ ntlm_print_message_fields(&(message->UserName), "UserName");
+ ntlm_print_message_fields(&(message->Workstation), "Workstation");
+ ntlm_print_message_fields(&(message->LmChallengeResponse), "LmChallengeResponse");
+ ntlm_print_message_fields(&(message->NtChallengeResponse), "NtChallengeResponse");
+ ntlm_print_message_fields(&(message->EncryptedRandomSessionKey), "EncryptedRandomSessionKey");
+
+ if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
+ {
+ WLog_VRB(TAG, "MessageIntegrityCheck (length = 16)");
+ winpr_HexDump(TAG, WLOG_TRACE, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck));
+ }
+}
+
+static void ntlm_print_authentication_complete(const NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+
+ WLog_VRB(TAG, "ClientChallenge");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ClientChallenge, 8);
+ WLog_VRB(TAG, "ServerChallenge");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ServerChallenge, 8);
+ WLog_VRB(TAG, "SessionBaseKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->SessionBaseKey, 16);
+ WLog_VRB(TAG, "KeyExchangeKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->KeyExchangeKey, 16);
+ WLog_VRB(TAG, "ExportedSessionKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ExportedSessionKey, 16);
+ WLog_VRB(TAG, "RandomSessionKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->RandomSessionKey, 16);
+ WLog_VRB(TAG, "ClientSigningKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ClientSigningKey, 16);
+ WLog_VRB(TAG, "ClientSealingKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ClientSealingKey, 16);
+ WLog_VRB(TAG, "ServerSigningKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ServerSigningKey, 16);
+ WLog_VRB(TAG, "ServerSealingKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ServerSealingKey, 16);
+ WLog_VRB(TAG, "Timestamp");
+ winpr_HexDump(TAG, WLOG_TRACE, context->Timestamp, 8);
+}
+#endif
+
+static BOOL ntlm_read_message_header(wStream* s, NTLM_MESSAGE_HEADER* header, UINT32 expected)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(header);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return FALSE;
+
+ Stream_Read(s, header->Signature, 8);
+ Stream_Read_UINT32(s, header->MessageType);
+
+ if (strncmp((char*)header->Signature, NTLM_SIGNATURE, 8) != 0)
+ {
+ WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid signature, got %s, expected %s",
+ header->Signature, NTLM_SIGNATURE);
+ return FALSE;
+ }
+
+ if (header->MessageType != expected)
+ {
+ WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid message tyep, got %s, expected %s",
+ ntlm_message_type_string(header->MessageType), ntlm_message_type_string(expected));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL ntlm_write_message_header(wStream* s, const NTLM_MESSAGE_HEADER* header)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(header);
+
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, sizeof(NTLM_SIGNATURE) + 4ull,
+ "NTLM_MESSAGE_HEADER::header"))
+ return FALSE;
+
+ Stream_Write(s, header->Signature, sizeof(NTLM_SIGNATURE));
+ Stream_Write_UINT32(s, header->MessageType);
+
+ return TRUE;
+}
+
+static BOOL ntlm_populate_message_header(NTLM_MESSAGE_HEADER* header, UINT32 MessageType)
+{
+ WINPR_ASSERT(header);
+
+ CopyMemory(header->Signature, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
+ header->MessageType = MessageType;
+ return TRUE;
+}
+
+static BOOL ntlm_read_message_fields(wStream* s, NTLM_MESSAGE_FIELDS* fields)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(fields);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ ntlm_free_message_fields_buffer(fields);
+
+ Stream_Read_UINT16(s, fields->Len); /* Len (2 bytes) */
+ Stream_Read_UINT16(s, fields->MaxLen); /* MaxLen (2 bytes) */
+ Stream_Read_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
+ return TRUE;
+}
+
+static BOOL ntlm_write_message_fields(wStream* s, const NTLM_MESSAGE_FIELDS* fields)
+{
+ UINT16 MaxLen = 0;
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(fields);
+
+ MaxLen = fields->MaxLen;
+ if (fields->MaxLen < 1)
+ MaxLen = fields->Len;
+
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), 8, "NTLM_MESSAGE_FIELDS::header"))
+ return FALSE;
+
+ Stream_Write_UINT16(s, fields->Len); /* Len (2 bytes) */
+ Stream_Write_UINT16(s, MaxLen); /* MaxLen (2 bytes) */
+ Stream_Write_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
+ return TRUE;
+}
+
+static BOOL ntlm_read_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(fields);
+
+ if (fields->Len > 0)
+ {
+ const UINT32 offset = fields->BufferOffset + fields->Len;
+
+ if (fields->BufferOffset > UINT32_MAX - fields->Len)
+ {
+ WLog_ERR(TAG,
+ "NTLM_MESSAGE_FIELDS::BufferOffset %" PRIu32
+ " too large, maximum allowed is %" PRIu32,
+ fields->BufferOffset, UINT32_MAX - fields->Len);
+ return FALSE;
+ }
+
+ if (offset > Stream_Length(s))
+ {
+ WLog_ERR(TAG,
+ "NTLM_MESSAGE_FIELDS::Buffer offset %" PRIu32 " beyond received data %" PRIuz,
+ offset, Stream_Length(s));
+ return FALSE;
+ }
+
+ fields->Buffer = (PBYTE)malloc(fields->Len);
+
+ if (!fields->Buffer)
+ {
+ WLog_ERR(TAG, "NTLM_MESSAGE_FIELDS::Buffer allocation of %" PRIu16 "bytes failed",
+ fields->Len);
+ return FALSE;
+ }
+
+ Stream_SetPosition(s, fields->BufferOffset);
+ Stream_Read(s, fields->Buffer, fields->Len);
+ }
+
+ return TRUE;
+}
+
+static BOOL ntlm_write_message_fields_buffer(wStream* s, const NTLM_MESSAGE_FIELDS* fields)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(fields);
+
+ if (fields->Len > 0)
+ {
+ Stream_SetPosition(s, fields->BufferOffset);
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), fields->Len, "NTLM_MESSAGE_FIELDS::Len"))
+ return FALSE;
+
+ Stream_Write(s, fields->Buffer, fields->Len);
+ }
+ return TRUE;
+}
+
+void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields)
+{
+ if (fields)
+ {
+ if (fields->Buffer)
+ {
+ free(fields->Buffer);
+ fields->Len = 0;
+ fields->MaxLen = 0;
+ fields->Buffer = NULL;
+ fields->BufferOffset = 0;
+ }
+ }
+}
+
+static BOOL ntlm_read_negotiate_flags(wStream* s, UINT32* flags, UINT32 required, const char* name)
+{
+ UINT32 NegotiateFlags = 0;
+ char buffer[1024] = { 0 };
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(flags);
+ WINPR_ASSERT(name);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, NegotiateFlags); /* NegotiateFlags (4 bytes) */
+
+ if ((NegotiateFlags & required) != required)
+ {
+ WLog_ERR(TAG, "%s::NegotiateFlags invalid flags 0x08%" PRIx32 ", 0x%08" PRIx32 " required",
+ name, NegotiateFlags, required);
+ return FALSE;
+ }
+
+ WLog_DBG(TAG, "Read flags %s",
+ ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), NegotiateFlags));
+ *flags = NegotiateFlags;
+ return TRUE;
+}
+
+static BOOL ntlm_write_negotiate_flags(wStream* s, UINT32 flags, const char* name)
+{
+ char buffer[1024] = { 0 };
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(name);
+
+ if (!Stream_CheckAndLogRequiredCapacityEx(TAG, WLOG_WARN, s, 4ull, 1ull,
+ "%s(%s:%" PRIuz ") %s::NegotiateFlags", __func__,
+ __FILE__, (size_t)__LINE__, name))
+ return FALSE;
+
+ WLog_DBG(TAG, "Write flags %s", ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), flags));
+ Stream_Write_UINT32(s, flags); /* NegotiateFlags (4 bytes) */
+ return TRUE;
+}
+
+static BOOL ntlm_read_message_integrity_check(wStream* s, size_t* offset, BYTE* data, size_t size,
+ const char* name)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(offset);
+ WINPR_ASSERT(data);
+ WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH);
+ WINPR_ASSERT(name);
+
+ *offset = Stream_GetPosition(s);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
+ return FALSE;
+
+ Stream_Read(s, data, size);
+ return TRUE;
+}
+
+static BOOL ntlm_write_message_integrity_check(wStream* s, size_t offset, const BYTE* data,
+ size_t size, const char* name)
+{
+ size_t pos = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(data);
+ WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH);
+ WINPR_ASSERT(name);
+
+ pos = Stream_GetPosition(s);
+
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, offset, "MessageIntegrityCheck::offset"))
+ return FALSE;
+
+ Stream_SetPosition(s, offset);
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, size, "MessageIntegrityCheck::size"))
+ return FALSE;
+
+ Stream_Write(s, data, size);
+ Stream_SetPosition(s, pos);
+ return TRUE;
+}
+
+SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
+{
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ const NTLM_NEGOTIATE_MESSAGE empty = { 0 };
+ NTLM_NEGOTIATE_MESSAGE* message = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(buffer);
+
+ message = &context->NEGOTIATE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_NEGOTIATE))
+ return SEC_E_INVALID_TOKEN;
+
+ if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags,
+ NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_UNICODE,
+ "NTLM_NEGOTIATE_MESSAGE"))
+ return SEC_E_INVALID_TOKEN;
+
+ context->NegotiateFlags = message->NegotiateFlags;
+
+ /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
+ // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
+ {
+ if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
+ return SEC_E_INVALID_TOKEN;
+ }
+
+ /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */
+ // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ {
+ if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
+ return SEC_E_INVALID_TOKEN;
+ }
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
+ return SEC_E_INVALID_TOKEN;
+ }
+
+ if (!ntlm_read_message_fields_buffer(s, &message->DomainName))
+ return SEC_E_INVALID_TOKEN;
+
+ if (!ntlm_read_message_fields_buffer(s, &message->Workstation))
+ return SEC_E_INVALID_TOKEN;
+
+ length = Stream_GetPosition(s);
+ WINPR_ASSERT(length <= ULONG_MAX);
+ buffer->cbBuffer = (ULONG)length;
+
+ if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length))
+ return SEC_E_INTERNAL_ERROR;
+
+ CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer);
+ context->NegotiateMessage.BufferType = buffer->BufferType;
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_negotiate_message(&context->NegotiateMessage, message);
+#endif
+ ntlm_change_state(context, NTLM_STATE_CHALLENGE);
+ return SEC_I_CONTINUE_NEEDED;
+}
+
+SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer)
+{
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ const NTLM_NEGOTIATE_MESSAGE empty = { 0 };
+ NTLM_NEGOTIATE_MESSAGE* message = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(buffer);
+
+ message = &context->NEGOTIATE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_NEGOTIATE))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (context->NTLMv2)
+ {
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_LM_KEY;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM;
+ }
+
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
+ message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
+
+ if (context->confidentiality)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
+
+ if (context->SendVersionInfo)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ ntlm_get_version_info(&(message->Version));
+
+ context->NegotiateFlags = message->NegotiateFlags;
+ /* Message Header (12 bytes) */
+ if (!ntlm_write_message_header(s, &message->header))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_NEGOTIATE_MESSAGE"))
+ return SEC_E_INTERNAL_ERROR;
+
+ /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
+ /* DomainNameFields (8 bytes) */
+ if (!ntlm_write_message_fields(s, &(message->DomainName)))
+ return SEC_E_INTERNAL_ERROR;
+
+ /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */
+ /* WorkstationFields (8 bytes) */
+ if (!ntlm_write_message_fields(s, &(message->Workstation)))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_write_version_info(s, &(message->Version)))
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ length = Stream_GetPosition(s);
+ WINPR_ASSERT(length <= ULONG_MAX);
+ buffer->cbBuffer = (ULONG)length;
+
+ if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length))
+ return SEC_E_INTERNAL_ERROR;
+
+ CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer);
+ context->NegotiateMessage.BufferType = buffer->BufferType;
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_negotiate_message(&context->NegotiateMessage, message);
+#endif
+ ntlm_change_state(context, NTLM_STATE_CHALLENGE);
+ return SEC_I_CONTINUE_NEEDED;
+}
+
+SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
+{
+ SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ size_t StartOffset = 0;
+ size_t PayloadOffset = 0;
+ NTLM_AV_PAIR* AvTimestamp = NULL;
+ const NTLM_CHALLENGE_MESSAGE empty = { 0 };
+ NTLM_CHALLENGE_MESSAGE* message = NULL;
+
+ if (!context || !buffer)
+ return SEC_E_INTERNAL_ERROR;
+
+ ntlm_generate_client_challenge(context);
+ message = &context->CHALLENGE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ StartOffset = Stream_GetPosition(s);
+
+ if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_CHALLENGE))
+ goto fail;
+
+ if (!ntlm_read_message_fields(s, &(message->TargetName))) /* TargetNameFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_CHALLENGE_MESSAGE"))
+ goto fail;
+
+ context->NegotiateFlags = message->NegotiateFlags;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
+ goto fail;
+
+ Stream_Read(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
+ CopyMemory(context->ServerChallenge, message->ServerChallenge, 8);
+ Stream_Read(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */
+
+ if (!ntlm_read_message_fields(s, &(message->TargetInfo))) /* TargetInfoFields (8 bytes) */
+ goto fail;
+
+ if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
+ goto fail;
+ }
+
+ /* Payload (variable) */
+ PayloadOffset = Stream_GetPosition(s);
+
+ status = SEC_E_INTERNAL_ERROR;
+ if (message->TargetName.Len > 0)
+ {
+ if (!ntlm_read_message_fields_buffer(s, &(message->TargetName)))
+ goto fail;
+ }
+
+ if (message->TargetInfo.Len > 0)
+ {
+ size_t cbAvTimestamp = 0;
+
+ if (!ntlm_read_message_fields_buffer(s, &(message->TargetInfo)))
+ goto fail;
+
+ context->ChallengeTargetInfo.pvBuffer = message->TargetInfo.Buffer;
+ context->ChallengeTargetInfo.cbBuffer = message->TargetInfo.Len;
+ AvTimestamp = ntlm_av_pair_get((NTLM_AV_PAIR*)message->TargetInfo.Buffer,
+ message->TargetInfo.Len, MsvAvTimestamp, &cbAvTimestamp);
+
+ if (AvTimestamp)
+ {
+ PBYTE ptr = ntlm_av_pair_get_value_pointer(AvTimestamp);
+
+ if (!ptr)
+ goto fail;
+
+ if (context->NTLMv2)
+ context->UseMIC = TRUE;
+
+ CopyMemory(context->ChallengeTimestamp, ptr, 8);
+ }
+ }
+
+ length = (PayloadOffset - StartOffset) + message->TargetName.Len + message->TargetInfo.Len;
+ if (length > buffer->cbBuffer)
+ goto fail;
+
+ if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length))
+ goto fail;
+
+ if (context->ChallengeMessage.pvBuffer)
+ CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s) + StartOffset, length);
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_challenge_message(&context->ChallengeMessage, message, NULL);
+#endif
+ /* AV_PAIRs */
+
+ if (context->NTLMv2)
+ {
+ if (!ntlm_construct_authenticate_target_info(context))
+ goto fail;
+
+ sspi_SecBufferFree(&context->ChallengeTargetInfo);
+ context->ChallengeTargetInfo.pvBuffer = context->AuthenticateTargetInfo.pvBuffer;
+ context->ChallengeTargetInfo.cbBuffer = context->AuthenticateTargetInfo.cbBuffer;
+ }
+
+ ntlm_generate_timestamp(context); /* Timestamp */
+
+ if (!ntlm_compute_lm_v2_response(context)) /* LmChallengeResponse */
+ goto fail;
+
+ if (!ntlm_compute_ntlm_v2_response(context)) /* NtChallengeResponse */
+ goto fail;
+
+ ntlm_generate_key_exchange_key(context); /* KeyExchangeKey */
+ ntlm_generate_random_session_key(context); /* RandomSessionKey */
+ ntlm_generate_exported_session_key(context); /* ExportedSessionKey */
+ ntlm_encrypt_random_session_key(context); /* EncryptedRandomSessionKey */
+ /* Generate signing keys */
+ if (!ntlm_generate_client_signing_key(context))
+ goto fail;
+ if (!ntlm_generate_server_signing_key(context))
+ goto fail;
+ /* Generate sealing keys */
+ if (!ntlm_generate_client_sealing_key(context))
+ goto fail;
+ if (!ntlm_generate_server_sealing_key(context))
+ goto fail;
+ /* Initialize RC4 seal state using client sealing key */
+ if (!ntlm_init_rc4_seal_states(context))
+ goto fail;
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_authentication_complete(context);
+#endif
+ ntlm_change_state(context, NTLM_STATE_AUTHENTICATE);
+ status = SEC_I_CONTINUE_NEEDED;
+fail:
+ ntlm_free_message_fields_buffer(&(message->TargetName));
+ return status;
+}
+
+SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, const PSecBuffer buffer)
+{
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ UINT32 PayloadOffset = 0;
+ const NTLM_CHALLENGE_MESSAGE empty = { 0 };
+ NTLM_CHALLENGE_MESSAGE* message = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(buffer);
+
+ message = &context->CHALLENGE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ ntlm_get_version_info(&(message->Version)); /* Version */
+ ntlm_generate_server_challenge(context); /* Server Challenge */
+ ntlm_generate_timestamp(context); /* Timestamp */
+
+ if (!ntlm_construct_challenge_target_info(context)) /* TargetInfo */
+ return SEC_E_INTERNAL_ERROR;
+
+ CopyMemory(message->ServerChallenge, context->ServerChallenge, 8); /* ServerChallenge */
+ message->NegotiateFlags = context->NegotiateFlags;
+ if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_CHALLENGE))
+ return SEC_E_INTERNAL_ERROR;
+
+ /* Message Header (12 bytes) */
+ if (!ntlm_write_message_header(s, &message->header))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
+ {
+ message->TargetName.Len = (UINT16)context->TargetName.cbBuffer;
+ message->TargetName.Buffer = (PBYTE)context->TargetName.pvBuffer;
+ }
+
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
+ {
+ message->TargetInfo.Len = (UINT16)context->ChallengeTargetInfo.cbBuffer;
+ message->TargetInfo.Buffer = (PBYTE)context->ChallengeTargetInfo.pvBuffer;
+ }
+
+ PayloadOffset = 48;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ PayloadOffset += 8;
+
+ message->TargetName.BufferOffset = PayloadOffset;
+ message->TargetInfo.BufferOffset = message->TargetName.BufferOffset + message->TargetName.Len;
+ /* TargetNameFields (8 bytes) */
+ if (!ntlm_write_message_fields(s, &(message->TargetName)))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_CHALLENGE_MESSAGE"))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 16, "NTLM_CHALLENGE_MESSAGE::ServerChallenge"))
+ return SEC_E_INTERNAL_ERROR;
+
+ Stream_Write(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
+ Stream_Write(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */
+
+ /* TargetInfoFields (8 bytes) */
+ if (!ntlm_write_message_fields(s, &(message->TargetInfo)))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ /* Payload (variable) */
+ if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
+ {
+ if (!ntlm_write_message_fields_buffer(s, &(message->TargetName)))
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
+ {
+ if (!ntlm_write_message_fields_buffer(s, &(message->TargetInfo)))
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ length = Stream_GetPosition(s);
+ WINPR_ASSERT(length <= ULONG_MAX);
+ buffer->cbBuffer = (ULONG)length;
+
+ if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length))
+ return SEC_E_INTERNAL_ERROR;
+
+ CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s), length);
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_challenge_message(&context->ChallengeMessage, message,
+ &context->ChallengeTargetInfo);
+#endif
+ ntlm_change_state(context, NTLM_STATE_AUTHENTICATE);
+ return SEC_I_CONTINUE_NEEDED;
+}
+
+SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
+{
+ SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ UINT32 flags = 0;
+ NTLM_AV_PAIR* AvFlags = NULL;
+ size_t PayloadBufferOffset = 0;
+ const NTLM_AUTHENTICATE_MESSAGE empty = { 0 };
+ NTLM_AUTHENTICATE_MESSAGE* message = NULL;
+ SSPI_CREDENTIALS* credentials = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(buffer);
+
+ credentials = context->credentials;
+ WINPR_ASSERT(credentials);
+
+ message = &context->AUTHENTICATE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_AUTHENTICATE))
+ goto fail;
+
+ if (!ntlm_read_message_fields(
+ s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_message_fields(
+ s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_message_fields(
+ s,
+ &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_AUTHENTICATE_MESSAGE"))
+ goto fail;
+
+ context->NegotiateKeyExchange =
+ (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) ? TRUE : FALSE;
+
+ if ((context->NegotiateKeyExchange && !message->EncryptedRandomSessionKey.Len) ||
+ (!context->NegotiateKeyExchange && message->EncryptedRandomSessionKey.Len))
+ goto fail;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
+ goto fail;
+ }
+
+ PayloadBufferOffset = Stream_GetPosition(s);
+
+ status = SEC_E_INTERNAL_ERROR;
+ if (!ntlm_read_message_fields_buffer(s, &(message->DomainName))) /* DomainName */
+ goto fail;
+
+ if (!ntlm_read_message_fields_buffer(s, &(message->UserName))) /* UserName */
+ goto fail;
+
+ if (!ntlm_read_message_fields_buffer(s, &(message->Workstation))) /* Workstation */
+ goto fail;
+
+ if (!ntlm_read_message_fields_buffer(s,
+ &(message->LmChallengeResponse))) /* LmChallengeResponse */
+ goto fail;
+
+ if (!ntlm_read_message_fields_buffer(s,
+ &(message->NtChallengeResponse))) /* NtChallengeResponse */
+ goto fail;
+
+ if (message->NtChallengeResponse.Len > 0)
+ {
+ size_t cbAvFlags = 0;
+ wStream ssbuffer;
+ wStream* snt = Stream_StaticConstInit(&ssbuffer, message->NtChallengeResponse.Buffer,
+ message->NtChallengeResponse.Len);
+
+ if (!snt)
+ goto fail;
+
+ status = SEC_E_INVALID_TOKEN;
+ if (!ntlm_read_ntlm_v2_response(snt, &(context->NTLMv2Response)))
+ goto fail;
+ status = SEC_E_INTERNAL_ERROR;
+
+ context->NtChallengeResponse.pvBuffer = message->NtChallengeResponse.Buffer;
+ context->NtChallengeResponse.cbBuffer = message->NtChallengeResponse.Len;
+ sspi_SecBufferFree(&(context->ChallengeTargetInfo));
+ context->ChallengeTargetInfo.pvBuffer = (void*)context->NTLMv2Response.Challenge.AvPairs;
+ context->ChallengeTargetInfo.cbBuffer = message->NtChallengeResponse.Len - (28 + 16);
+ CopyMemory(context->ClientChallenge, context->NTLMv2Response.Challenge.ClientChallenge, 8);
+ AvFlags =
+ ntlm_av_pair_get(context->NTLMv2Response.Challenge.AvPairs,
+ context->NTLMv2Response.Challenge.cbAvPairs, MsvAvFlags, &cbAvFlags);
+
+ if (AvFlags)
+ Data_Read_UINT32(ntlm_av_pair_get_value_pointer(AvFlags), flags);
+ }
+
+ if (!ntlm_read_message_fields_buffer(
+ s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
+ goto fail;
+
+ if (message->EncryptedRandomSessionKey.Len > 0)
+ {
+ if (message->EncryptedRandomSessionKey.Len != 16)
+ goto fail;
+
+ CopyMemory(context->EncryptedRandomSessionKey, message->EncryptedRandomSessionKey.Buffer,
+ 16);
+ }
+
+ length = Stream_GetPosition(s);
+ WINPR_ASSERT(length <= ULONG_MAX);
+
+ if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length))
+ goto fail;
+
+ CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
+ buffer->cbBuffer = (ULONG)length;
+ Stream_SetPosition(s, PayloadBufferOffset);
+
+ if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
+ {
+ status = SEC_E_INVALID_TOKEN;
+ if (!ntlm_read_message_integrity_check(
+ s, &context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
+ goto fail;
+ }
+
+ status = SEC_E_INTERNAL_ERROR;
+
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_authenticate_message(&context->AuthenticateMessage, message, flags, NULL);
+#endif
+
+ if (message->UserName.Len > 0)
+ {
+ credentials->identity.User = (UINT16*)malloc(message->UserName.Len);
+
+ if (!credentials->identity.User)
+ goto fail;
+
+ CopyMemory(credentials->identity.User, message->UserName.Buffer, message->UserName.Len);
+ credentials->identity.UserLength = message->UserName.Len / 2;
+ }
+
+ if (message->DomainName.Len > 0)
+ {
+ credentials->identity.Domain = (UINT16*)malloc(message->DomainName.Len);
+
+ if (!credentials->identity.Domain)
+ goto fail;
+
+ CopyMemory(credentials->identity.Domain, message->DomainName.Buffer,
+ message->DomainName.Len);
+ credentials->identity.DomainLength = message->DomainName.Len / 2;
+ }
+
+ if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
+ {
+ if (!ntlm_compute_lm_v2_response(context)) /* LmChallengeResponse */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (!ntlm_compute_ntlm_v2_response(context)) /* NtChallengeResponse */
+ return SEC_E_INTERNAL_ERROR;
+
+ /* KeyExchangeKey */
+ ntlm_generate_key_exchange_key(context);
+ /* EncryptedRandomSessionKey */
+ ntlm_decrypt_random_session_key(context);
+ /* ExportedSessionKey */
+ ntlm_generate_exported_session_key(context);
+
+ if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
+ {
+ BYTE messageIntegrityCheck[16] = { 0 };
+
+ ntlm_compute_message_integrity_check(context, messageIntegrityCheck,
+ sizeof(messageIntegrityCheck));
+ CopyMemory(
+ &((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset],
+ message->MessageIntegrityCheck, sizeof(message->MessageIntegrityCheck));
+
+ if (memcmp(messageIntegrityCheck, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck)) != 0)
+ {
+ WLog_ERR(TAG, "Message Integrity Check (MIC) verification failed!");
+#ifdef WITH_DEBUG_NTLM
+ WLog_ERR(TAG, "Expected MIC:");
+ winpr_HexDump(TAG, WLOG_ERROR, messageIntegrityCheck, sizeof(messageIntegrityCheck));
+ WLog_ERR(TAG, "Actual MIC:");
+ winpr_HexDump(TAG, WLOG_ERROR, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck));
+#endif
+ return SEC_E_MESSAGE_ALTERED;
+ }
+ }
+ else
+ {
+ /* no mic message was present
+
+ https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/f9e6fbc4-a953-4f24-b229-ccdcc213b9ec
+ the mic is optional, as not supported in Windows NT, Windows 2000, Windows XP, and
+ Windows Server 2003 and, as it seems, in the NTLMv2 implementation of Qt5.
+
+ now check the NtProofString, to detect if the entered client password matches the
+ expected password.
+ */
+
+#ifdef WITH_DEBUG_NTLM
+ WLog_VRB(TAG, "No MIC present, using NtProofString for verification.");
+#endif
+
+ if (memcmp(context->NTLMv2Response.Response, context->NtProofString, 16) != 0)
+ {
+ WLog_ERR(TAG, "NtProofString verification failed!");
+#ifdef WITH_DEBUG_NTLM
+ WLog_ERR(TAG, "Expected NtProofString:");
+ winpr_HexDump(TAG, WLOG_ERROR, context->NtProofString, sizeof(context->NtProofString));
+ WLog_ERR(TAG, "Actual NtProofString:");
+ winpr_HexDump(TAG, WLOG_ERROR, context->NTLMv2Response.Response,
+ sizeof(context->NTLMv2Response));
+#endif
+ return SEC_E_LOGON_DENIED;
+ }
+ }
+
+ /* Generate signing keys */
+ if (!ntlm_generate_client_signing_key(context))
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_generate_server_signing_key(context))
+ return SEC_E_INTERNAL_ERROR;
+ /* Generate sealing keys */
+ if (!ntlm_generate_client_sealing_key(context))
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_generate_server_sealing_key(context))
+ return SEC_E_INTERNAL_ERROR;
+ /* Initialize RC4 seal state */
+ if (!ntlm_init_rc4_seal_states(context))
+ return SEC_E_INTERNAL_ERROR;
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_authentication_complete(context);
+#endif
+ ntlm_change_state(context, NTLM_STATE_FINAL);
+ ntlm_free_message_fields_buffer(&(message->DomainName));
+ ntlm_free_message_fields_buffer(&(message->UserName));
+ ntlm_free_message_fields_buffer(&(message->Workstation));
+ ntlm_free_message_fields_buffer(&(message->LmChallengeResponse));
+ ntlm_free_message_fields_buffer(&(message->NtChallengeResponse));
+ ntlm_free_message_fields_buffer(&(message->EncryptedRandomSessionKey));
+ return SEC_E_OK;
+
+fail:
+ return status;
+}
+
+/**
+ * Send NTLMSSP AUTHENTICATE_MESSAGE. msdn{cc236643}
+ *
+ * @param context Pointer to the NTLM context
+ * @param buffer The buffer to write
+ */
+
+SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer)
+{
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ UINT32 PayloadBufferOffset = 0;
+ const NTLM_AUTHENTICATE_MESSAGE empty = { 0 };
+ NTLM_AUTHENTICATE_MESSAGE* message = NULL;
+ SSPI_CREDENTIALS* credentials = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(buffer);
+
+ credentials = context->credentials;
+ WINPR_ASSERT(credentials);
+
+ message = &context->AUTHENTICATE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ if (context->NTLMv2)
+ {
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
+
+ if (context->SendVersionInfo)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
+ }
+
+ if (context->UseMIC)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
+
+ if (context->SendWorkstationName)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
+
+ if (context->confidentiality)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
+
+ if (context->CHALLENGE_MESSAGE.NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
+
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
+ message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ ntlm_get_version_info(&(message->Version));
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ {
+ message->Workstation.Len = context->Workstation.Length;
+ message->Workstation.Buffer = (BYTE*)context->Workstation.Buffer;
+ }
+
+ if (credentials->identity.DomainLength > 0)
+ {
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
+ message->DomainName.Len = (UINT16)credentials->identity.DomainLength * 2;
+ message->DomainName.Buffer = (BYTE*)credentials->identity.Domain;
+ }
+
+ message->UserName.Len = (UINT16)credentials->identity.UserLength * 2;
+ message->UserName.Buffer = (BYTE*)credentials->identity.User;
+ message->LmChallengeResponse.Len = (UINT16)context->LmChallengeResponse.cbBuffer;
+ message->LmChallengeResponse.Buffer = (BYTE*)context->LmChallengeResponse.pvBuffer;
+ message->NtChallengeResponse.Len = (UINT16)context->NtChallengeResponse.cbBuffer;
+ message->NtChallengeResponse.Buffer = (BYTE*)context->NtChallengeResponse.pvBuffer;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ {
+ message->EncryptedRandomSessionKey.Len = 16;
+ message->EncryptedRandomSessionKey.Buffer = context->EncryptedRandomSessionKey;
+ }
+
+ PayloadBufferOffset = 64;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ PayloadBufferOffset += 8; /* Version (8 bytes) */
+
+ if (context->UseMIC)
+ PayloadBufferOffset += 16; /* Message Integrity Check (16 bytes) */
+
+ message->DomainName.BufferOffset = PayloadBufferOffset;
+ message->UserName.BufferOffset = message->DomainName.BufferOffset + message->DomainName.Len;
+ message->Workstation.BufferOffset = message->UserName.BufferOffset + message->UserName.Len;
+ message->LmChallengeResponse.BufferOffset =
+ message->Workstation.BufferOffset + message->Workstation.Len;
+ message->NtChallengeResponse.BufferOffset =
+ message->LmChallengeResponse.BufferOffset + message->LmChallengeResponse.Len;
+ message->EncryptedRandomSessionKey.BufferOffset =
+ message->NtChallengeResponse.BufferOffset + message->NtChallengeResponse.Len;
+ if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_AUTHENTICATE))
+ return SEC_E_INVALID_TOKEN;
+ if (!ntlm_write_message_header(s, &message->header)) /* Message Header (12 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(
+ s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(
+ s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(
+ s,
+ &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_AUTHENTICATE_MESSAGE"))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (context->UseMIC)
+ {
+ const BYTE data[WINPR_MD5_DIGEST_LENGTH] = { 0 };
+
+ context->MessageIntegrityCheckOffset = Stream_GetPosition(s);
+ if (!ntlm_write_message_integrity_check(s, Stream_GetPosition(s), data, sizeof(data),
+ "NTLM_AUTHENTICATE_MESSAGE"))
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
+ {
+ if (!ntlm_write_message_fields_buffer(s, &(message->DomainName))) /* DomainName */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (!ntlm_write_message_fields_buffer(s, &(message->UserName))) /* UserName */
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ {
+ if (!ntlm_write_message_fields_buffer(s, &(message->Workstation))) /* Workstation */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
+ {
+ if (!ntlm_write_message_fields_buffer(
+ s, &(message->LmChallengeResponse))) /* LmChallengeResponse */
+ return SEC_E_INTERNAL_ERROR;
+ }
+ if (!ntlm_write_message_fields_buffer(
+ s, &(message->NtChallengeResponse))) /* NtChallengeResponse */
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ {
+ if (!ntlm_write_message_fields_buffer(
+ s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ length = Stream_GetPosition(s);
+ WINPR_ASSERT(length <= ULONG_MAX);
+
+ if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length))
+ return SEC_E_INTERNAL_ERROR;
+
+ CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
+ buffer->cbBuffer = (ULONG)length;
+
+ if (context->UseMIC)
+ {
+ /* Message Integrity Check */
+ ntlm_compute_message_integrity_check(context, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck));
+ if (!ntlm_write_message_integrity_check(
+ s, context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_authenticate_message(&context->AuthenticateMessage, message,
+ context->UseMIC ? MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK : 0,
+ &context->AuthenticateTargetInfo);
+#endif
+ ntlm_change_state(context, NTLM_STATE_FINAL);
+ return SEC_E_OK;
+}