20#include <winpr/config.h>
22#include <winpr/assert.h>
28#include <winpr/print.h>
29#include <winpr/sysinfo.h>
30#include <winpr/tchar.h>
31#include <winpr/crypto.h>
33#include "ntlm_compute.h"
35#include "ntlm_av_pairs.h"
37#if defined(WITH_DEBUG_NTLM)
39#define TAG WINPR_TAG("sspi.NTLM")
42static BOOL ntlm_av_pair_get_next_offset(
const NTLM_AV_PAIR* pAvPair,
size_t size,
size_t* pOffset);
44static BOOL ntlm_av_pair_check_data(
const NTLM_AV_PAIR* pAvPair,
size_t cbAvPair,
size_t size)
49 if (!ntlm_av_pair_get_next_offset(pAvPair, cbAvPair, &offset))
51 return cbAvPair >= offset;
55static const char* get_av_pair_string(UINT16 pair)
61 case MsvAvNbComputerName:
62 return "MsvAvNbComputerName";
63 case MsvAvNbDomainName:
64 return "MsvAvNbDomainName";
65 case MsvAvDnsComputerName:
66 return "MsvAvDnsComputerName";
67 case MsvAvDnsDomainName:
68 return "MsvAvDnsDomainName";
69 case MsvAvDnsTreeName:
70 return "MsvAvDnsTreeName";
74 return "MsvAvTimestamp";
76 return "MsvAvSingleHost";
78 return "MsvAvTargetName";
79 case MsvAvChannelBindings:
80 return "MsvAvChannelBindings";
87static BOOL ntlm_av_pair_check(
const NTLM_AV_PAIR* pAvPair,
size_t cbAvPair);
90static inline void ntlm_av_pair_set_id(
NTLM_AV_PAIR* pAvPair, UINT16
id)
92 WINPR_ASSERT(pAvPair);
93 winpr_Data_Write_UINT16(&pAvPair->AvId,
id);
96static inline void ntlm_av_pair_set_len(
NTLM_AV_PAIR* pAvPair, UINT16 len)
98 WINPR_ASSERT(pAvPair);
99 winpr_Data_Write_UINT16(&pAvPair->AvLen, len);
102static BOOL ntlm_av_pair_list_init(
NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList)
109 ntlm_av_pair_set_id(pAvPair, MsvAvEOL);
110 ntlm_av_pair_set_len(pAvPair, 0);
114WINPR_ATTR_NODISCARD
static inline BOOL ntlm_av_pair_get_id(
const NTLM_AV_PAIR* pAvPair,
115 size_t size, UINT16* pair)
117 if (!pAvPair || !pair)
123 const UINT16 AvId = winpr_Data_Get_UINT16(&pAvPair->AvId);
129ULONG ntlm_av_pair_list_length(
NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList)
134 pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair);
138 if (pAvPair < pAvPairList)
141 const size_t size = WINPR_ASSERTING_INT_CAST(
size_t, ((PBYTE)pAvPair - (PBYTE)pAvPairList)) +
143 WINPR_ASSERT(size <= UINT32_MAX);
144 WINPR_ASSERT(size >= 0);
148WINPR_ATTR_NODISCARD
static inline BOOL ntlm_av_pair_get_len(
const NTLM_AV_PAIR* pAvPair,
149 size_t size,
size_t* pAvLen)
157 const UINT16 AvLen = winpr_Data_Get_UINT16(&pAvPair->AvLen);
163#ifdef WITH_DEBUG_NTLM
164void ntlm_print_av_pair_list(
NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList)
167 size_t cbAvPair = cbAvPairList;
170 if (!ntlm_av_pair_check(pAvPair, cbAvPair))
173 WLog_VRB(TAG,
"AV_PAIRs =");
175 while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair) && (pair != MsvAvEOL))
178 if (!ntlm_av_pair_get_len(pAvPair, cbAvPair, &cbLen))
179 WLog_WARN(TAG,
"ntlm_av_pair_get_len failed");
181 WLog_VRB(TAG,
"\t%s AvId: %" PRIu16
" AvLen: %" PRIuz
"", get_av_pair_string(pair), pair,
183 winpr_HexDump(TAG, WLOG_TRACE, ntlm_av_pair_get_value_pointer(pAvPair, cbAvPair), cbLen);
185 pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
190static size_t ntlm_av_pair_list_size(
size_t AvPairsCount,
size_t AvPairsValueLength)
193 return ((AvPairsCount + 1) * 4ULL) + AvPairsValueLength;
196PBYTE ntlm_av_pair_get_value_pointer(
NTLM_AV_PAIR* pAvPair,
size_t cbAvPair)
198 WINPR_ASSERT(pAvPair);
204static BOOL ntlm_av_pair_get_next_offset(
const NTLM_AV_PAIR* pAvPair,
size_t size,
size_t* pOffset)
210 if (!ntlm_av_pair_get_len(pAvPair, size, &avLen))
216static BOOL ntlm_av_pair_check(
const NTLM_AV_PAIR* pAvPair,
size_t cbAvPair)
218 return ntlm_av_pair_check_data(pAvPair, cbAvPair, 0);
227 if (!ntlm_av_pair_check(pAvPair, *pcbAvPair))
230 if (!ntlm_av_pair_get_next_offset(pAvPair, *pcbAvPair, &offset))
233 *pcbAvPair -= offset;
235 if (!ntlm_av_pair_check(next, *pcbAvPair))
241 size_t* pcbAvPairListRemaining)
244 size_t cbAvPair = cbAvPairList;
247 if (!ntlm_av_pair_check(pAvPair, cbAvPair))
250 while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &
id))
260 pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
265 if (pcbAvPairListRemaining)
266 *pcbAvPairListRemaining = cbAvPair;
271static BOOL ntlm_av_pair_add(
NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList, NTLM_AV_ID AvId,
272 PBYTE Value, UINT16 AvLen)
277 pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair);
280 if (!pAvPair || cbAvPair < 2 *
sizeof(
NTLM_AV_PAIR) + AvLen)
283 ntlm_av_pair_set_id(pAvPair, (UINT16)AvId);
284 ntlm_av_pair_set_len(pAvPair, AvLen);
287 WINPR_ASSERT(Value !=
nullptr);
288 CopyMemory(ntlm_av_pair_get_value_pointer(pAvPair, cbAvPair), Value, AvLen);
291 pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
292 return ntlm_av_pair_list_init(pAvPair, cbAvPair);
295static BOOL ntlm_av_pair_valid(UINT16 pair)
300 case MsvAvNbComputerName:
301 case MsvAvNbDomainName:
302 case MsvAvDnsComputerName:
303 case MsvAvDnsDomainName:
304 case MsvAvDnsTreeName:
307 case MsvAvSingleHost:
308 case MsvAvTargetName:
309 case MsvAvChannelBindings:
316static BOOL ntlm_av_pair_add_copy(
NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList,
322 if (!ntlm_av_pair_check(pAvPair, cbAvPair))
325 if (!ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair))
328 if (!ntlm_av_pair_get_len(pAvPair, cbAvPair, &avLen))
331 if (!ntlm_av_pair_valid(pair))
334 WINPR_ASSERT(avLen <= UINT16_MAX);
335 return ntlm_av_pair_add(pAvPairList, cbAvPairList, WINPR_ASSERTING_INT_CAST(NTLM_AV_ID, pair),
336 ntlm_av_pair_get_value_pointer(pAvPair, cbAvPair), (UINT16)avLen);
344 if (string->Length != 0)
345 free(string->Buffer);
346 string->Buffer =
nullptr;
348 string->MaximumLength = 0;
354 if (!other || (other->Length == 0) || (other->MaximumLength == 0))
356 if (other->Length > other->MaximumLength)
360 clone.Buffer = calloc(other->MaximumLength /
sizeof(WCHAR) + 1ull,
sizeof(WCHAR));
363 clone.Length = other->Length;
364 clone.MaximumLength = other->MaximumLength;
365 memcpy(clone.Buffer, other->Buffer, clone.Length);
369UNICODE_STRING ntlm_from_unicode_string_w(
const WCHAR* str,
size_t wcharlen)
372 if (!str || (wcharlen == 0))
374 if (wcharlen > USHRT_MAX /
sizeof(WCHAR))
376 clone.Buffer = wcsndup(str, wcharlen);
379 clone.Length = WINPR_ASSERTING_INT_CAST(UINT16, wcharlen *
sizeof(WCHAR));
380 clone.MaximumLength = WINPR_ASSERTING_INT_CAST(UINT16, wcharlen *
sizeof(WCHAR));
384UNICODE_STRING ntlm_from_unicode_string_utf8(
const char* str,
size_t charlen)
387 if (!str || (charlen == 0))
391 clone.Buffer = ConvertUtf8NToWCharAlloc(str, charlen, &wlen);
394 if (wlen > USHRT_MAX /
sizeof(WCHAR))
397 clone.Buffer =
nullptr;
400 clone.Length = WINPR_ASSERTING_INT_CAST(UINT16, wlen *
sizeof(WCHAR));
401 clone.MaximumLength = WINPR_ASSERTING_INT_CAST(UINT16, wlen *
sizeof(WCHAR));
408 if (str->Length == 0)
410 if (str->MaximumLength == 0)
412 return str->Buffer ==
nullptr;
443static BOOL ntlm_md5_update_uint32_be(WINPR_DIGEST_CTX* md5, UINT32 num)
446 be32[0] = (num >> 0) & 0xFF;
447 be32[1] = (num >> 8) & 0xFF;
448 be32[2] = (num >> 16) & 0xFF;
449 be32[3] = (num >> 24) & 0xFF;
450 return winpr_Digest_Update(md5, be32, 4);
453static void ntlm_compute_channel_bindings(
NTLM_CONTEXT* context)
455 WINPR_DIGEST_CTX* md5 =
nullptr;
456 BYTE* ChannelBindingToken =
nullptr;
457 UINT32 ChannelBindingTokenLength = 0;
460 WINPR_ASSERT(context);
462 ZeroMemory(context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH);
463 ChannelBindings = context->Bindings.Bindings;
465 if (!ChannelBindings)
468 if (!(md5 = winpr_Digest_New()))
471 if (!winpr_Digest_Init(md5, WINPR_MD_MD5))
475 ChannelBindingToken = &((BYTE*)ChannelBindings)[ChannelBindings->dwApplicationDataOffset];
477 if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwInitiatorAddrType))
480 if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbInitiatorLength))
483 if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwAcceptorAddrType))
486 if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbAcceptorLength))
489 if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbApplicationDataLength))
492 if (!winpr_Digest_Update(md5, (
void*)ChannelBindingToken, ChannelBindingTokenLength))
495 if (!winpr_Digest_Final(md5, context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH))
499 winpr_Digest_Free(md5);
502static void ntlm_compute_single_host_data(
NTLM_CONTEXT* context)
504 WINPR_ASSERT(context);
513 winpr_Data_Write_UINT32(&context->SingleHostData.Size, 48);
514 winpr_Data_Write_UINT32(&context->SingleHostData.Z4, 0);
515 winpr_Data_Write_UINT32(&context->SingleHostData.DataPresent, 1);
516 winpr_Data_Write_UINT32(&context->SingleHostData.CustomData, SECURITY_MANDATORY_MEDIUM_RID);
517 FillMemory(context->SingleHostData.MachineID, 32, 0xAA);
520BOOL ntlm_construct_challenge_target_info(
NTLM_CONTEXT* context)
524 WINPR_ASSERT(context);
526 const ULONG AvPairsCount = 5;
527 const ULONG AvPairsLength = context->NbDomainName.Length + context->NbComputerName.Length +
528 context->DnsDomainName.Length + context->DnsComputerName.Length + 8;
530 const size_t length = ntlm_av_pair_list_size(AvPairsCount, AvPairsLength);
531 if (!ntlm_SecBufferRealloc(&context->ChallengeTargetInfo,
532 WINPR_ASSERTING_INT_CAST(uint32_t, length)))
537 const size_t cbAvPairList = context->ChallengeTargetInfo.cbBuffer;
539 if (!ntlm_av_pair_list_init(pAvPairList, cbAvPairList))
542 if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbDomainName,
543 (PBYTE)context->NbDomainName.Buffer, context->NbDomainName.Length))
546 if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbComputerName,
547 (PBYTE)context->NbComputerName.Buffer, context->NbComputerName.Length))
550 if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsDomainName,
551 (PBYTE)context->DnsDomainName.Buffer, context->DnsDomainName.Length))
554 if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsComputerName,
555 (PBYTE)context->DnsComputerName.Buffer, context->DnsComputerName.Length))
558 if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvTimestamp, context->Timestamp,
559 sizeof(context->Timestamp)))
567BOOL ntlm_construct_authenticate_target_info(
NTLM_CONTEXT* context)
569 ULONG AvPairsCount = 0;
570 size_t AvPairsValueLength = 0;
579 size_t cbAvTimestamp = 0;
580 size_t cbAvNbDomainName = 0;
581 size_t cbAvNbComputerName = 0;
582 size_t cbAvDnsDomainName = 0;
583 size_t cbAvDnsComputerName = 0;
584 size_t cbAvDnsTreeName = 0;
585 size_t cbChallengeTargetInfo = 0;
586 size_t cbAuthenticateTargetInfo = 0;
588 WINPR_ASSERT(context);
591 ChallengeTargetInfo = (
NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer;
592 cbChallengeTargetInfo = context->ChallengeTargetInfo.cbBuffer;
593 AvNbDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvNbDomainName,
595 AvNbComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
596 MsvAvNbComputerName, &cbAvNbComputerName);
597 AvDnsDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
598 MsvAvDnsDomainName, &cbAvDnsDomainName);
599 AvDnsComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
600 MsvAvDnsComputerName, &cbAvDnsComputerName);
601 AvDnsTreeName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvDnsTreeName,
603 AvTimestamp = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvTimestamp,
609 if (!ntlm_av_pair_get_len(AvNbDomainName, cbAvNbDomainName, &avLen))
612 AvPairsValueLength += avLen;
615 if (AvNbComputerName)
618 if (!ntlm_av_pair_get_len(AvNbComputerName, cbAvNbComputerName, &avLen))
621 AvPairsValueLength += avLen;
627 if (!ntlm_av_pair_get_len(AvDnsDomainName, cbAvDnsDomainName, &avLen))
630 AvPairsValueLength += avLen;
633 if (AvDnsComputerName)
636 if (!ntlm_av_pair_get_len(AvDnsComputerName, cbAvDnsComputerName, &avLen))
639 AvPairsValueLength += avLen;
645 if (!ntlm_av_pair_get_len(AvDnsTreeName, cbAvDnsTreeName, &avLen))
648 AvPairsValueLength += avLen;
652 AvPairsValueLength += 8;
657 AvPairsValueLength += 4;
660 if (context->SendSingleHostData)
663 ntlm_compute_single_host_data(context);
664 AvPairsValueLength += context->SingleHostData.Size;
672 if (!context->SuppressExtendedProtection)
679 AvPairsValueLength += 16;
680 ntlm_compute_channel_bindings(context);
682 if (!ntlm_is_unicode_string_empty(&context->ServicePrincipalName))
685 AvPairsValueLength += context->ServicePrincipalName.Length;
690 size_t size = ntlm_av_pair_list_size(AvPairsCount, AvPairsValueLength);
694 if (!ntlm_SecBufferRealloc(&context->AuthenticateTargetInfo,
695 WINPR_ASSERTING_INT_CAST(uint32_t, size)))
699 AuthenticateTargetInfo = (
NTLM_AV_PAIR*)context->AuthenticateTargetInfo.pvBuffer;
700 cbAuthenticateTargetInfo = context->AuthenticateTargetInfo.cbBuffer;
702 if (!ntlm_av_pair_list_init(AuthenticateTargetInfo, cbAuthenticateTargetInfo))
707 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvNbDomainName,
712 if (AvNbComputerName)
714 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
715 AvNbComputerName, cbAvNbComputerName))
721 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
722 AvDnsDomainName, cbAvDnsDomainName))
726 if (AvDnsComputerName)
728 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
729 AvDnsComputerName, cbAvDnsComputerName))
735 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvDnsTreeName,
742 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvTimestamp,
750 winpr_Data_Write_UINT32(&flags, MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK);
752 if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvFlags,
757 if (context->SendSingleHostData)
759 WINPR_ASSERT(context->SingleHostData.Size <= UINT16_MAX);
760 if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvSingleHost,
761 (PBYTE)&context->SingleHostData,
762 (UINT16)context->SingleHostData.Size))
766 if (!context->SuppressExtendedProtection)
768 if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
769 MsvAvChannelBindings, context->ChannelBindingsHash, 16))
772 if (!ntlm_is_unicode_string_empty(&context->ServicePrincipalName))
774 if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvTargetName,
775 (PBYTE)context->ServicePrincipalName.Buffer,
776 context->ServicePrincipalName.Length))
785 ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvEOL, &cbAvEOL);
787 size_t cbAvEntryLen = 0;
788 if (!ntlm_av_pair_get_len(AvEOL, cbAvEOL, &cbAvEntryLen))
796 sspi_SecBufferFree(&context->AuthenticateTargetInfo);