FreeRDP/winpr/libwinpr/sspi/NTLM/ntlm_message.c

1253 lines
41 KiB
C
Raw Normal View History

/**
* WinPR: Windows Portable Runtime
* NTLM Security Package (Message)
*
2014-06-06 06:10:08 +04:00
* 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.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "ntlm.h"
#include "../sspi.h"
#include <winpr/crt.h>
#include <winpr/print.h>
#include <winpr/stream.h>
#include <winpr/sysinfo.h>
#include "ntlm_compute.h"
#include "ntlm_message.h"
2014-08-18 19:22:22 +04:00
#include "../log.h"
#define TAG WINPR_TAG("sspi.NTLM")
2014-08-18 19:22:22 +04:00
static const char NTLM_SIGNATURE[8] = { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' };
#ifdef WITH_DEBUG_NTLM
2019-11-06 17:24:51 +03:00
static const char* const NTLM_NEGOTIATE_STRINGS[] = { "NTLMSSP_NEGOTIATE_56",
"NTLMSSP_NEGOTIATE_KEY_EXCH",
"NTLMSSP_NEGOTIATE_128",
"NTLMSSP_RESERVED1",
"NTLMSSP_RESERVED2",
"NTLMSSP_RESERVED3",
"NTLMSSP_NEGOTIATE_VERSION",
"NTLMSSP_RESERVED4",
"NTLMSSP_NEGOTIATE_TARGET_INFO",
"NTLMSSP_REQUEST_NON_NT_SESSION_KEY",
"NTLMSSP_RESERVED5",
"NTLMSSP_NEGOTIATE_IDENTIFY",
"NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY",
"NTLMSSP_RESERVED6",
"NTLMSSP_TARGET_TYPE_SERVER",
"NTLMSSP_TARGET_TYPE_DOMAIN",
"NTLMSSP_NEGOTIATE_ALWAYS_SIGN",
"NTLMSSP_RESERVED7",
"NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED",
"NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED",
"NTLMSSP_NEGOTIATE_ANONYMOUS",
"NTLMSSP_RESERVED8",
"NTLMSSP_NEGOTIATE_NTLM",
"NTLMSSP_RESERVED9",
"NTLMSSP_NEGOTIATE_LM_KEY",
"NTLMSSP_NEGOTIATE_DATAGRAM",
"NTLMSSP_NEGOTIATE_SEAL",
"NTLMSSP_NEGOTIATE_SIGN",
"NTLMSSP_RESERVED10",
"NTLMSSP_REQUEST_TARGET",
"NTLMSSP_NEGOTIATE_OEM",
"NTLMSSP_NEGOTIATE_UNICODE" };
static void ntlm_print_negotiate_flags(UINT32 flags)
{
int i;
2014-08-18 21:34:47 +04:00
const char* str;
2019-11-06 17:24:51 +03:00
WLog_INFO(TAG, "negotiateFlags \"0x%08" PRIX32 "\"", flags);
for (i = 31; i >= 0; i--)
{
if ((flags >> i) & 1)
{
str = NTLM_NEGOTIATE_STRINGS[(31 - i)];
2014-08-18 19:22:22 +04:00
WLog_INFO(TAG, "\t%s (%d),", str, (31 - i));
}
}
}
#endif
static int ntlm_read_message_header(wStream* s, NTLM_MESSAGE_HEADER* header)
{
if (Stream_GetRemainingLength(s) < 12)
return -1;
2014-06-07 18:50:51 +04:00
Stream_Read(s, header->Signature, 8);
Stream_Read_UINT32(s, header->MessageType);
2019-11-06 17:24:51 +03:00
if (strncmp((char*)header->Signature, NTLM_SIGNATURE, 8) != 0)
2014-06-07 18:50:51 +04:00
return -1;
return 1;
}
static void ntlm_write_message_header(wStream* s, NTLM_MESSAGE_HEADER* header)
{
Stream_Write(s, header->Signature, sizeof(NTLM_SIGNATURE));
Stream_Write_UINT32(s, header->MessageType);
}
static void ntlm_populate_message_header(NTLM_MESSAGE_HEADER* header, UINT32 MessageType)
{
CopyMemory(header->Signature, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
header->MessageType = MessageType;
}
static int ntlm_read_message_fields(wStream* s, NTLM_MESSAGE_FIELDS* fields)
{
if (Stream_GetRemainingLength(s) < 8)
return -1;
2019-11-06 17:24:51 +03:00
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 1;
}
static void ntlm_write_message_fields(wStream* s, NTLM_MESSAGE_FIELDS* fields)
{
if (fields->MaxLen < 1)
fields->MaxLen = fields->Len;
2019-11-06 17:24:51 +03:00
Stream_Write_UINT16(s, fields->Len); /* Len (2 bytes) */
Stream_Write_UINT16(s, fields->MaxLen); /* MaxLen (2 bytes) */
Stream_Write_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
}
static int ntlm_read_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields)
{
if (fields->Len > 0)
{
const UINT32 offset = fields->BufferOffset + fields->Len;
if (fields->BufferOffset > UINT32_MAX - fields->Len)
return -1;
if (offset > Stream_Length(s))
2014-06-07 18:50:51 +04:00
return -1;
2019-11-06 17:24:51 +03:00
fields->Buffer = (PBYTE)malloc(fields->Len);
if (!fields->Buffer)
return -1;
Stream_SetPosition(s, fields->BufferOffset);
Stream_Read(s, fields->Buffer, fields->Len);
}
return 1;
}
static void ntlm_write_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields)
{
if (fields->Len > 0)
{
Stream_SetPosition(s, fields->BufferOffset);
Stream_Write(s, fields->Buffer, fields->Len);
}
}
static void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields)
2012-08-23 09:18:47 +04:00
{
if (fields)
2012-08-23 09:18:47 +04:00
{
if (fields->Buffer)
2012-08-23 09:18:47 +04:00
{
free(fields->Buffer);
fields->Len = 0;
fields->MaxLen = 0;
fields->Buffer = NULL;
fields->BufferOffset = 0;
}
}
}
#ifdef WITH_DEBUG_NTLM
static void ntlm_print_message_fields(NTLM_MESSAGE_FIELDS* fields, const char* name)
{
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "%s (Len: %" PRIu16 " MaxLen: %" PRIu16 " BufferOffset: %" PRIu32 ")", name,
fields->Len, fields->MaxLen, fields->BufferOffset);
if (fields->Len > 0)
2014-08-18 19:22:22 +04:00
winpr_HexDump(TAG, WLOG_DEBUG, fields->Buffer, fields->Len);
}
#endif
2014-08-18 21:34:47 +04:00
SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
{
2014-08-18 21:34:47 +04:00
wStream* s;
size_t length;
2014-08-18 21:34:47 +04:00
NTLM_NEGOTIATE_MESSAGE* message;
2013-01-31 05:39:57 +04:00
message = &context->NEGOTIATE_MESSAGE;
ZeroMemory(message, sizeof(NTLM_NEGOTIATE_MESSAGE));
2019-11-06 17:24:51 +03:00
s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer);
if (!s)
return SEC_E_INTERNAL_ERROR;
2019-11-06 17:24:51 +03:00
if (ntlm_read_message_header(s, (NTLM_MESSAGE_HEADER*)message) < 0)
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2014-06-07 18:50:51 +04:00
if (message->MessageType != MESSAGE_TYPE_NEGOTIATE)
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2013-01-31 05:39:57 +04:00
Stream_Read_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */
2013-01-31 05:39:57 +04:00
if (!((message->NegotiateFlags & NTLMSSP_REQUEST_TARGET) &&
(message->NegotiateFlags & NTLMSSP_NEGOTIATE_NTLM) &&
(message->NegotiateFlags & NTLMSSP_NEGOTIATE_UNICODE)))
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2013-01-31 05:39:57 +04:00
context->NegotiateFlags = message->NegotiateFlags;
/* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
if (ntlm_read_message_fields(s, &(message->DomainName)) < 0) /* DomainNameFields (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
/* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */
if (ntlm_read_message_fields(s, &(message->Workstation)) < 0) /* WorkstationFields (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
{
if (ntlm_read_version_info(s, &(message->Version)) < 0) /* Version (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
}
length = Stream_GetPosition(s);
buffer->cbBuffer = length;
if (!sspi_SecBufferAlloc(&context->NegotiateMessage, length))
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer);
context->NegotiateMessage.BufferType = buffer->BufferType;
#ifdef WITH_DEBUG_NTLM
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "NEGOTIATE_MESSAGE (length = %" PRIu32 ")", context->NegotiateMessage.cbBuffer);
winpr_HexDump(TAG, WLOG_DEBUG, context->NegotiateMessage.pvBuffer,
context->NegotiateMessage.cbBuffer);
2013-01-31 05:39:57 +04:00
ntlm_print_negotiate_flags(message->NegotiateFlags);
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_print_version_info(&(message->Version));
2014-08-18 19:22:22 +04:00
#endif
context->state = NTLM_STATE_CHALLENGE;
Stream_Free(s, FALSE);
return SEC_I_CONTINUE_NEEDED;
}
2014-08-18 21:34:47 +04:00
SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
{
2014-08-18 21:34:47 +04:00
wStream* s;
size_t length;
2014-08-18 21:34:47 +04:00
NTLM_NEGOTIATE_MESSAGE* message;
2013-01-31 05:39:57 +04:00
message = &context->NEGOTIATE_MESSAGE;
ZeroMemory(message, sizeof(NTLM_NEGOTIATE_MESSAGE));
2019-11-06 17:24:51 +03:00
s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer);
if (!s)
return SEC_E_INTERNAL_ERROR;
2019-11-06 17:24:51 +03:00
ntlm_populate_message_header((NTLM_MESSAGE_HEADER*)message, MESSAGE_TYPE_NEGOTIATE);
if (context->NTLMv2)
{
2013-01-31 05:39:57 +04:00
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_LM_KEY;
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM;
}
2013-01-31 05:39:57 +04:00
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)
2013-01-31 05:39:57 +04:00
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
if (context->SendVersionInfo)
2013-01-31 05:39:57 +04:00
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_get_version_info(&(message->Version));
2012-07-29 04:29:02 +04:00
2013-01-31 05:39:57 +04:00
context->NegotiateFlags = message->NegotiateFlags;
/* Message Header (12 bytes) */
2019-11-06 17:24:51 +03:00
ntlm_write_message_header(s, (NTLM_MESSAGE_HEADER*)message);
2013-01-31 05:39:57 +04:00
Stream_Write_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */
/* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
/* DomainNameFields (8 bytes) */
2013-01-31 05:39:57 +04:00
ntlm_write_message_fields(s, &(message->DomainName));
/* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */
/* WorkstationFields (8 bytes) */
2013-01-31 05:39:57 +04:00
ntlm_write_message_fields(s, &(message->Workstation));
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_write_version_info(s, &(message->Version));
length = Stream_GetPosition(s);
buffer->cbBuffer = length;
if (!sspi_SecBufferAlloc(&context->NegotiateMessage, length))
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer);
context->NegotiateMessage.BufferType = buffer->BufferType;
#ifdef WITH_DEBUG_NTLM
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "NEGOTIATE_MESSAGE (length = %d)", length);
winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(s), length);
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_print_version_info(&(message->Version));
2014-08-18 19:22:22 +04:00
#endif
context->state = NTLM_STATE_CHALLENGE;
Stream_Free(s, FALSE);
return SEC_I_CONTINUE_NEEDED;
}
2014-08-18 21:34:47 +04:00
SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
{
2014-08-18 21:34:47 +04:00
wStream* s;
int length;
PBYTE StartOffset;
PBYTE PayloadOffset;
2014-08-18 21:34:47 +04:00
NTLM_AV_PAIR* AvTimestamp;
NTLM_CHALLENGE_MESSAGE* message;
ntlm_generate_client_challenge(context);
2013-01-31 05:39:57 +04:00
message = &context->CHALLENGE_MESSAGE;
ZeroMemory(message, sizeof(NTLM_CHALLENGE_MESSAGE));
2019-11-06 17:24:51 +03:00
s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer);
if (!s)
return SEC_E_INTERNAL_ERROR;
StartOffset = Stream_Pointer(s);
2019-11-06 17:24:51 +03:00
if (ntlm_read_message_header(s, (NTLM_MESSAGE_HEADER*)message) < 0)
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2014-06-07 18:50:51 +04:00
if (message->MessageType != MESSAGE_TYPE_CHALLENGE)
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
if (ntlm_read_message_fields(s, &(message->TargetName)) < 0) /* TargetNameFields (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
if (Stream_GetRemainingLength(s) < 4)
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2013-01-31 05:39:57 +04:00
Stream_Read_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */
context->NegotiateFlags = message->NegotiateFlags;
if (Stream_GetRemainingLength(s) < 8)
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2013-01-31 05:39:57 +04:00
Stream_Read(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
CopyMemory(context->ServerChallenge, message->ServerChallenge, 8);
if (Stream_GetRemainingLength(s) < 8)
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2013-01-31 05:39:57 +04:00
Stream_Read(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */
if (ntlm_read_message_fields(s, &(message->TargetInfo)) < 0) /* TargetInfoFields (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
{
if (ntlm_read_version_info(s, &(message->Version)) < 0) /* Version (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
}
/* Payload (variable) */
PayloadOffset = Stream_Pointer(s);
2013-01-31 05:39:57 +04:00
if (message->TargetName.Len > 0)
{
if (ntlm_read_message_fields_buffer(s, &(message->TargetName)) < 0)
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
}
2013-01-31 05:39:57 +04:00
if (message->TargetInfo.Len > 0)
{
size_t cbAvTimestamp;
if (ntlm_read_message_fields_buffer(s, &(message->TargetInfo)) < 0)
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
2013-01-31 05:39:57 +04:00
context->ChallengeTargetInfo.pvBuffer = message->TargetInfo.Buffer;
context->ChallengeTargetInfo.cbBuffer = message->TargetInfo.Len;
2019-04-05 10:22:50 +03:00
AvTimestamp = ntlm_av_pair_get((NTLM_AV_PAIR*)message->TargetInfo.Buffer,
2019-11-06 17:24:51 +03:00
message->TargetInfo.Len, MsvAvTimestamp, &cbAvTimestamp);
if (AvTimestamp)
{
Fix NTLM AvPair lists There were two main issues here: First, the `ntlm_av_pair_add` and `ntlm_av_pair_add_copy` were not adding a new `MsvAvEOL` to the end of the list to replace the one they overwrote. This caused the second call to one of those functions to fail (since it couldn't find the terminator), which was the source of the test failure. It also caused `ntlm_av_pair_list_length` and `ntlm_print_av_pair_list` to read out of bounds until they happened to find the right word. Second, several bounds checks were wrong or missing. For example, `ntlm_av_pair_add` does not ensure that the value fits inside the list. And `ntlm_av_pair_get_len` and `ntlm_av_pair_get_value_pointer` can return error codes or NULL, but those error returns were ignored, and the values used anyway (such as in `ntlm_av_pair_add_copy`). This fixes the list handling code to have the invariant that all functions returning `NTLM_AV_PAIR*` only return non-`NULL` if the entire returned `AvPair` is within bounds. This removes the need for the length parameter in functions that only operate on a single `AvPair`. This check is performed by the new `ntlm_av_pair_check` helper, which is added in some new places and used to simplify the code in others. Other issues fixed along the way include: - `ntlm_av_pair_list_length` did not cast to `PBYTE`, so it was returning the number of `NTLM_AV_PAIR`-sized chunks (which was possibly not even an integer) instead of the number of bytes - I removed an impossible check for `offset <= 0` in `ntlm_av_pair_get_next_pointer` - The assertion that `Value != NULL` and the call to `CopyMemory` are only necessary if `AvLen` is nonzero - `ntlm_av_pair_get_next_pointer` (renamed to `ntlm_av_pair_next`) could be declared `static` With this commit, TestNTLM now passes on powerpc64. ``` $ ./Testing/TestSspi TestNTLM NTLM_NEGOTIATE (length = 40): NTLM_CHALLENGE (length = 168): NTLM_AUTHENTICATE (length = 352): $ echo $? 0 ``` Fixes #5250
2019-03-18 04:20:10 +03:00
PBYTE ptr = ntlm_av_pair_get_value_pointer(AvTimestamp);
if (!ptr)
return SEC_E_INTERNAL_ERROR;
if (context->NTLMv2)
context->UseMIC = TRUE;
CopyMemory(context->ChallengeTimestamp, ptr, 8);
}
}
2013-01-31 05:39:57 +04:00
length = (PayloadOffset - StartOffset) + message->TargetName.Len + message->TargetInfo.Len;
if (!sspi_SecBufferAlloc(&context->ChallengeMessage, length))
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
CopyMemory(context->ChallengeMessage.pvBuffer, StartOffset, length);
#ifdef WITH_DEBUG_NTLM
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "CHALLENGE_MESSAGE (length = %d)", length);
winpr_HexDump(TAG, WLOG_DEBUG, context->ChallengeMessage.pvBuffer,
context->ChallengeMessage.cbBuffer);
ntlm_print_negotiate_flags(context->NegotiateFlags);
if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
2013-01-31 05:39:57 +04:00
ntlm_print_version_info(&(message->Version));
2013-01-31 05:39:57 +04:00
ntlm_print_message_fields(&(message->TargetName), "TargetName");
ntlm_print_message_fields(&(message->TargetInfo), "TargetInfo");
if (context->ChallengeTargetInfo.cbBuffer > 0)
{
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "ChallengeTargetInfo (%" PRIu32 "):", context->ChallengeTargetInfo.cbBuffer);
ntlm_print_av_pair_list(context->ChallengeTargetInfo.pvBuffer,
context->ChallengeTargetInfo.cbBuffer);
}
2014-08-18 19:22:22 +04:00
#endif
/* AV_PAIRs */
if (context->NTLMv2)
{
if (ntlm_construct_authenticate_target_info(context) < 0)
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
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) < 0) /* LmChallengeResponse */
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
if (ntlm_compute_ntlm_v2_response(context) < 0) /* NtChallengeResponse */
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
2019-11-06 17:24:51 +03:00
ntlm_generate_key_exchange_key(context); /* KeyExchangeKey */
ntlm_generate_random_session_key(context); /* RandomSessionKey */
ntlm_generate_exported_session_key(context); /* ExportedSessionKey */
2019-11-06 17:24:51 +03:00
ntlm_encrypt_random_session_key(context); /* EncryptedRandomSessionKey */
/* Generate signing keys */
ntlm_generate_client_signing_key(context);
ntlm_generate_server_signing_key(context);
/* Generate sealing keys */
ntlm_generate_client_sealing_key(context);
ntlm_generate_server_sealing_key(context);
/* Initialize RC4 seal state using client sealing key */
ntlm_init_rc4_seal_states(context);
#ifdef WITH_DEBUG_NTLM
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ClientChallenge");
winpr_HexDump(TAG, WLOG_DEBUG, context->ClientChallenge, 8);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ServerChallenge");
winpr_HexDump(TAG, WLOG_DEBUG, context->ServerChallenge, 8);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "SessionBaseKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->SessionBaseKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "KeyExchangeKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->KeyExchangeKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ExportedSessionKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->ExportedSessionKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "RandomSessionKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->RandomSessionKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ClientSigningKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->ClientSigningKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ClientSealingKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->ClientSealingKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ServerSigningKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->ServerSigningKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ServerSealingKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->ServerSealingKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "Timestamp");
winpr_HexDump(TAG, WLOG_DEBUG, context->Timestamp, 8);
#endif
context->state = NTLM_STATE_AUTHENTICATE;
2013-01-31 05:39:57 +04:00
ntlm_free_message_fields_buffer(&(message->TargetName));
Stream_Free(s, FALSE);
return SEC_I_CONTINUE_NEEDED;
}
2014-08-18 21:34:47 +04:00
SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
{
2014-08-18 21:34:47 +04:00
wStream* s;
size_t length;
UINT32 PayloadOffset;
2014-08-18 21:34:47 +04:00
NTLM_CHALLENGE_MESSAGE* message;
2013-01-31 05:39:57 +04:00
message = &context->CHALLENGE_MESSAGE;
ZeroMemory(message, sizeof(NTLM_CHALLENGE_MESSAGE));
2019-11-06 17:24:51 +03:00
s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer);
if (!s)
return SEC_E_INTERNAL_ERROR;
ntlm_get_version_info(&(message->Version)); /* Version */
2019-11-06 17:24:51 +03:00
ntlm_generate_server_challenge(context); /* Server Challenge */
ntlm_generate_timestamp(context); /* Timestamp */
if (ntlm_construct_challenge_target_info(context) < 0) /* TargetInfo */
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
CopyMemory(message->ServerChallenge, context->ServerChallenge, 8); /* ServerChallenge */
2013-01-31 05:39:57 +04:00
message->NegotiateFlags = context->NegotiateFlags;
2019-11-06 17:24:51 +03:00
ntlm_populate_message_header((NTLM_MESSAGE_HEADER*)message, MESSAGE_TYPE_CHALLENGE);
/* Message Header (12 bytes) */
2019-11-06 17:24:51 +03:00
ntlm_write_message_header(s, (NTLM_MESSAGE_HEADER*)message);
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
{
2019-11-06 17:24:51 +03:00
message->TargetName.Len = (UINT16)context->TargetName.cbBuffer;
message->TargetName.Buffer = (PBYTE)context->TargetName.pvBuffer;
}
2013-01-31 05:39:57 +04:00
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
{
2019-11-06 17:24:51 +03:00
message->TargetInfo.Len = (UINT16)context->ChallengeTargetInfo.cbBuffer;
message->TargetInfo.Buffer = (PBYTE)context->ChallengeTargetInfo.pvBuffer;
}
PayloadOffset = 48;
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
PayloadOffset += 8;
2013-01-31 05:39:57 +04:00
message->TargetName.BufferOffset = PayloadOffset;
message->TargetInfo.BufferOffset = message->TargetName.BufferOffset + message->TargetName.Len;
/* TargetNameFields (8 bytes) */
2013-01-31 05:39:57 +04:00
ntlm_write_message_fields(s, &(message->TargetName));
Stream_Write_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */
2019-11-06 17:24:51 +03:00
Stream_Write(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
Stream_Write(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */
/* TargetInfoFields (8 bytes) */
2013-01-31 05:39:57 +04:00
ntlm_write_message_fields(s, &(message->TargetInfo));
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_write_version_info(s, &(message->Version)); /* Version (8 bytes) */
/* Payload (variable) */
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
ntlm_write_message_fields_buffer(s, &(message->TargetName));
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
ntlm_write_message_fields_buffer(s, &(message->TargetInfo));
length = Stream_GetPosition(s);
buffer->cbBuffer = length;
if (!sspi_SecBufferAlloc(&context->ChallengeMessage, length))
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s), length);
#ifdef WITH_DEBUG_NTLM
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "CHALLENGE_MESSAGE (length = %d)", length);
winpr_HexDump(TAG, WLOG_DEBUG, context->ChallengeMessage.pvBuffer,
context->ChallengeMessage.cbBuffer);
2013-01-31 05:39:57 +04:00
ntlm_print_negotiate_flags(message->NegotiateFlags);
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_print_version_info(&(message->Version));
2013-01-31 05:39:57 +04:00
ntlm_print_message_fields(&(message->TargetName), "TargetName");
ntlm_print_message_fields(&(message->TargetInfo), "TargetInfo");
#endif
context->state = NTLM_STATE_AUTHENTICATE;
Stream_Free(s, FALSE);
return SEC_I_CONTINUE_NEEDED;
}
2014-08-18 21:34:47 +04:00
SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
{
2014-08-18 21:34:47 +04:00
wStream* s;
size_t length;
UINT32 flags;
2014-08-18 21:34:47 +04:00
NTLM_AV_PAIR* AvFlags;
UINT32 PayloadBufferOffset;
2014-08-18 21:34:47 +04:00
NTLM_AUTHENTICATE_MESSAGE* message;
SSPI_CREDENTIALS* credentials = context->credentials;
flags = 0;
AvFlags = NULL;
2013-01-31 05:39:57 +04:00
message = &context->AUTHENTICATE_MESSAGE;
ZeroMemory(message, sizeof(NTLM_AUTHENTICATE_MESSAGE));
2019-11-06 17:24:51 +03:00
s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer);
if (!s)
return SEC_E_INTERNAL_ERROR;
2019-11-06 17:24:51 +03:00
if (ntlm_read_message_header(s, (NTLM_MESSAGE_HEADER*)message) < 0)
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2014-06-07 18:50:51 +04:00
if (message->MessageType != MESSAGE_TYPE_AUTHENTICATE)
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2019-11-06 17:24:51 +03:00
if (ntlm_read_message_fields(s, &(message->LmChallengeResponse)) <
0) /* LmChallengeResponseFields (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2019-11-06 17:24:51 +03:00
if (ntlm_read_message_fields(s, &(message->NtChallengeResponse)) <
0) /* NtChallengeResponseFields (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
if (ntlm_read_message_fields(s, &(message->DomainName)) < 0) /* DomainNameFields (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
if (ntlm_read_message_fields(s, &(message->UserName)) < 0) /* UserNameFields (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
if (ntlm_read_message_fields(s, &(message->Workstation)) < 0) /* WorkstationFields (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2019-11-06 17:24:51 +03:00
if (ntlm_read_message_fields(s, &(message->EncryptedRandomSessionKey)) <
0) /* EncryptedRandomSessionKeyFields (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2013-01-31 05:39:57 +04:00
Stream_Read_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */
2019-11-06 17:24:51 +03:00
context->NegotiateKeyExchange =
(message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) ? TRUE : FALSE;
2014-06-10 22:16:02 +04:00
if ((context->NegotiateKeyExchange && !message->EncryptedRandomSessionKey.Len) ||
(!context->NegotiateKeyExchange && message->EncryptedRandomSessionKey.Len))
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2014-06-10 22:16:02 +04:00
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
{
if (ntlm_read_version_info(s, &(message->Version)) < 0) /* Version (8 bytes) */
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
}
PayloadBufferOffset = Stream_GetPosition(s);
if (ntlm_read_message_fields_buffer(s, &(message->DomainName)) < 0) /* DomainName */
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
if (ntlm_read_message_fields_buffer(s, &(message->UserName)) < 0) /* UserName */
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
if (ntlm_read_message_fields_buffer(s, &(message->Workstation)) < 0) /* Workstation */
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
2019-11-06 17:24:51 +03:00
if (ntlm_read_message_fields_buffer(s, &(message->LmChallengeResponse)) <
0) /* LmChallengeResponse */
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
2019-11-06 17:24:51 +03:00
if (ntlm_read_message_fields_buffer(s, &(message->NtChallengeResponse)) <
0) /* NtChallengeResponse */
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
2013-01-31 05:39:57 +04:00
if (message->NtChallengeResponse.Len > 0)
{
size_t cbAvFlags;
2019-11-06 17:24:51 +03:00
wStream* snt =
Stream_New(message->NtChallengeResponse.Buffer, message->NtChallengeResponse.Len);
if (!snt)
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
if (ntlm_read_ntlm_v2_response(snt, &(context->NTLMv2Response)) < 0)
{
Stream_Free(s, FALSE);
Stream_Free(snt, FALSE);
2014-06-07 18:50:51 +04:00
return SEC_E_INVALID_TOKEN;
}
Stream_Free(snt, FALSE);
2013-01-31 05:39:57 +04:00
context->NtChallengeResponse.pvBuffer = message->NtChallengeResponse.Buffer;
context->NtChallengeResponse.cbBuffer = message->NtChallengeResponse.Len;
2014-06-10 22:16:02 +04:00
sspi_SecBufferFree(&(context->ChallengeTargetInfo));
2019-11-06 17:24:51 +03:00
context->ChallengeTargetInfo.pvBuffer = (void*)context->NTLMv2Response.Challenge.AvPairs;
2013-01-31 05:39:57 +04:00
context->ChallengeTargetInfo.cbBuffer = message->NtChallengeResponse.Len - (28 + 16);
CopyMemory(context->ClientChallenge, context->NTLMv2Response.Challenge.ClientChallenge, 8);
2019-11-06 17:24:51 +03:00
AvFlags =
ntlm_av_pair_get(context->NTLMv2Response.Challenge.AvPairs,
context->NTLMv2Response.Challenge.cbAvPairs, MsvAvFlags, &cbAvFlags);
if (AvFlags)
Fix NTLM AvPair lists There were two main issues here: First, the `ntlm_av_pair_add` and `ntlm_av_pair_add_copy` were not adding a new `MsvAvEOL` to the end of the list to replace the one they overwrote. This caused the second call to one of those functions to fail (since it couldn't find the terminator), which was the source of the test failure. It also caused `ntlm_av_pair_list_length` and `ntlm_print_av_pair_list` to read out of bounds until they happened to find the right word. Second, several bounds checks were wrong or missing. For example, `ntlm_av_pair_add` does not ensure that the value fits inside the list. And `ntlm_av_pair_get_len` and `ntlm_av_pair_get_value_pointer` can return error codes or NULL, but those error returns were ignored, and the values used anyway (such as in `ntlm_av_pair_add_copy`). This fixes the list handling code to have the invariant that all functions returning `NTLM_AV_PAIR*` only return non-`NULL` if the entire returned `AvPair` is within bounds. This removes the need for the length parameter in functions that only operate on a single `AvPair`. This check is performed by the new `ntlm_av_pair_check` helper, which is added in some new places and used to simplify the code in others. Other issues fixed along the way include: - `ntlm_av_pair_list_length` did not cast to `PBYTE`, so it was returning the number of `NTLM_AV_PAIR`-sized chunks (which was possibly not even an integer) instead of the number of bytes - I removed an impossible check for `offset <= 0` in `ntlm_av_pair_get_next_pointer` - The assertion that `Value != NULL` and the call to `CopyMemory` are only necessary if `AvLen` is nonzero - `ntlm_av_pair_get_next_pointer` (renamed to `ntlm_av_pair_next`) could be declared `static` With this commit, TestNTLM now passes on powerpc64. ``` $ ./Testing/TestSspi TestNTLM NTLM_NEGOTIATE (length = 40): NTLM_CHALLENGE (length = 168): NTLM_AUTHENTICATE (length = 352): $ echo $? 0 ``` Fixes #5250
2019-03-18 04:20:10 +03:00
Data_Read_UINT32(ntlm_av_pair_get_value_pointer(AvFlags), flags);
}
2019-11-06 17:24:51 +03:00
if (ntlm_read_message_fields_buffer(s, &(message->EncryptedRandomSessionKey)) <
0) /* EncryptedRandomSessionKey */
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
if (message->EncryptedRandomSessionKey.Len > 0)
{
if (message->EncryptedRandomSessionKey.Len != 16)
{
Stream_Free(s, FALSE);
return SEC_E_INVALID_TOKEN;
}
2019-11-06 17:24:51 +03:00
CopyMemory(context->EncryptedRandomSessionKey, message->EncryptedRandomSessionKey.Buffer,
16);
}
length = Stream_GetPosition(s);
if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, length))
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
2014-08-18 19:22:22 +04:00
CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
buffer->cbBuffer = length;
Stream_SetPosition(s, PayloadBufferOffset);
if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
{
2019-11-06 17:24:51 +03:00
context->MessageIntegrityCheckOffset = (UINT32)Stream_GetPosition(s);
2014-06-07 18:50:51 +04:00
if (Stream_GetRemainingLength(s) < 16)
{
Stream_Free(s, FALSE);
2014-06-07 18:50:51 +04:00
return SEC_E_INVALID_TOKEN;
}
2014-06-07 18:50:51 +04:00
2013-01-31 05:39:57 +04:00
Stream_Read(s, message->MessageIntegrityCheck, 16);
}
#ifdef WITH_DEBUG_NTLM
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG, "AUTHENTICATE_MESSAGE (length = %" PRIu32 ")",
context->AuthenticateMessage.cbBuffer);
winpr_HexDump(TAG, WLOG_DEBUG, context->AuthenticateMessage.pvBuffer,
context->AuthenticateMessage.cbBuffer);
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_print_version_info(&(message->Version));
2013-01-31 05:39:57 +04:00
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");
ntlm_print_av_pair_list(context->NTLMv2Response.Challenge.AvPairs,
context->NTLMv2Response.Challenge.cbAvPairs);
if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
{
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "MessageIntegrityCheck:");
winpr_HexDump(TAG, WLOG_DEBUG, message->MessageIntegrityCheck, 16);
}
2014-08-18 19:22:22 +04:00
#endif
2013-01-31 05:39:57 +04:00
if (message->UserName.Len > 0)
{
2019-11-06 17:24:51 +03:00
credentials->identity.User = (UINT16*)malloc(message->UserName.Len);
if (!credentials->identity.User)
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
CopyMemory(credentials->identity.User, message->UserName.Buffer, message->UserName.Len);
credentials->identity.UserLength = message->UserName.Len / 2;
}
2012-06-04 03:59:35 +04:00
2013-01-31 05:39:57 +04:00
if (message->DomainName.Len > 0)
2012-06-04 03:59:35 +04:00
{
2019-11-06 17:24:51 +03:00
credentials->identity.Domain = (UINT16*)malloc(message->DomainName.Len);
if (!credentials->identity.Domain)
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
2019-11-06 17:24:51 +03:00
CopyMemory(credentials->identity.Domain, message->DomainName.Buffer,
message->DomainName.Len);
credentials->identity.DomainLength = message->DomainName.Len / 2;
}
Stream_Free(s, FALSE);
/* Computations beyond this point require the NTLM hash of the password */
context->state = NTLM_STATE_COMPLETION;
return SEC_I_COMPLETE_NEEDED;
}
/**
* Send NTLMSSP AUTHENTICATE_MESSAGE.\n
* AUTHENTICATE_MESSAGE @msdn{cc236643}
* @param NTLM context
* @param buffer
*/
2014-08-18 21:34:47 +04:00
SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
{
2014-08-18 21:34:47 +04:00
wStream* s;
size_t length;
UINT32 PayloadBufferOffset;
2014-08-18 21:34:47 +04:00
NTLM_AUTHENTICATE_MESSAGE* message;
SSPI_CREDENTIALS* credentials = context->credentials;
2013-01-31 05:39:57 +04:00
message = &context->AUTHENTICATE_MESSAGE;
ZeroMemory(message, sizeof(NTLM_AUTHENTICATE_MESSAGE));
2019-11-06 17:24:51 +03:00
s = Stream_New((BYTE*)buffer->pvBuffer, buffer->cbBuffer);
if (!s)
return SEC_E_INTERNAL_ERROR;
if (context->NTLMv2)
{
2013-01-31 05:39:57 +04:00
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
if (context->SendVersionInfo)
2013-01-31 05:39:57 +04:00
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
}
if (context->UseMIC)
2013-01-31 05:39:57 +04:00
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
if (context->SendWorkstationName)
2013-01-31 05:39:57 +04:00
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
if (context->confidentiality)
2013-01-31 05:39:57 +04:00
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
if (context->CHALLENGE_MESSAGE.NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
2013-01-31 05:39:57 +04:00
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;
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_get_version_info(&(message->Version));
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
{
2013-01-31 05:39:57 +04:00
message->Workstation.Len = context->Workstation.Length;
2019-11-06 17:24:51 +03:00
message->Workstation.Buffer = (BYTE*)context->Workstation.Buffer;
}
if (credentials->identity.DomainLength > 0)
{
2013-01-31 05:39:57 +04:00
message->NegotiateFlags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
2019-11-06 17:24:51 +03:00
message->DomainName.Len = (UINT16)credentials->identity.DomainLength * 2;
message->DomainName.Buffer = (BYTE*)credentials->identity.Domain;
}
2019-11-06 17:24:51 +03:00
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;
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
{
2013-01-31 05:39:57 +04:00
message->EncryptedRandomSessionKey.Len = 16;
message->EncryptedRandomSessionKey.Buffer = context->EncryptedRandomSessionKey;
}
PayloadBufferOffset = 64;
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
PayloadBufferOffset += 8; /* Version (8 bytes) */
if (context->UseMIC)
PayloadBufferOffset += 16; /* Message Integrity Check (16 bytes) */
2013-01-31 05:39:57 +04:00
message->DomainName.BufferOffset = PayloadBufferOffset;
message->UserName.BufferOffset = message->DomainName.BufferOffset + message->DomainName.Len;
message->Workstation.BufferOffset = message->UserName.BufferOffset + message->UserName.Len;
2019-11-06 17:24:51 +03:00
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;
ntlm_populate_message_header((NTLM_MESSAGE_HEADER*)message, MESSAGE_TYPE_AUTHENTICATE);
ntlm_write_message_header(s, (NTLM_MESSAGE_HEADER*)message); /* Message Header (12 bytes) */
ntlm_write_message_fields(
s, &(message->LmChallengeResponse)); /* LmChallengeResponseFields (8 bytes) */
ntlm_write_message_fields(
s, &(message->NtChallengeResponse)); /* NtChallengeResponseFields (8 bytes) */
ntlm_write_message_fields(s, &(message->DomainName)); /* DomainNameFields (8 bytes) */
ntlm_write_message_fields(s, &(message->UserName)); /* UserNameFields (8 bytes) */
2013-01-31 05:39:57 +04:00
ntlm_write_message_fields(s, &(message->Workstation)); /* WorkstationFields (8 bytes) */
2019-11-06 17:24:51 +03:00
ntlm_write_message_fields(
s, &(message->EncryptedRandomSessionKey)); /* EncryptedRandomSessionKeyFields (8 bytes) */
2013-01-31 05:39:57 +04:00
Stream_Write_UINT32(s, message->NegotiateFlags); /* NegotiateFlags (4 bytes) */
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_write_version_info(s, &(message->Version)); /* Version (8 bytes) */
if (context->UseMIC)
{
2019-11-06 17:24:51 +03:00
context->MessageIntegrityCheckOffset = (UINT32)Stream_GetPosition(s);
Stream_Zero(s, 16); /* Message Integrity Check (16 bytes) */
}
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
ntlm_write_message_fields_buffer(s, &(message->DomainName)); /* DomainName */
2013-01-31 05:39:57 +04:00
ntlm_write_message_fields_buffer(s, &(message->UserName)); /* UserName */
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
ntlm_write_message_fields_buffer(s, &(message->Workstation)); /* Workstation */
2013-01-31 05:39:57 +04:00
ntlm_write_message_fields_buffer(s, &(message->LmChallengeResponse)); /* LmChallengeResponse */
ntlm_write_message_fields_buffer(s, &(message->NtChallengeResponse)); /* NtChallengeResponse */
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
2019-11-06 17:24:51 +03:00
ntlm_write_message_fields_buffer(
s, &(message->EncryptedRandomSessionKey)); /* EncryptedRandomSessionKey */
length = Stream_GetPosition(s);
2014-08-18 19:22:22 +04:00
if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, length))
{
Stream_Free(s, FALSE);
return SEC_E_INTERNAL_ERROR;
}
CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
buffer->cbBuffer = length;
if (context->UseMIC)
{
/* Message Integrity Check */
ntlm_compute_message_integrity_check(context, message->MessageIntegrityCheck, 16);
Stream_SetPosition(s, context->MessageIntegrityCheckOffset);
Stream_Write(s, message->MessageIntegrityCheck, 16);
Stream_SetPosition(s, length);
}
#ifdef WITH_DEBUG_NTLM
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "AUTHENTICATE_MESSAGE (length = %d)", length);
winpr_HexDump(TAG, WLOG_DEBUG, Stream_Buffer(s), length);
2013-01-31 05:39:57 +04:00
ntlm_print_negotiate_flags(message->NegotiateFlags);
2013-01-31 05:39:57 +04:00
if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_print_version_info(&(message->Version));
if (context->AuthenticateTargetInfo.cbBuffer > 0)
{
2019-11-06 17:24:51 +03:00
WLog_DBG(TAG,
"AuthenticateTargetInfo (%" PRIu32 "):", context->AuthenticateTargetInfo.cbBuffer);
ntlm_print_av_pair_list(context->AuthenticateTargetInfo.pvBuffer,
context->AuthenticateTargetInfo.cbBuffer);
}
2013-01-31 05:39:57 +04:00
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 (context->UseMIC)
{
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "MessageIntegrityCheck (length = 16)");
winpr_HexDump(TAG, WLOG_DEBUG, message->MessageIntegrityCheck, 16);
}
2014-08-18 19:22:22 +04:00
#endif
context->state = NTLM_STATE_FINAL;
Stream_Free(s, FALSE);
return SEC_I_COMPLETE_NEEDED;
}
2014-08-18 21:34:47 +04:00
SECURITY_STATUS ntlm_server_AuthenticateComplete(NTLM_CONTEXT* context)
{
UINT32 flags = 0;
size_t cbAvFlags;
2014-08-18 21:34:47 +04:00
NTLM_AV_PAIR* AvFlags = NULL;
NTLM_AUTHENTICATE_MESSAGE* message;
BYTE messageIntegrityCheck[16];
if (!context)
return SEC_E_INVALID_PARAMETER;
if (context->state != NTLM_STATE_COMPLETION)
return SEC_E_OUT_OF_SEQUENCE;
message = &context->AUTHENTICATE_MESSAGE;
AvFlags = ntlm_av_pair_get(context->NTLMv2Response.Challenge.AvPairs,
2019-11-06 17:24:51 +03:00
context->NTLMv2Response.Challenge.cbAvPairs, MsvAvFlags, &cbAvFlags);
if (AvFlags)
Fix NTLM AvPair lists There were two main issues here: First, the `ntlm_av_pair_add` and `ntlm_av_pair_add_copy` were not adding a new `MsvAvEOL` to the end of the list to replace the one they overwrote. This caused the second call to one of those functions to fail (since it couldn't find the terminator), which was the source of the test failure. It also caused `ntlm_av_pair_list_length` and `ntlm_print_av_pair_list` to read out of bounds until they happened to find the right word. Second, several bounds checks were wrong or missing. For example, `ntlm_av_pair_add` does not ensure that the value fits inside the list. And `ntlm_av_pair_get_len` and `ntlm_av_pair_get_value_pointer` can return error codes or NULL, but those error returns were ignored, and the values used anyway (such as in `ntlm_av_pair_add_copy`). This fixes the list handling code to have the invariant that all functions returning `NTLM_AV_PAIR*` only return non-`NULL` if the entire returned `AvPair` is within bounds. This removes the need for the length parameter in functions that only operate on a single `AvPair`. This check is performed by the new `ntlm_av_pair_check` helper, which is added in some new places and used to simplify the code in others. Other issues fixed along the way include: - `ntlm_av_pair_list_length` did not cast to `PBYTE`, so it was returning the number of `NTLM_AV_PAIR`-sized chunks (which was possibly not even an integer) instead of the number of bytes - I removed an impossible check for `offset <= 0` in `ntlm_av_pair_get_next_pointer` - The assertion that `Value != NULL` and the call to `CopyMemory` are only necessary if `AvLen` is nonzero - `ntlm_av_pair_get_next_pointer` (renamed to `ntlm_av_pair_next`) could be declared `static` With this commit, TestNTLM now passes on powerpc64. ``` $ ./Testing/TestSspi TestNTLM NTLM_NEGOTIATE (length = 40): NTLM_CHALLENGE (length = 168): NTLM_AUTHENTICATE (length = 352): $ echo $? 0 ``` Fixes #5250
2019-03-18 04:20:10 +03:00
Data_Read_UINT32(ntlm_av_pair_get_value_pointer(AvFlags), flags);
if (ntlm_compute_lm_v2_response(context) < 0) /* LmChallengeResponse */
return SEC_E_INTERNAL_ERROR;
if (ntlm_compute_ntlm_v2_response(context) < 0) /* 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)
{
2019-11-06 17:24:51 +03:00
ZeroMemory(
&((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset],
16);
ntlm_compute_message_integrity_check(context, messageIntegrityCheck,
sizeof(messageIntegrityCheck));
CopyMemory(
&((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset],
message->MessageIntegrityCheck, 16);
if (memcmp(messageIntegrityCheck, message->MessageIntegrityCheck, 16) != 0)
{
2014-08-18 19:22:22 +04:00
WLog_ERR(TAG, "Message Integrity Check (MIC) verification failed!");
#ifdef WITH_DEBUG_NTLM
2014-08-18 19:22:22 +04:00
WLog_ERR(TAG, "Expected MIC:");
winpr_HexDump(TAG, WLOG_ERROR, messageIntegrityCheck, 16);
2014-08-18 19:22:22 +04:00
WLog_ERR(TAG, "Actual MIC:");
winpr_HexDump(TAG, WLOG_ERROR, message->MessageIntegrityCheck, 16);
#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_DBG(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, 16);
WLog_ERR(TAG, "Actual NtProofString:");
winpr_HexDump(TAG, WLOG_ERROR, context->NTLMv2Response.Response, 16);
#endif
return SEC_E_LOGON_DENIED;
}
}
/* Generate signing keys */
ntlm_generate_client_signing_key(context);
ntlm_generate_server_signing_key(context);
/* Generate sealing keys */
ntlm_generate_client_sealing_key(context);
ntlm_generate_server_sealing_key(context);
/* Initialize RC4 seal state */
ntlm_init_rc4_seal_states(context);
#ifdef WITH_DEBUG_NTLM
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ClientChallenge");
winpr_HexDump(TAG, WLOG_DEBUG, context->ClientChallenge, 8);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ServerChallenge");
winpr_HexDump(TAG, WLOG_DEBUG, context->ServerChallenge, 8);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "SessionBaseKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->SessionBaseKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "KeyExchangeKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->KeyExchangeKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ExportedSessionKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->ExportedSessionKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "RandomSessionKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->RandomSessionKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ClientSigningKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->ClientSigningKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ClientSealingKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->ClientSealingKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ServerSigningKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->ServerSigningKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "ServerSealingKey");
winpr_HexDump(TAG, WLOG_DEBUG, context->ServerSealingKey, 16);
2014-08-18 19:22:22 +04:00
WLog_DBG(TAG, "Timestamp");
winpr_HexDump(TAG, WLOG_DEBUG, context->Timestamp, 8);
#endif
context->state = 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;
}