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")
42struct NTLM_AV_PAIR_IMPL
49static BOOL ntlm_av_pair_get_next_offset(
const NTLM_AV_PAIR* pAvPair,
size_t size,
size_t* pOffset);
52static BOOL ntlm_av_pair_check_data(
const NTLM_AV_PAIR* pAvPair,
size_t cbAvPair,
size_t size)
55 if (!pAvPair || cbAvPair <
sizeof(
struct NTLM_AV_PAIR_IMPL) + size)
57 if (!ntlm_av_pair_get_next_offset(pAvPair, cbAvPair, &offset))
59 return cbAvPair >= offset;
64static const char* get_av_pair_string(UINT16 pair)
70 case MsvAvNbComputerName:
71 return "MsvAvNbComputerName";
72 case MsvAvNbDomainName:
73 return "MsvAvNbDomainName";
74 case MsvAvDnsComputerName:
75 return "MsvAvDnsComputerName";
76 case MsvAvDnsDomainName:
77 return "MsvAvDnsDomainName";
78 case MsvAvDnsTreeName:
79 return "MsvAvDnsTreeName";
83 return "MsvAvTimestamp";
85 return "MsvAvSingleHost";
87 return "MsvAvTargetName";
88 case MsvAvChannelBindings:
89 return "MsvAvChannelBindings";
97static BOOL ntlm_av_pair_check(
const NTLM_AV_PAIR* pAvPair,
size_t cbAvPair);
100static NTLM_AV_PAIR* ntlm_av_pair_next(NTLM_AV_PAIR* pAvPairList,
size_t* pcbAvPairList);
102static inline void ntlm_av_pair_set_id(NTLM_AV_PAIR* pAvPair, UINT16
id)
104 WINPR_ASSERT(pAvPair);
105 struct NTLM_AV_PAIR_IMPL* impl = (
struct NTLM_AV_PAIR_IMPL*)pAvPair;
106 winpr_Data_Write_UINT16(&impl->AvId,
id);
109static inline void ntlm_av_pair_set_len(NTLM_AV_PAIR* pAvPair, UINT16 len)
111 WINPR_ASSERT(pAvPair);
112 struct NTLM_AV_PAIR_IMPL* impl = (
struct NTLM_AV_PAIR_IMPL*)pAvPair;
113 winpr_Data_Write_UINT16(&impl->AvLen, len);
117static BOOL ntlm_av_pair_list_init(NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList)
119 NTLM_AV_PAIR* pAvPair = pAvPairList;
121 if (!pAvPair || (cbAvPairList <
sizeof(
struct NTLM_AV_PAIR_IMPL)))
124 ntlm_av_pair_set_id(pAvPair, MsvAvEOL);
125 ntlm_av_pair_set_len(pAvPair, 0);
129WINPR_ATTR_NODISCARD
static inline BOOL ntlm_av_pair_get_id(
const NTLM_AV_PAIR* pAvPair,
130 size_t size, UINT16* pair)
132 if (!pAvPair || !pair)
135 if (size <
sizeof(
struct NTLM_AV_PAIR_IMPL))
138 const struct NTLM_AV_PAIR_IMPL* impl = (
const struct NTLM_AV_PAIR_IMPL*)pAvPair;
139 const UINT16 AvId = winpr_Data_Get_UINT16(&impl->AvId);
145ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList)
149 NTLM_AV_PAIR* pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair);
153 if (pAvPair < pAvPairList)
156 const size_t size = WINPR_ASSERTING_INT_CAST(
size_t, ((PBYTE)pAvPair - (PBYTE)pAvPairList)) +
157 sizeof(
struct NTLM_AV_PAIR_IMPL);
158 WINPR_ASSERT(size <= UINT32_MAX);
159 WINPR_ASSERT(size >= 0);
163WINPR_ATTR_NODISCARD
static inline BOOL ntlm_av_pair_get_len(
const NTLM_AV_PAIR* pAvPair,
164 size_t size,
size_t* pAvLen)
169 if (size <
sizeof(
struct NTLM_AV_PAIR_IMPL))
172 const struct NTLM_AV_PAIR_IMPL* impl = (
const struct NTLM_AV_PAIR_IMPL*)pAvPair;
173 const UINT16 AvLen = winpr_Data_Get_UINT16(&impl->AvLen);
179#ifdef WITH_DEBUG_NTLM
180void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList)
183 size_t cbAvPair = cbAvPairList;
184 NTLM_AV_PAIR* pAvPair = pAvPairList;
186 if (!ntlm_av_pair_check(pAvPair, cbAvPair))
189 WLog_VRB(TAG,
"AV_PAIRs =");
191 while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair) && (pair != MsvAvEOL))
194 if (!ntlm_av_pair_get_len(pAvPair, cbAvPair, &cbLen))
195 WLog_WARN(TAG,
"ntlm_av_pair_get_len failed");
197 WLog_VRB(TAG,
"\t%s AvId: %" PRIu16
" AvLen: %" PRIuz
"", get_av_pair_string(pair), pair,
199 winpr_HexDump(TAG, WLOG_TRACE, ntlm_av_pair_get_value_pointer(pAvPair, cbAvPair), cbLen);
201 pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
207static size_t ntlm_av_pair_list_size(
size_t AvPairsCount,
size_t AvPairsValueLength)
210 return ((AvPairsCount + 1) * 4ULL) + AvPairsValueLength;
213PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair,
size_t cbAvPair)
215 WINPR_ASSERT(pAvPair);
216 if (cbAvPair <
sizeof(
struct NTLM_AV_PAIR_IMPL))
218 return (PBYTE)pAvPair +
sizeof(
struct NTLM_AV_PAIR_IMPL);
222static BOOL ntlm_av_pair_get_next_offset(
const NTLM_AV_PAIR* pAvPair,
size_t size,
size_t* pOffset)
228 if (!ntlm_av_pair_get_len(pAvPair, size, &avLen))
230 *pOffset = avLen +
sizeof(
struct NTLM_AV_PAIR_IMPL);
235static BOOL ntlm_av_pair_check(
const NTLM_AV_PAIR* pAvPair,
size_t cbAvPair)
237 return ntlm_av_pair_check_data(pAvPair, cbAvPair, 0);
241static NTLM_AV_PAIR* ntlm_av_pair_next(NTLM_AV_PAIR* pAvPair,
size_t* pcbAvPair)
247 if (!ntlm_av_pair_check(pAvPair, *pcbAvPair))
250 if (!ntlm_av_pair_get_next_offset(pAvPair, *pcbAvPair, &offset))
253 *pcbAvPair -= offset;
254 NTLM_AV_PAIR* next = WINPR_CXX_COMPAT_CAST(NTLM_AV_PAIR*, ((PBYTE)pAvPair + offset));
255 if (!ntlm_av_pair_check(next, *pcbAvPair))
260NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList, NTLM_AV_ID AvId,
261 size_t* pcbAvPairListRemaining)
264 size_t cbAvPair = cbAvPairList;
265 NTLM_AV_PAIR* pAvPair = pAvPairList;
267 if (!ntlm_av_pair_check(pAvPair, cbAvPair))
270 while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &
id))
280 pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
285 if (pcbAvPairListRemaining)
286 *pcbAvPairListRemaining = cbAvPair;
292static BOOL ntlm_av_pair_add(NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList, NTLM_AV_ID AvId,
293 PBYTE Value, UINT16 AvLen)
296 NTLM_AV_PAIR* pAvPair =
nullptr;
298 pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair);
301 if (!pAvPair || cbAvPair < 2 *
sizeof(
struct NTLM_AV_PAIR_IMPL) + AvLen)
304 ntlm_av_pair_set_id(pAvPair, (UINT16)AvId);
305 ntlm_av_pair_set_len(pAvPair, AvLen);
308 WINPR_ASSERT(Value !=
nullptr);
309 CopyMemory(ntlm_av_pair_get_value_pointer(pAvPair, cbAvPair), Value, AvLen);
312 pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
313 return ntlm_av_pair_list_init(pAvPair, cbAvPair);
317static BOOL ntlm_av_pair_valid(UINT16 pair)
322 case MsvAvNbComputerName:
323 case MsvAvNbDomainName:
324 case MsvAvDnsComputerName:
325 case MsvAvDnsDomainName:
326 case MsvAvDnsTreeName:
329 case MsvAvSingleHost:
330 case MsvAvTargetName:
331 case MsvAvChannelBindings:
339static BOOL ntlm_av_pair_add_copy(NTLM_AV_PAIR* pAvPairList,
size_t cbAvPairList,
340 NTLM_AV_PAIR* pAvPair,
size_t cbAvPair)
345 if (!ntlm_av_pair_check(pAvPair, cbAvPair))
348 if (!ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair))
351 if (!ntlm_av_pair_get_len(pAvPair, cbAvPair, &avLen))
354 if (!ntlm_av_pair_valid(pair))
357 WINPR_ASSERT(avLen <= UINT16_MAX);
358 return ntlm_av_pair_add(pAvPairList, cbAvPairList, WINPR_ASSERTING_INT_CAST(NTLM_AV_ID, pair),
359 ntlm_av_pair_get_value_pointer(pAvPair, cbAvPair), (UINT16)avLen);
367 if (string->Length != 0)
368 free(string->Buffer);
369 string->Buffer =
nullptr;
371 string->MaximumLength = 0;
377 if (!other || (other->Length == 0) || (other->MaximumLength == 0))
379 if (other->Length > other->MaximumLength)
383 clone.Buffer = calloc(other->MaximumLength /
sizeof(WCHAR) + 1ull,
sizeof(WCHAR));
386 clone.Length = other->Length;
387 clone.MaximumLength = other->MaximumLength;
388 memcpy(clone.Buffer, other->Buffer, clone.Length);
392UNICODE_STRING ntlm_from_unicode_string_w(
const WCHAR* str,
size_t wcharlen)
395 if (!str || (wcharlen == 0))
397 if (wcharlen > USHRT_MAX /
sizeof(WCHAR))
399 clone.Buffer = wcsndup(str, wcharlen);
402 clone.Length = WINPR_ASSERTING_INT_CAST(UINT16, wcharlen *
sizeof(WCHAR));
403 clone.MaximumLength = WINPR_ASSERTING_INT_CAST(UINT16, wcharlen *
sizeof(WCHAR));
407UNICODE_STRING ntlm_from_unicode_string_utf8(
const char* str,
size_t charlen)
410 if (!str || (charlen == 0))
414 clone.Buffer = ConvertUtf8NToWCharAlloc(str, charlen, &wlen);
417 if (wlen > USHRT_MAX /
sizeof(WCHAR))
420 clone.Buffer =
nullptr;
423 clone.Length = WINPR_ASSERTING_INT_CAST(UINT16, wlen *
sizeof(WCHAR));
424 clone.MaximumLength = WINPR_ASSERTING_INT_CAST(UINT16, wlen *
sizeof(WCHAR));
431 if (str->Length == 0)
433 if (str->MaximumLength == 0)
435 return str->Buffer ==
nullptr;
466static BOOL ntlm_md5_update_uint32_be(WINPR_DIGEST_CTX* md5, UINT32 num)
469 be32[0] = (num >> 0) & 0xFF;
470 be32[1] = (num >> 8) & 0xFF;
471 be32[2] = (num >> 16) & 0xFF;
472 be32[3] = (num >> 24) & 0xFF;
473 return winpr_Digest_Update(md5, be32, 4);
476static void ntlm_compute_channel_bindings(
NTLM_CONTEXT* context)
478 WINPR_DIGEST_CTX* md5 =
nullptr;
479 BYTE* ChannelBindingToken =
nullptr;
480 UINT32 ChannelBindingTokenLength = 0;
483 WINPR_ASSERT(context);
485 ZeroMemory(context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH);
486 ChannelBindings = context->Bindings.Bindings;
488 if (!ChannelBindings)
491 if (!(md5 = winpr_Digest_New()))
494 if (!winpr_Digest_Init(md5, WINPR_MD_MD5))
498 ChannelBindingToken = &((BYTE*)ChannelBindings)[ChannelBindings->dwApplicationDataOffset];
500 if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwInitiatorAddrType))
503 if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbInitiatorLength))
506 if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwAcceptorAddrType))
509 if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbAcceptorLength))
512 if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbApplicationDataLength))
515 if (!winpr_Digest_Update(md5, (
void*)ChannelBindingToken, ChannelBindingTokenLength))
518 if (!winpr_Digest_Final(md5, context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH))
522 winpr_Digest_Free(md5);
525static void ntlm_compute_single_host_data(
NTLM_CONTEXT* context)
527 WINPR_ASSERT(context);
536 winpr_Data_Write_UINT32(&context->SingleHostData.Size, 48);
537 winpr_Data_Write_UINT32(&context->SingleHostData.Z4, 0);
538 winpr_Data_Write_UINT32(&context->SingleHostData.DataPresent, 1);
539 winpr_Data_Write_UINT32(&context->SingleHostData.CustomData, SECURITY_MANDATORY_MEDIUM_RID);
540 FillMemory(context->SingleHostData.MachineID, 32, 0xAA);
543BOOL ntlm_construct_challenge_target_info(
NTLM_CONTEXT* context)
547 WINPR_ASSERT(context);
549 const ULONG AvPairsCount = 5;
550 const ULONG AvPairsLength = context->NbDomainName.Length + context->NbComputerName.Length +
551 context->DnsDomainName.Length + context->DnsComputerName.Length + 8;
553 const size_t length = ntlm_av_pair_list_size(AvPairsCount, AvPairsLength);
554 if (!ntlm_SecBufferRealloc(&context->ChallengeTargetInfo,
555 WINPR_ASSERTING_INT_CAST(uint32_t, length)))
559 NTLM_AV_PAIR* pAvPairList = (NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer;
560 const size_t cbAvPairList = context->ChallengeTargetInfo.cbBuffer;
562 if (!ntlm_av_pair_list_init(pAvPairList, cbAvPairList))
565 if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbDomainName,
566 (PBYTE)context->NbDomainName.Buffer, context->NbDomainName.Length))
569 if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbComputerName,
570 (PBYTE)context->NbComputerName.Buffer, context->NbComputerName.Length))
573 if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsDomainName,
574 (PBYTE)context->DnsDomainName.Buffer, context->DnsDomainName.Length))
577 if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsComputerName,
578 (PBYTE)context->DnsComputerName.Buffer, context->DnsComputerName.Length))
581 if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvTimestamp, context->Timestamp,
582 sizeof(context->Timestamp)))
590BOOL ntlm_construct_authenticate_target_info(
NTLM_CONTEXT* context)
592 ULONG AvPairsCount = 0;
593 size_t AvPairsValueLength = 0;
594 NTLM_AV_PAIR* AvTimestamp =
nullptr;
595 NTLM_AV_PAIR* AvNbDomainName =
nullptr;
596 NTLM_AV_PAIR* AvNbComputerName =
nullptr;
597 NTLM_AV_PAIR* AvDnsDomainName =
nullptr;
598 NTLM_AV_PAIR* AvDnsComputerName =
nullptr;
599 NTLM_AV_PAIR* AvDnsTreeName =
nullptr;
600 NTLM_AV_PAIR* ChallengeTargetInfo =
nullptr;
601 NTLM_AV_PAIR* AuthenticateTargetInfo =
nullptr;
602 size_t cbAvTimestamp = 0;
603 size_t cbAvNbDomainName = 0;
604 size_t cbAvNbComputerName = 0;
605 size_t cbAvDnsDomainName = 0;
606 size_t cbAvDnsComputerName = 0;
607 size_t cbAvDnsTreeName = 0;
608 size_t cbChallengeTargetInfo = 0;
609 size_t cbAuthenticateTargetInfo = 0;
611 WINPR_ASSERT(context);
614 ChallengeTargetInfo = (NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer;
615 cbChallengeTargetInfo = context->ChallengeTargetInfo.cbBuffer;
616 AvNbDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvNbDomainName,
618 AvNbComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
619 MsvAvNbComputerName, &cbAvNbComputerName);
620 AvDnsDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
621 MsvAvDnsDomainName, &cbAvDnsDomainName);
622 AvDnsComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
623 MsvAvDnsComputerName, &cbAvDnsComputerName);
624 AvDnsTreeName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvDnsTreeName,
626 AvTimestamp = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvTimestamp,
632 if (!ntlm_av_pair_get_len(AvNbDomainName, cbAvNbDomainName, &avLen))
635 AvPairsValueLength += avLen;
638 if (AvNbComputerName)
641 if (!ntlm_av_pair_get_len(AvNbComputerName, cbAvNbComputerName, &avLen))
644 AvPairsValueLength += avLen;
650 if (!ntlm_av_pair_get_len(AvDnsDomainName, cbAvDnsDomainName, &avLen))
653 AvPairsValueLength += avLen;
656 if (AvDnsComputerName)
659 if (!ntlm_av_pair_get_len(AvDnsComputerName, cbAvDnsComputerName, &avLen))
662 AvPairsValueLength += avLen;
668 if (!ntlm_av_pair_get_len(AvDnsTreeName, cbAvDnsTreeName, &avLen))
671 AvPairsValueLength += avLen;
675 AvPairsValueLength += 8;
680 AvPairsValueLength += 4;
683 if (context->SendSingleHostData)
686 ntlm_compute_single_host_data(context);
687 AvPairsValueLength += context->SingleHostData.Size;
695 if (!context->SuppressExtendedProtection)
702 AvPairsValueLength += 16;
703 ntlm_compute_channel_bindings(context);
705 if (!ntlm_is_unicode_string_empty(&context->ServicePrincipalName))
708 AvPairsValueLength += context->ServicePrincipalName.Length;
713 size_t size = ntlm_av_pair_list_size(AvPairsCount, AvPairsValueLength);
717 if (!ntlm_SecBufferRealloc(&context->AuthenticateTargetInfo,
718 WINPR_ASSERTING_INT_CAST(uint32_t, size)))
722 AuthenticateTargetInfo = (NTLM_AV_PAIR*)context->AuthenticateTargetInfo.pvBuffer;
723 cbAuthenticateTargetInfo = context->AuthenticateTargetInfo.cbBuffer;
725 if (!ntlm_av_pair_list_init(AuthenticateTargetInfo, cbAuthenticateTargetInfo))
730 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvNbDomainName,
735 if (AvNbComputerName)
737 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
738 AvNbComputerName, cbAvNbComputerName))
744 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
745 AvDnsDomainName, cbAvDnsDomainName))
749 if (AvDnsComputerName)
751 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
752 AvDnsComputerName, cbAvDnsComputerName))
758 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvDnsTreeName,
765 if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvTimestamp,
773 winpr_Data_Write_UINT32(&flags, MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK);
775 if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvFlags,
780 if (context->SendSingleHostData)
782 WINPR_ASSERT(context->SingleHostData.Size <= UINT16_MAX);
783 if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvSingleHost,
784 (PBYTE)&context->SingleHostData,
785 (UINT16)context->SingleHostData.Size))
789 if (!context->SuppressExtendedProtection)
791 if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
792 MsvAvChannelBindings, context->ChannelBindingsHash, 16))
795 if (!ntlm_is_unicode_string_empty(&context->ServicePrincipalName))
797 if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvTargetName,
798 (PBYTE)context->ServicePrincipalName.Buffer,
799 context->ServicePrincipalName.Length))
807 NTLM_AV_PAIR* AvEOL =
808 ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvEOL, &cbAvEOL);
810 size_t cbAvEntryLen = 0;
811 if (!ntlm_av_pair_get_len(AvEOL, cbAvEOL, &cbAvEntryLen))
814 ZeroMemory(AvEOL,
sizeof(
struct NTLM_AV_PAIR_IMPL));
819 sspi_SecBufferFree(&context->AuthenticateTargetInfo);
823size_t ntlm_av_pair_get_length(NTLM_AV_PAIR* pAvPair)
825 WINPR_ASSERT(pAvPair);
827 const size_t size =
sizeof(
struct NTLM_AV_PAIR_IMPL);
829 if (!ntlm_av_pair_get_len(pAvPair, size, &len))