libwinpr-sspi: add proper detection of MIC in server-side NLA

This commit is contained in:
Marc-André Moreau 2012-07-01 17:05:31 -04:00
parent 63a3fe70cb
commit bf559e37eb
7 changed files with 184 additions and 54 deletions

View File

@ -86,9 +86,9 @@ NTLM_CONTEXT* ntlm_ContextNew()
if (context != NULL)
{
context->ntlm_v2 = FALSE;
context->ntlm_v2 = TRUE;
context->NegotiateFlags = 0;
context->SendVersionInfo = 0;
context->SendVersionInfo = TRUE;
context->LmCompatibilityLevel = 3;
context->state = NTLM_STATE_INITIAL;
context->SuppressExtendedProtection = 1;

View File

@ -126,6 +126,10 @@ enum _AV_ID
};
typedef enum _AV_ID AV_ID;
#define MSV_AV_FLAGS_AUTHENTICATION_CONSTRAINED 0x00000001
#define MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK 0x00000002
#define MSV_AV_FLAGS_TARGET_SPN_UNTRUSTED_SOURCE 0x00000004
#define WINDOWS_MAJOR_VERSION_5 0x05
#define WINDOWS_MAJOR_VERSION_6 0x06
#define WINDOWS_MINOR_VERSION_0 0x00

View File

@ -28,7 +28,7 @@
#include "ntlm_av_pairs.h"
const char* const AV_PAIRS_STRINGS[] =
const char* const AV_PAIR_STRINGS[] =
{
"MsvAvEOL",
"MsvAvNbComputerName",
@ -51,6 +51,47 @@ void ntlm_av_pair_list_init(NTLM_AV_PAIR* pAvPairList)
pAvPair->AvLen = 0;
}
ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList)
{
ULONG length;
NTLM_AV_PAIR* pAvPair = pAvPairList;
if (!pAvPair)
return 0;
while (pAvPair->AvId != MsvAvEOL)
{
pAvPair = ntlm_av_pair_get_next_pointer(pAvPair);
}
length = (pAvPair - pAvPairList) + sizeof(NTLM_AV_PAIR);
return length;
}
void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList)
{
NTLM_AV_PAIR* pAvPair = pAvPairList;
if (!pAvPair)
return;
printf("AV_PAIRs =\n{\n");
while (pAvPair->AvId != MsvAvEOL)
{
printf("\t%s AvId: %d AvLen: %d\n",
AV_PAIR_STRINGS[pAvPair->AvId],
pAvPair->AvId, pAvPair->AvLen);
winpr_HexDump(ntlm_av_pair_get_value_pointer(pAvPair), pAvPair->AvLen);
pAvPair = ntlm_av_pair_get_next_pointer(pAvPair);
}
printf("}\n");
}
ULONG ntlm_av_pair_list_size(ULONG AvPairsCount, ULONG AvPairsValueLength)
{
/* size of headers + value lengths + terminating MsvAvEOL AV_PAIR */
@ -72,7 +113,7 @@ NTLM_AV_PAIR* ntlm_av_pair_get_next_pointer(NTLM_AV_PAIR* pAvPair)
return (NTLM_AV_PAIR*) ((PBYTE) pAvPair + ntlm_av_pair_get_next_offset(pAvPair));
}
NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, AV_ID AvId, LONG AvPairListSize)
NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, AV_ID AvId)
{
NTLM_AV_PAIR* pAvPair = pAvPairList;
@ -87,11 +128,6 @@ NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, AV_ID AvId, LONG AvPai
if (pAvPair->AvId == MsvAvEOL)
return NULL;
AvPairListSize -= ntlm_av_pair_get_next_offset(pAvPair);
if (AvPairListSize <= 0)
return NULL;
pAvPair = ntlm_av_pair_get_next_pointer(pAvPair);
}
@ -102,7 +138,7 @@ NTLM_AV_PAIR* ntlm_av_pair_add(NTLM_AV_PAIR* pAvPairList, AV_ID AvId, PUNICODE_S
{
NTLM_AV_PAIR* pAvPair;
pAvPair = ntlm_av_pair_get(pAvPairList, MsvAvEOL, AvPairListSize);
pAvPair = ntlm_av_pair_get(pAvPairList, MsvAvEOL);
if (!pAvPair)
return NULL;
@ -207,7 +243,7 @@ void ntlm_input_av_pairs(NTLM_CONTEXT* context, PStream s)
#ifdef WITH_DEBUG_NTLM
if (AvId < 10)
printf("\tAvId: %s, AvLen: %d\n", AV_PAIRS_STRINGS[AvId], AvLen);
printf("\tAvId: %s, AvLen: %d\n", AV_PAIR_STRINGS[AvId], AvLen);
else
printf("\tAvId: %s, AvLen: %d\n", "Unknown", AvLen);

View File

@ -25,11 +25,13 @@
#include <winpr/stream.h>
void ntlm_av_pair_list_init(NTLM_AV_PAIR* pAvPairList);
ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList);
void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList);
ULONG ntlm_av_pair_list_size(ULONG AvPairsCount, ULONG AvPairsValueLength);
PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair);
int ntlm_av_pair_get_next_offset(NTLM_AV_PAIR* pAvPair);
NTLM_AV_PAIR* ntlm_av_pair_get_next_pointer(NTLM_AV_PAIR* pAvPair);
NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, AV_ID AvId, LONG AvPairListSize);
NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, AV_ID AvId);
NTLM_AV_PAIR* ntlm_av_pair_add(NTLM_AV_PAIR* pAvPairList, AV_ID AvId, PUNICODE_STRING pValue, LONG AvPairListSize);
void ntlm_construct_server_target_info(NTLM_CONTEXT* context);

View File

@ -103,6 +103,51 @@ void ntlm_print_version_info(NTLM_VERSION_INFO* versionInfo)
printf("\tNTLMRevisionCurrent: 0x%02X\n", versionInfo->NTLMRevisionCurrent);
}
void ntlm_read_ntlm_v2_client_challenge(PStream s, NTLMv2_CLIENT_CHALLENGE* challenge)
{
size_t size;
StreamRead_UINT8(s, challenge->RespType);
StreamRead_UINT8(s, challenge->HiRespType);
StreamRead_UINT16(s, challenge->Reserved1);
StreamRead_UINT32(s, challenge->Reserved2);
StreamRead(s, challenge->Timestamp, 8);
StreamRead(s, challenge->ClientChallenge, 8);
StreamRead_UINT32(s, challenge->Reserved3);
size = StreamRemainingSize(s);
challenge->AvPairs = (NTLM_AV_PAIR*) malloc(size);
StreamRead(s, challenge->AvPairs, size);
}
void ntlm_write_ntlm_v2_client_challenge(PStream s, NTLMv2_CLIENT_CHALLENGE* challenge)
{
ULONG length;
StreamWrite_UINT8(s, challenge->RespType);
StreamWrite_UINT8(s, challenge->HiRespType);
StreamWrite_UINT16(s, challenge->Reserved1);
StreamWrite_UINT32(s, challenge->Reserved2);
StreamWrite(s, challenge->Timestamp, 8);
StreamWrite(s, challenge->ClientChallenge, 8);
StreamWrite_UINT32(s, challenge->Reserved3);
length = ntlm_av_pair_list_length(challenge->AvPairs);
StreamWrite(s, challenge->AvPairs, length);
}
void ntlm_read_ntlm_v2_response(PStream s, NTLMv2_RESPONSE* response)
{
StreamRead(s, response->Response, 16);
ntlm_read_ntlm_v2_client_challenge(s, &(response->Challenge));
}
void ntlm_write_ntlm_v2_response(PStream s, NTLMv2_RESPONSE* response)
{
StreamWrite(s, response->Response, 16);
ntlm_write_ntlm_v2_client_challenge(s, &(response->Challenge));
}
/**
* Output Restriction_Encoding.\n
* Restriction_Encoding @msdn{cc236647}

View File

@ -29,6 +29,9 @@ void ntlm_read_version_info(PStream s, NTLM_VERSION_INFO* versionInfo);
void ntlm_write_version_info(PStream s, NTLM_VERSION_INFO* versionInfo);
void ntlm_print_version_info(NTLM_VERSION_INFO* versionInfo);
void ntlm_read_ntlm_v2_response(PStream s, NTLMv2_RESPONSE* response);
void ntlm_write_ntlm_v2_response(PStream s, NTLMv2_RESPONSE* response);
void ntlm_output_restriction_encoding(NTLM_CONTEXT* context);
void ntlm_output_target_name(NTLM_CONTEXT* context);
void ntlm_output_channel_bindings(NTLM_CONTEXT* context);

View File

@ -209,10 +209,7 @@ SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buf
ntlm_read_message_fields(s, &(message.Workstation));
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
{
/* Only present if NTLMSSP_NEGOTIATE_VERSION is set */
ntlm_read_version_info(s, &(message.Version)); /* Version (8 bytes) */
}
length = StreamSize(s);
buffer->cbBuffer = length;
@ -225,6 +222,11 @@ SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buf
printf("NEGOTIATE_MESSAGE (length = %d)\n", length);
winpr_HexDump(s->data, length);
printf("\n");
ntlm_print_negotiate_flags(message.NegotiateFlags);
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_print_version_info(&(message.Version));
#endif
context->state = NTLM_STATE_CHALLENGE;
@ -245,9 +247,6 @@ SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer bu
ntlm_populate_message_header((NTLM_MESSAGE_HEADER*) &message, MESSAGE_TYPE_NEGOTIATE);
/* Message Header (12 bytes) */
ntlm_write_message_header(s, (NTLM_MESSAGE_HEADER*) &message);
if (context->ntlm_v2)
{
message.NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
@ -275,6 +274,9 @@ SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer bu
message.NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
}
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_get_version_info(&(message.Version));
if (context->confidentiality)
message.NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
@ -283,6 +285,9 @@ SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer bu
context->NegotiateFlags = message.NegotiateFlags;
/* Message Header (12 bytes) */
ntlm_write_message_header(s, (NTLM_MESSAGE_HEADER*) &message);
StreamWrite_UINT32(s, message.NegotiateFlags); /* NegotiateFlags (4 bytes) */
/* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
@ -296,19 +301,8 @@ SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer bu
ntlm_write_message_fields(s, &(message.Workstation));
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
{
/* Only present if NTLMSSP_NEGOTIATE_VERSION is set */
ntlm_get_version_info(&(message.Version));
ntlm_write_version_info(s, &(message.Version));
#ifdef WITH_DEBUG_NTLM
printf("Version (length = 8)\n");
winpr_HexDump((s->p - 8), 8);
printf("\n");
#endif
}
length = StreamSize(s);
buffer->cbBuffer = length;
@ -320,6 +314,9 @@ SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer bu
printf("NEGOTIATE_MESSAGE (length = %d)\n", length);
winpr_HexDump(s->data, length);
printf("\n");
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_print_version_info(&(message.Version));
#endif
context->state = NTLM_STATE_CHALLENGE;
@ -523,20 +520,22 @@ SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer bu
/* ServerChallenge */
CopyMemory(message.ServerChallenge, context->ServerChallenge, 8);
message.NegotiateFlags = context->NegotiateFlags;
ntlm_populate_message_header((NTLM_MESSAGE_HEADER*) &message, MESSAGE_TYPE_CHALLENGE);
/* Message Header (12 bytes) */
ntlm_write_message_header(s, (NTLM_MESSAGE_HEADER*) &message);
if (context->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
if (message.NegotiateFlags & NTLMSSP_REQUEST_TARGET)
{
message.TargetName.Len = (UINT16) context->TargetName.cbBuffer;
message.TargetName.Buffer = context->TargetName.pvBuffer;
}
context->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
message.NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
{
message.TargetInfo.Len = (UINT16) context->TargetInfo.cbBuffer;
message.TargetInfo.Buffer = context->TargetInfo.pvBuffer;
@ -544,7 +543,7 @@ SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer bu
PayloadOffset = 48;
if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
PayloadOffset += 8;
message.TargetName.BufferOffset = PayloadOffset;
@ -553,7 +552,7 @@ SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer bu
/* TargetNameFields (8 bytes) */
ntlm_write_message_fields(s, &(message.TargetName));
StreamWrite_UINT32(s, context->NegotiateFlags); /* NegotiateFlags (4 bytes) */
StreamWrite_UINT32(s, message.NegotiateFlags); /* NegotiateFlags (4 bytes) */
StreamWrite(s, message.ServerChallenge, 8); /* ServerChallenge (8 bytes) */
StreamWrite(s, message.Reserved, 8); /* Reserved (8 bytes), should be ignored */
@ -561,15 +560,15 @@ SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer bu
/* TargetInfoFields (8 bytes) */
ntlm_write_message_fields(s, &(message.TargetInfo));
if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_write_version_info(s, &(message.Version)); /* Version (8 bytes) */
/* Payload (variable) */
if (context->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
if (message.NegotiateFlags & NTLMSSP_REQUEST_TARGET)
ntlm_write_message_fields_buffer(s, &(message.TargetName));
if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
ntlm_write_message_fields_buffer(s, &(message.TargetInfo));
length = StreamSize(s);
@ -583,7 +582,9 @@ SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer bu
winpr_HexDump(context->ChallengeMessage.pvBuffer, context->ChallengeMessage.cbBuffer);
printf("\n");
if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
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");
@ -601,6 +602,9 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer
{
PStream s;
int length;
UINT32 flags = 0;
NTLMv2_RESPONSE response;
UINT32 PayloadBufferOffset;
NTLM_AUTHENTICATE_MESSAGE message;
ZeroMemory(&message, sizeof(message));
@ -641,10 +645,7 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
ntlm_read_version_info(s, &(message.Version)); /* Version (8 bytes) */
length = StreamSize(s);
sspi_SecBufferAlloc(&context->AuthenticateMessage, length);
CopyMemory(context->AuthenticateMessage.pvBuffer, s->data, length);
buffer->cbBuffer = length;
PayloadBufferOffset = StreamGetOffset(s);
/* DomainName */
ntlm_read_message_fields_buffer(s, &(message.DomainName));
@ -663,15 +664,40 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer
if (message.NtChallengeResponse.Len > 0)
{
BYTE* ClientChallengeBuffer;
ClientChallengeBuffer = message.NtChallengeResponse.Buffer + 32;
CopyMemory(context->ClientChallenge, ClientChallengeBuffer, 8);
NTLM_AV_PAIR* AvFlags;
PStream s = PStreamAllocAttach(message.NtChallengeResponse.Buffer, message.NtChallengeResponse.Len);
ntlm_read_ntlm_v2_response(s, &response);
PStreamFreeDetach(s);
CopyMemory(context->ClientChallenge, response.Challenge.ClientChallenge, 8);
AvFlags = ntlm_av_pair_get(response.Challenge.AvPairs, MsvAvFlags);
if (AvFlags != NULL)
flags = *((UINT32*) ntlm_av_pair_get_value_pointer(AvFlags));
}
/* EncryptedRandomSessionKey */
ntlm_read_message_fields_buffer(s, &(message.EncryptedRandomSessionKey));
CopyMemory(context->EncryptedRandomSessionKey, message.EncryptedRandomSessionKey.Buffer, 16);
StreamSetOffset(s, PayloadBufferOffset);
if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
{
StreamRead(s, message.MessageIntegrityCheck, 16);
PayloadBufferOffset += 16;
}
/* move to end of stream, incorrectly assumes the EncryptedRandomSessionKey buffer is last */
StreamSetOffset(s, message.EncryptedRandomSessionKey.BufferOffset + message.EncryptedRandomSessionKey.Len);
length = StreamSize(s);
sspi_SecBufferAlloc(&context->AuthenticateMessage, length);
CopyMemory(context->AuthenticateMessage.pvBuffer, s->data, length);
buffer->cbBuffer = length;
#ifdef WITH_DEBUG_NTLM
printf("AUTHENTICATE_MESSAGE (length = %d)\n", length);
winpr_HexDump(s->data, length);
@ -686,6 +712,14 @@ SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer
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(response.Challenge.AvPairs);
if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
{
printf("MessageIntegrityCheck:\n");
winpr_HexDump(message.MessageIntegrityCheck, 16);
}
#endif
if (message.UserName.Len > 0)
@ -835,9 +869,15 @@ SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer
if (context->ntlm_v2 < 1)
message.Workstation.Len = 0;
if (message.Workstation.Len > 0)
message.NegotiateFlags |= NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
message.DomainName.Len = (UINT16) context->identity.DomainLength * 2;
message.DomainName.Buffer = (BYTE*) context->identity.Domain;
if (message.DomainName.Len > 0)
message.NegotiateFlags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
message.UserName.Len = (UINT16) context->identity.UserLength * 2;
message.UserName.Buffer = (BYTE*) context->identity.User;
@ -853,10 +893,10 @@ SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer
if (context->confidentiality)
message.NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
PayloadBufferOffset = 64;
if (context->ntlm_v2)
PayloadBufferOffset = 80; /* starting buffer offset */
else
PayloadBufferOffset = 64; /* starting buffer offset */
PayloadBufferOffset += 16; /* Message Integrity Check */
if (message.NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
PayloadBufferOffset += 8;