20#include <winpr/config.h>
23#include <winpr/assert.h>
24#include <winpr/sspi.h>
25#include <winpr/print.h>
26#include <winpr/string.h>
27#include <winpr/tchar.h>
28#include <winpr/sysinfo.h>
29#include <winpr/registry.h>
30#include <winpr/endian.h>
31#include <winpr/build-config.h>
34#include "ntlm_export.h"
37#include "ntlm_message.h"
39#include "../../utils.h"
42#define TAG WINPR_TAG("sspi.NTLM")
45#define MIN(a, b) ((a) < (b)) ? (a) : (b)
48#define WINPR_KEY "Software\\%s\\WinPR\\NTLM"
50#define check_context(ctx) check_context_((ctx), __FILE__, __func__, __LINE__)
53static BOOL check_context_(
NTLM_CONTEXT* context,
const char* file,
const char* fkt,
size_t line)
56 wLog* log = WLog_Get(TAG);
57 const DWORD log_level = WLOG_ERROR;
61 if (WLog_IsLevelActive(log, log_level))
62 WLog_PrintTextMessage(log, log_level, line, file, fkt,
"invalid context");
67 if (!context->RecvRc4Seal)
69 if (WLog_IsLevelActive(log, log_level))
70 WLog_PrintTextMessage(log, log_level, line, file, fkt,
"invalid context->RecvRc4Seal");
73 if (!context->SendRc4Seal)
75 if (WLog_IsLevelActive(log, log_level))
76 WLog_PrintTextMessage(log, log_level, line, file, fkt,
"invalid context->SendRc4Seal");
80 if (!context->SendSigningKey)
82 if (WLog_IsLevelActive(log, log_level))
83 WLog_PrintTextMessage(log, log_level, line, file, fkt,
84 "invalid context->SendSigningKey");
87 if (!context->RecvSigningKey)
89 if (WLog_IsLevelActive(log, log_level))
90 WLog_PrintTextMessage(log, log_level, line, file, fkt,
91 "invalid context->RecvSigningKey");
94 if (!context->SendSealingKey)
96 if (WLog_IsLevelActive(log, log_level))
97 WLog_PrintTextMessage(log, log_level, line, file, fkt,
98 "invalid context->SendSealingKey");
101 if (!context->RecvSealingKey)
103 if (WLog_IsLevelActive(log, log_level))
104 WLog_PrintTextMessage(log, log_level, line, file, fkt,
105 "invalid context->RecvSealingKey");
111WINPR_ATTR_MALLOC(free, 1)
112static
char* get_computer_name(COMPUTER_NAME_FORMAT type,
size_t* pSize)
119 if (GetComputerNameExA(type,
nullptr, &nSize))
122 if (GetLastError() != ERROR_MORE_DATA)
125 char* computerName = calloc(1, nSize);
130 if (!GetComputerNameExA(type, computerName, &nSize))
142SECURITY_STATUS ntlm_SetContextWorkstationX(
NTLM_CONTEXT* context, BOOL unicode,
const void* data,
145 WINPR_ASSERT(context);
146 ntlm_free_unicode_string(&context->Workstation);
153 context->Workstation = ntlm_from_unicode_string_w(data, length /
sizeof(WCHAR));
155 context->Workstation = ntlm_from_unicode_string_utf8(data, length);
157 if (ntlm_is_unicode_string_empty(&context->Workstation))
158 return SEC_E_INSUFFICIENT_MEMORY;
164static int ntlm_SetContextWorkstation(
NTLM_CONTEXT* context,
const char* Workstation)
166 const char* ws = Workstation;
167 CHAR* computerName =
nullptr;
171 computerName = get_computer_name(ComputerNameNetBIOS,
nullptr);
177 const size_t len = strlen(ws);
178 const SECURITY_STATUS status = ntlm_SetContextWorkstationX(context, FALSE, ws, len);
181 return (status == SEC_E_OK) ? 1 : -1;
185static int ntlm_SetContextServicePrincipalNameW(
NTLM_CONTEXT* context, LPWSTR ServicePrincipalName)
187 WINPR_ASSERT(context);
189 ntlm_free_unicode_string(&context->ServicePrincipalName);
190 if (!ServicePrincipalName)
193 const size_t len = _wcslen(ServicePrincipalName);
194 context->ServicePrincipalName = ntlm_from_unicode_string_w(ServicePrincipalName, len);
195 if (ntlm_is_unicode_string_empty(&context->ServicePrincipalName))
202static int ntlm_SetContextTargetName(
NTLM_CONTEXT* context,
char* TargetName)
204 char* name = TargetName;
205 WINPR_ASSERT(context);
210 char* computerName = get_computer_name(ComputerNameNetBIOS, &nSize);
215 if (nSize > MAX_COMPUTERNAME_LENGTH)
216 computerName[MAX_COMPUTERNAME_LENGTH] =
'\0';
227 sspi_SecBufferFree(&context->TargetName);
228 context->TargetName.pvBuffer = ConvertUtf8ToWCharAlloc(name, &len);
230 if (!context->TargetName.pvBuffer || (len > UINT16_MAX /
sizeof(WCHAR)))
232 free(context->TargetName.pvBuffer);
233 context->TargetName.pvBuffer =
nullptr;
241 context->TargetName.cbBuffer = (USHORT)(len *
sizeof(WCHAR));
254 winpr_RC4_Free(context->SendRc4Seal);
255 winpr_RC4_Free(context->RecvRc4Seal);
256 sspi_SecBufferFree(&context->NegotiateMessage);
257 sspi_SecBufferFree(&context->ChallengeMessage);
258 sspi_SecBufferFree(&context->AuthenticateMessage);
259 sspi_SecBufferFree(&context->ChallengeTargetInfo);
260 sspi_SecBufferFree(&context->AuthenticateTargetInfo);
261 sspi_SecBufferFree(&context->TargetName);
262 sspi_SecBufferFree(&context->NtChallengeResponse);
263 sspi_SecBufferFree(&context->LmChallengeResponse);
264 ntlm_free_unicode_string(&context->ServicePrincipalName);
265 ntlm_free_unicode_string(&context->Workstation);
266 ntlm_free_unicode_string(&context->NbComputerName);
267 ntlm_free_unicode_string(&context->NbDomainName);
268 ntlm_free_unicode_string(&context->DnsComputerName);
269 ntlm_free_unicode_string(&context->DnsDomainName);
271 ntlm_free_messages(context);
274 memset(context->NtlmHash, 0,
sizeof(context->NtlmHash));
275 memset(context->NtlmV2Hash, 0,
sizeof(context->NtlmV2Hash));
276 memset(context->SessionBaseKey, 0,
sizeof(context->SessionBaseKey));
277 memset(context->KeyExchangeKey, 0,
sizeof(context->KeyExchangeKey));
278 memset(context->RandomSessionKey, 0,
sizeof(context->RandomSessionKey));
279 memset(context->ExportedSessionKey, 0,
sizeof(context->ExportedSessionKey));
280 memset(context->EncryptedRandomSessionKey, 0,
sizeof(context->EncryptedRandomSessionKey));
281 memset(context->NtProofString, 0,
sizeof(context->NtProofString));
287 WINPR_ATTR_UNUSED COMPUTER_NAME_FORMAT type)
290 ntlm_free_unicode_string(pName);
293 char* name = get_computer_name(ComputerNameNetBIOS, &len);
299 *pName = ntlm_from_unicode_string_utf8(name, len);
302 return !ntlm_is_unicode_string_empty(pName);
306static BOOL ntlm_ContextFillDefaultNames(
NTLM_CONTEXT* context)
308 WINPR_ASSERT(context);
310 if (ntlm_SetContextWorkstation(context,
nullptr) < 0)
313 if (ntlm_get_target_computer_name(&context->NbDomainName, ComputerNameNetBIOS) < 0)
316 if (ntlm_get_target_computer_name(&context->NbComputerName, ComputerNameNetBIOS) < 0)
319 if (ntlm_get_target_computer_name(&context->DnsDomainName, ComputerNameDnsDomain) < 0)
322 if (ntlm_get_target_computer_name(&context->DnsComputerName, ComputerNameDnsHostname) < 0)
327static BOOL ntlm_try_set_from_registry(HKEY hKey,
const char* key,
UNICODE_STRING* ustr)
334 WCHAR wkey[64] = WINPR_C_ARRAY_INIT;
335 const SSIZE_T res = ConvertUtf8ToWChar(key, wkey, ARRAYSIZE(wkey));
338 WINPR_ASSERT((
size_t)res < ARRAYSIZE(wkey));
342 if (RegQueryValueExW(hKey, wkey,
nullptr, &dwType,
nullptr, &dwSize) != ERROR_SUCCESS)
345 if ((dwSize > UINT16_MAX) || ((dwSize % 2) != 0))
348 str.Buffer = calloc(dwSize /
sizeof(WCHAR) + 1,
sizeof(WCHAR));
351 str.Length = WINPR_ASSERTING_INT_CAST(UINT16, dwSize);
352 str.MaximumLength = WINPR_ASSERTING_INT_CAST(UINT16, dwSize);
354 const LONG rc = RegQueryValueExW(hKey, wkey,
nullptr, &dwType, (BYTE*)str.Buffer, &dwSize);
355 if (rc != ERROR_SUCCESS)
357 ntlm_free_unicode_string(ustr);
362 ntlm_free_unicode_string(&str);
367static BOOL ntlm_ContextFromConfig(
NTLM_CONTEXT* context)
370 WINPR_ASSERT(context);
372 char* key = winpr_getApplicatonDetailsRegKey(WINPR_KEY);
378 RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
381 if (status == ERROR_SUCCESS)
387 if (RegQueryValueEx(hKey, _T(
"NTLMv2"),
nullptr, &dwType, (BYTE*)&dwValue,
388 &dwSize) == ERROR_SUCCESS)
389 context->NTLMv2 = dwValue ? 1 : 0;
391 if (RegQueryValueEx(hKey, _T(
"UseMIC"),
nullptr, &dwType, (BYTE*)&dwValue,
392 &dwSize) == ERROR_SUCCESS)
393 context->UseMIC = dwValue ? 1 : 0;
395 if (RegQueryValueEx(hKey, _T(
"SendVersionInfo"),
nullptr, &dwType, (BYTE*)&dwValue,
396 &dwSize) == ERROR_SUCCESS)
397 context->SendVersionInfo = dwValue ? 1 : 0;
399 if (RegQueryValueEx(hKey, _T(
"SendSingleHostData"),
nullptr, &dwType,
400 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
401 context->SendSingleHostData = dwValue ? 1 : 0;
403 if (RegQueryValueEx(hKey, _T(
"SendWorkstationName"),
nullptr, &dwType,
404 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
405 context->SendWorkstationName = dwValue ? 1 : 0;
407 (void)ntlm_try_set_from_registry(hKey,
"WorkstationName", &context->Workstation);
408 (void)ntlm_try_set_from_registry(hKey,
"NbDomainName", &context->NbDomainName);
409 (void)ntlm_try_set_from_registry(hKey,
"NbComputerName", &context->NbComputerName);
410 (void)ntlm_try_set_from_registry(hKey,
"DnsDomainName", &context->DnsDomainName);
411 (void)ntlm_try_set_from_registry(hKey,
"DnsComputerName",
412 &context->DnsComputerName);
421 RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(
"System\\CurrentControlSet\\Control\\LSA"), 0,
422 KEY_READ | KEY_WOW64_64KEY, &hKey);
424 if (status == ERROR_SUCCESS)
429 if (RegQueryValueEx(hKey, _T(
"SuppressExtendedProtection"),
nullptr, &dwType,
430 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
431 context->SuppressExtendedProtection = dwValue ? 1 : 0;
440 context->SuppressExtendedProtection = FALSE;
444WINPR_ATTR_MALLOC(ntlm_ContextFree, 1)
452 context->NTLMv2 = TRUE;
453 context->UseMIC = FALSE;
454 context->SendVersionInfo = TRUE;
455 context->SendSingleHostData = FALSE;
456 context->SendWorkstationName = TRUE;
457 context->NegotiateKeyExchange = TRUE;
458 context->UseSamFileDatabase = TRUE;
460 context->NegotiateFlags = 0;
461 context->LmCompatibilityLevel = 3;
462 ntlm_change_state(context, NTLM_STATE_INITIAL);
463 FillMemory(context->MachineID,
sizeof(context->MachineID), 0xAA);
466 context->UseMIC = TRUE;
468 if (!ntlm_ContextFillDefaultNames(context))
470 if (!ntlm_ContextFromConfig(context))
476 ntlm_ContextFree(context);
481static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
482 WINPR_ATTR_UNUSED SEC_WCHAR* pszPrincipal, WINPR_ATTR_UNUSED SEC_WCHAR* pszPackage,
483 ULONG fCredentialUse, WINPR_ATTR_UNUSED
void* pvLogonID,
void* pAuthData,
484 SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
487 if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) &&
488 (fCredentialUse != SECPKG_CRED_BOTH))
490 return SEC_E_INVALID_PARAMETER;
496 return SEC_E_INTERNAL_ERROR;
498 credentials->fCredentialUse = fCredentialUse;
499 credentials->pGetKeyFn = pGetKeyFn;
500 credentials->pvGetKeyArgument = pvGetKeyArgument;
502#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
503 SEC_WINPR_NTLM_SETTINGS* settingsV1 =
nullptr;
505 SEC_WINPR_NTLM_SETTINGS_V2* settingsV2 =
nullptr;
508 UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData);
510 if (sspi_CopyAuthIdentity(&(credentials->identity),
513 sspi_CredentialsFree(credentials);
514 return SEC_E_INVALID_PARAMETER;
517#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
518 if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED)
519 settingsV1 = (((SEC_WINNT_AUTH_IDENTITY_WINPR*)pAuthData)->ntlmSettings);
522 if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED_v2)
524 const SEC_WINNT_AUTH_IDENTITY_WINPR_V2* auth =
525 (
const SEC_WINNT_AUTH_IDENTITY_WINPR_V2*)pAuthData;
527 if (auth->version < SEC_WINNT_AUTH_IDENTITY_WINPR_V2_REVISION_1)
528 return SEC_E_INVALID_PARAMETER;
529 settingsV2 = auth->ntlmSettingsV2;
533#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
536 if (settingsV1->samFile)
538 if (!sspi_CloneSecSettingsString(&credentials->ntlmSettingsV2->samFile,
539 settingsV1->samFile))
541 sspi_CredentialsFree(credentials);
542 return SEC_E_INSUFFICIENT_MEMORY;
545 credentials->ntlmSettingsV2->hashCallback = settingsV1->hashCallback;
546 credentials->ntlmSettingsV2->hashCallbackArg = settingsV1->hashCallbackArg;
552 sspi_FreeSecNtlmSettings(credentials->ntlmSettingsV2);
553 credentials->ntlmSettingsV2 = sspi_CloneSecNtlmSettings(settingsV2);
554 if (!credentials->ntlmSettingsV2)
556 sspi_CredentialsFree(credentials);
557 return SEC_E_INVALID_PARAMETER;
561 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)credentials);
562 sspi_SecureHandleSetPackageId(phCredential, SSPI_PACKAGE_NTLM);
567static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
568 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
569 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
572 SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
573 SEC_WCHAR* principal =
nullptr;
574 SEC_WCHAR*
package = nullptr;
578 principal = ConvertUtf8ToWCharAlloc(pszPrincipal,
nullptr);
584 package = ConvertUtf8ToWCharAlloc(pszPackage, nullptr);
590 ntlm_AcquireCredentialsHandleW(principal, package, fCredentialUse, pvLogonID, pAuthData,
591 pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
601static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(
PCredHandle phCredential)
604 return SEC_E_INVALID_HANDLE;
608 sspi_SecureHandleInvalidate(phCredential);
610 return SEC_E_INVALID_HANDLE;
612 sspi_CredentialsFree(credentials);
617static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(
618 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
619 WINPR_ATTR_UNUSED
void* pBuffer)
621 if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
626 WLog_ERR(TAG,
"TODO: Implement");
627 return SEC_E_UNSUPPORTED_FUNCTION;
631static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(
PCredHandle phCredential,
632 ULONG ulAttribute,
void* pBuffer)
634 return ntlm_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
638static SECURITY_STATUS ntml_setUnicodeStringA(
UNICODE_STRING* str,
const char* val,
size_t charlen);
644static SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(
647 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED
PTimeStamp ptsTimeStamp)
649 SECURITY_STATUS status = 0;
655 if (phContext && !phContext->dwLower && !phContext->dwUpper)
656 return SEC_E_INVALID_HANDLE;
662 context = ntlm_ContextNew();
665 return SEC_E_INSUFFICIENT_MEMORY;
667 context->server = TRUE;
669 if (fContextReq & ASC_REQ_CONFIDENTIALITY)
670 context->confidentiality = TRUE;
672 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
673 context->credentials = credentials;
674 context->SamFile = credentials->ntlmSettingsV2->samFile;
675 context->HashCallback = credentials->ntlmSettingsV2->hashCallback;
676 context->HashCallbackArg = credentials->ntlmSettingsV2->hashCallbackArg;
678 if (credentials->ntlmSettingsV2->dnsComputerName)
680 const SECURITY_STATUS rc = ntml_setUnicodeStringA(
681 &context->DnsComputerName, credentials->ntlmSettingsV2->dnsComputerName,
682 strlen(credentials->ntlmSettingsV2->dnsComputerName));
687 if (credentials->ntlmSettingsV2->dnsDomainName)
689 const SECURITY_STATUS rc = ntml_setUnicodeStringA(
690 &context->DnsDomainName, credentials->ntlmSettingsV2->dnsDomainName,
691 strlen(credentials->ntlmSettingsV2->dnsDomainName));
696 if (credentials->ntlmSettingsV2->netBiosComputerName)
698 const SECURITY_STATUS rc = ntml_setUnicodeStringA(
699 &context->NbComputerName, credentials->ntlmSettingsV2->netBiosComputerName,
700 strlen(credentials->ntlmSettingsV2->netBiosComputerName));
705 if (credentials->ntlmSettingsV2->netBiosDomainName)
707 const SECURITY_STATUS rc = ntml_setUnicodeStringA(
708 &context->NbDomainName, credentials->ntlmSettingsV2->netBiosDomainName,
709 strlen(credentials->ntlmSettingsV2->netBiosDomainName));
714 if (!ntlm_SetContextTargetName(context, credentials->ntlmSettingsV2->targetName))
715 return SEC_E_INVALID_HANDLE;
716 sspi_SecureHandleSetLowerPointer(phNewContext, context);
717 sspi_SecureHandleSetPackageId(phNewContext, SSPI_PACKAGE_NTLM);
720 switch (ntlm_get_state(context))
722 case NTLM_STATE_INITIAL:
724 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
727 return SEC_E_INVALID_TOKEN;
729 if (pInput->cBuffers < 1)
730 return SEC_E_INVALID_TOKEN;
732 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
735 return SEC_E_INVALID_TOKEN;
737 if (input_buffer->cbBuffer < 1)
738 return SEC_E_INVALID_TOKEN;
740 status = ntlm_read_NegotiateMessage(context, input_buffer);
741 if (status != SEC_I_CONTINUE_NEEDED)
744 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
747 return SEC_E_INVALID_TOKEN;
749 if (pOutput->cBuffers < 1)
750 return SEC_E_INVALID_TOKEN;
752 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
754 if (!output_buffer->BufferType)
755 return SEC_E_INVALID_TOKEN;
757 if (output_buffer->cbBuffer < 1)
758 return SEC_E_INSUFFICIENT_MEMORY;
760 return ntlm_write_ChallengeMessage(context, output_buffer);
763 return SEC_E_OUT_OF_SEQUENCE;
766 case NTLM_STATE_AUTHENTICATE:
769 return SEC_E_INVALID_TOKEN;
771 if (pInput->cBuffers < 1)
772 return SEC_E_INVALID_TOKEN;
774 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
777 return SEC_E_INVALID_TOKEN;
779 if (input_buffer->cbBuffer < 1)
780 return SEC_E_INVALID_TOKEN;
782 status = ntlm_read_AuthenticateMessage(context, input_buffer);
786 for (ULONG i = 0; i < pOutput->cBuffers; i++)
788 pOutput->pBuffers[i].cbBuffer = 0;
789 pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN;
797 return SEC_E_OUT_OF_SEQUENCE;
802static SECURITY_STATUS SEC_ENTRY
803ntlm_ImpersonateSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
809static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
811 WINPR_ATTR_UNUSED ULONG Reserved1, WINPR_ATTR_UNUSED ULONG TargetDataRep,
PSecBufferDesc pInput,
813 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED
PTimeStamp ptsExpiry)
815 SECURITY_STATUS status = 0;
821 if (phContext && !phContext->dwLower && !phContext->dwUpper)
822 return SEC_E_INVALID_HANDLE;
828 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
833 context = ntlm_ContextNew();
836 return SEC_E_INSUFFICIENT_MEMORY;
838 if (fContextReq & ISC_REQ_CONFIDENTIALITY)
839 context->confidentiality = TRUE;
841 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
842 context->credentials = credentials;
844 if (ntlm_SetContextServicePrincipalNameW(context, pszTargetName) < 0)
846 ntlm_ContextFree(context);
847 return SEC_E_INTERNAL_ERROR;
850 sspi_SecureHandleSetLowerPointer(phNewContext, context);
851 sspi_SecureHandleSetPackageId(phNewContext, SSPI_PACKAGE_NTLM);
854 if ((!input_buffer) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE))
857 return SEC_E_INVALID_TOKEN;
859 if (pOutput->cBuffers < 1)
860 return SEC_E_INVALID_TOKEN;
862 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
865 return SEC_E_INVALID_TOKEN;
867 if (output_buffer->cbBuffer < 1)
868 return SEC_E_INVALID_TOKEN;
870 if (ntlm_get_state(context) == NTLM_STATE_INITIAL)
871 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
873 if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE)
874 return ntlm_write_NegotiateMessage(context, output_buffer);
876 return SEC_E_OUT_OF_SEQUENCE;
881 return SEC_E_INVALID_TOKEN;
883 if (input_buffer->cbBuffer < 1)
884 return SEC_E_INVALID_TOKEN;
886 PSecBuffer channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
888 if (channel_bindings)
890 context->Bindings.BindingsLength = channel_bindings->cbBuffer;
894 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
896 status = ntlm_read_ChallengeMessage(context, input_buffer);
898 if (status != SEC_I_CONTINUE_NEEDED)
902 return SEC_E_INVALID_TOKEN;
904 if (pOutput->cBuffers < 1)
905 return SEC_E_INVALID_TOKEN;
907 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
910 return SEC_E_INVALID_TOKEN;
912 if (output_buffer->cbBuffer < 1)
913 return SEC_E_INSUFFICIENT_MEMORY;
915 if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)
916 return ntlm_write_AuthenticateMessage(context, output_buffer);
919 return SEC_E_OUT_OF_SEQUENCE;
922 return SEC_E_OUT_OF_SEQUENCE;
929static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
931 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
934 SECURITY_STATUS status = 0;
935 SEC_WCHAR* pszTargetNameW =
nullptr;
939 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName,
nullptr);
941 return SEC_E_INTERNAL_ERROR;
944 status = ntlm_InitializeSecurityContextW(phCredential, phContext, pszTargetNameW, fContextReq,
945 Reserved1, TargetDataRep, pInput, Reserved2,
946 phNewContext, pOutput, pfContextAttr, ptsExpiry);
947 free(pszTargetNameW);
953static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(
PCtxtHandle phContext)
956 sspi_SecureHandleInvalidate(phContext);
957 ntlm_ContextFree(context);
963 BYTE* blob =
nullptr;
967 WINPR_ASSERT(ntproof);
969 target = &ntlm->ChallengeTargetInfo;
971 if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer))
972 return SEC_E_INSUFFICIENT_MEMORY;
974 blob = (BYTE*)ntproof->pvBuffer;
975 CopyMemory(blob, ntlm->ServerChallenge, 8);
979 CopyMemory(&blob[16], ntlm->Timestamp, 8);
980 CopyMemory(&blob[24], ntlm->ClientChallenge, 8);
983 CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer);
989 BYTE* blob =
nullptr;
993 WINPR_ASSERT(micvalue);
995 msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer +
996 ntlm->AuthenticateMessage.cbBuffer;
998 if (!sspi_SecBufferAlloc(micvalue, msgSize))
999 return SEC_E_INSUFFICIENT_MEMORY;
1001 blob = (BYTE*)micvalue->pvBuffer;
1002 CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer);
1003 blob += ntlm->NegotiateMessage.cbBuffer;
1004 CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer);
1005 blob += ntlm->ChallengeMessage.cbBuffer;
1006 CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer);
1007 blob += ntlm->MessageIntegrityCheckOffset;
1008 ZeroMemory(blob, 16);
1013static bool identityToAuthIdentity(
const SEC_WINNT_AUTH_IDENTITY* identity,
1016 WINPR_ASSERT(identity);
1022 *pAuthIdentity = empty;
1024 if ((identity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0)
1026 if (identity->UserLength > 0)
1028 if (ConvertWCharNToUtf8(identity->User, identity->UserLength, pAuthIdentity->User,
1029 ARRAYSIZE(pAuthIdentity->User)) <= 0)
1033 if (identity->DomainLength > 0)
1035 if (ConvertWCharNToUtf8(identity->Domain, identity->DomainLength, pAuthIdentity->Domain,
1036 ARRAYSIZE(pAuthIdentity->Domain)) <= 0)
1040 else if ((identity->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI) != 0)
1042 if (identity->UserLength > 0)
1044 const size_t len = MIN(ARRAYSIZE(pAuthIdentity->User) - 1, identity->UserLength);
1045 strncpy(pAuthIdentity->User, (
char*)identity->User, len);
1046 pAuthIdentity->User[len] =
'\0';
1049 if (identity->DomainLength > 0)
1051 const size_t len = MIN(ARRAYSIZE(pAuthIdentity->Domain) - 1, identity->DomainLength);
1052 strncpy(pAuthIdentity->Domain, (
char*)identity->Domain, len);
1053 pAuthIdentity->Domain[len] =
'\0';
1062static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesCommon(
PCtxtHandle phContext,
1063 ULONG ulAttribute,
void* pBuffer)
1066 return SEC_E_INVALID_HANDLE;
1069 return SEC_E_INSUFFICIENT_MEMORY;
1072 if (!check_context(context))
1073 return SEC_E_INVALID_HANDLE;
1075 switch (ulAttribute)
1077 case SECPKG_ATTR_AUTH_IDENTITY:
1082 return SEC_E_INTERNAL_ERROR;
1083 if (!identityToAuthIdentity(&credentials->identity, AuthIdentity))
1084 return SEC_E_INTERNAL_ERROR;
1085 context->UseSamFileDatabase = FALSE;
1088 case SECPKG_ATTR_SIZES:
1091 ContextSizes->cbMaxToken = 2010;
1092 ContextSizes->cbMaxSignature = 16;
1093 ContextSizes->cbBlockSize = 0;
1094 ContextSizes->cbSecurityTrailer = 16;
1098 case SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE:
1099 return ntlm_computeProofValue(context, (
SecBuffer*)pBuffer);
1101 case SECPKG_ATTR_AUTH_NTLM_RANDKEY:
1105 if (!sspi_SecBufferAlloc(randkey, 16))
1106 return (SEC_E_INSUFFICIENT_MEMORY);
1108 CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16);
1112 case SECPKG_ATTR_AUTH_NTLM_MIC:
1117 if (!sspi_SecBufferAlloc(mic, 16))
1118 return (SEC_E_INSUFFICIENT_MEMORY);
1120 CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16);
1124 case SECPKG_ATTR_AUTH_NTLM_MIC_VALUE:
1125 return ntlm_computeMicValue(context, (
SecBuffer*)pBuffer);
1128 WLog_ERR(TAG,
"TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute);
1129 return SEC_E_UNSUPPORTED_FUNCTION;
1135static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(
PCtxtHandle phContext,
1136 ULONG ulAttribute,
void* pBuffer)
1139 return SEC_E_INVALID_HANDLE;
1142 return SEC_E_INSUFFICIENT_MEMORY;
1145 if (!check_context(context))
1146 return SEC_E_INVALID_HANDLE;
1148 switch (ulAttribute)
1150 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1152 memcpy(pBuffer, context->Workstation.Buffer, context->Workstation.Length);
1155 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME:
1157 memcpy(pBuffer, context->NbDomainName.Buffer, context->NbDomainName.Length);
1160 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME:
1162 memcpy(pBuffer, context->NbComputerName.Buffer, context->NbComputerName.Length);
1165 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME:
1167 memcpy(pBuffer, context->DnsDomainName.Buffer, context->DnsDomainName.Length);
1170 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME:
1172 memcpy(pBuffer, context->DnsComputerName.Buffer, context->DnsComputerName.Length);
1176 case SECPKG_ATTR_PACKAGE_INFO:
1181 (
SecPkgInfoW*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
1184 return SEC_E_INSUFFICIENT_MEMORY;
1186 pPackageInfo->fCapabilities = NTLM_SecPkgInfoW.fCapabilities;
1187 pPackageInfo->wVersion = NTLM_SecPkgInfoW.wVersion;
1188 pPackageInfo->wRPCID = NTLM_SecPkgInfoW.wRPCID;
1189 pPackageInfo->cbMaxToken = NTLM_SecPkgInfoW.cbMaxToken;
1190 pPackageInfo->Name = _wcsdup(NTLM_SecPkgInfoW.Name);
1191 pPackageInfo->Comment = _wcsdup(NTLM_SecPkgInfoW.Comment);
1193 if (!pPackageInfo->Name || !pPackageInfo->Comment)
1195 sspi_ContextBufferFree(pPackageInfo);
1196 return SEC_E_INSUFFICIENT_MEMORY;
1198 PackageInfo->PackageInfo = pPackageInfo;
1202 return ntlm_QueryContextAttributesCommon(phContext, ulAttribute, pBuffer);
1207static SECURITY_STATUS utf8len(
const UNICODE_STRING* str,
void* pBuffer)
1210 WINPR_ASSERT(pBuffer);
1211 ULONG* val = (ULONG*)pBuffer;
1212 const size_t wlen = str->Length /
sizeof(WCHAR);
1213 const SSIZE_T rc = ConvertWCharNToUtf8(str->Buffer, wlen,
nullptr, 0);
1215 return SEC_E_INVALID_PARAMETER;
1216 *val = WINPR_ASSERTING_INT_CAST(ULONG, rc);
1221static SECURITY_STATUS utf8str(
const UNICODE_STRING* str,
void* pBuffer)
1224 WINPR_ASSERT(pBuffer);
1227 const SECURITY_STATUS status = utf8len(str, &len);
1228 if (status != SEC_E_OK)
1233 const size_t wlen = str->Length /
sizeof(WCHAR);
1234 const SSIZE_T rc = ConvertWCharNToUtf8(str->Buffer, wlen, pBuffer, (
size_t)len);
1235 return rc < 0 ? SEC_E_INVALID_PARAMETER : SEC_E_OK;
1239static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(
PCtxtHandle phContext,
1240 ULONG ulAttribute,
void* pBuffer)
1243 return SEC_E_INVALID_HANDLE;
1246 return SEC_E_INSUFFICIENT_MEMORY;
1250 switch (ulAttribute)
1252 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME_LEN:
1253 return utf8len(&context->Workstation, pBuffer);
1254 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME_LEN:
1255 return utf8len(&context->NbDomainName, pBuffer);
1256 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME_LEN:
1257 return utf8len(&context->NbComputerName, pBuffer);
1258 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME_LEN:
1259 return utf8len(&context->DnsDomainName, pBuffer);
1260 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME_LEN:
1261 return utf8len(&context->DnsComputerName, pBuffer);
1262 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1263 return utf8str(&context->Workstation, pBuffer);
1264 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME:
1265 return utf8str(&context->NbDomainName, pBuffer);
1266 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME:
1267 return utf8str(&context->NbComputerName, pBuffer);
1268 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME:
1269 return utf8str(&context->DnsDomainName, pBuffer);
1270 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME:
1271 return utf8str(&context->DnsComputerName, pBuffer);
1272 case SECPKG_ATTR_PACKAGE_INFO:
1277 (
SecPkgInfoA*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
1280 return SEC_E_INSUFFICIENT_MEMORY;
1282 pPackageInfo->fCapabilities = NTLM_SecPkgInfoA.fCapabilities;
1283 pPackageInfo->wVersion = NTLM_SecPkgInfoA.wVersion;
1284 pPackageInfo->wRPCID = NTLM_SecPkgInfoA.wRPCID;
1285 pPackageInfo->cbMaxToken = NTLM_SecPkgInfoA.cbMaxToken;
1286 pPackageInfo->Name = _strdup(NTLM_SecPkgInfoA.Name);
1287 pPackageInfo->Comment = _strdup(NTLM_SecPkgInfoA.Comment);
1289 if (!pPackageInfo->Name || !pPackageInfo->Comment)
1291 sspi_ContextBufferFree(pPackageInfo);
1292 return SEC_E_INSUFFICIENT_MEMORY;
1294 PackageInfo->PackageInfo = pPackageInfo;
1299 return ntlm_QueryContextAttributesCommon(phContext, ulAttribute, pBuffer);
1304static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesCommon(
PCtxtHandle phContext,
1305 ULONG ulAttribute,
void* pBuffer,
1309 return SEC_E_INVALID_HANDLE;
1312 return SEC_E_INVALID_PARAMETER;
1316 return SEC_E_INVALID_HANDLE;
1318 switch (ulAttribute)
1320 case SECPKG_ATTR_AUTH_NTLM_HASH:
1325 return SEC_E_INVALID_PARAMETER;
1327 if (AuthNtlmHash->Version == 1)
1328 CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16);
1329 else if (AuthNtlmHash->Version == 2)
1330 CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16);
1335 case SECPKG_ATTR_AUTH_NTLM_MESSAGE:
1341 return SEC_E_INVALID_PARAMETER;
1343 if (AuthNtlmMessage->type == 1)
1345 if (!ntlm_SecBufferRealloc(&context->NegotiateMessage, AuthNtlmMessage->length))
1346 return SEC_E_INSUFFICIENT_MEMORY;
1348 CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer,
1349 AuthNtlmMessage->length);
1351 else if (AuthNtlmMessage->type == 2)
1353 if (!ntlm_SecBufferRealloc(&context->ChallengeMessage, AuthNtlmMessage->length))
1354 return SEC_E_INSUFFICIENT_MEMORY;
1356 CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer,
1357 AuthNtlmMessage->length);
1359 else if (AuthNtlmMessage->type == 3)
1361 if (!ntlm_SecBufferRealloc(&context->AuthenticateMessage, AuthNtlmMessage->length))
1362 return SEC_E_INSUFFICIENT_MEMORY;
1364 CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer,
1365 AuthNtlmMessage->length);
1371 case SECPKG_ATTR_AUTH_NTLM_TIMESTAMP:
1377 return SEC_E_INVALID_PARAMETER;
1379 if (AuthNtlmTimestamp->ChallengeOrResponse)
1380 CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8);
1382 CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8);
1387 case SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE:
1393 return SEC_E_INVALID_PARAMETER;
1395 CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8);
1399 case SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE:
1405 return SEC_E_INVALID_PARAMETER;
1407 CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8);
1412 WLog_ERR(TAG,
"TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
1413 return SEC_E_UNSUPPORTED_FUNCTION;
1418static SECURITY_STATUS ntml_setUnicodeStringW(
UNICODE_STRING* str,
const WCHAR* val,
size_t bytelen)
1421 ntlm_free_unicode_string(str);
1422 *str = ntlm_from_unicode_string_w(val, bytelen /
sizeof(WCHAR));
1423 if (ntlm_is_unicode_string_empty(str))
1424 return SEC_E_INVALID_PARAMETER;
1429static SECURITY_STATUS utf16len(
const UNICODE_STRING* str,
void* pBuffer)
1432 WINPR_ASSERT(pBuffer);
1433 ULONG* val = (ULONG*)pBuffer;
1439static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(
PCtxtHandle phContext,
1440 ULONG ulAttribute,
void* pBuffer,
1444 return SEC_E_INVALID_HANDLE;
1447 return SEC_E_INVALID_PARAMETER;
1451 return SEC_E_INVALID_HANDLE;
1453 switch (ulAttribute)
1455 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME_LEN:
1456 return utf16len(&context->Workstation, pBuffer);
1457 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME_LEN:
1458 return utf16len(&context->NbDomainName, pBuffer);
1459 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME_LEN:
1460 return utf16len(&context->NbComputerName, pBuffer);
1461 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME_LEN:
1462 return utf16len(&context->DnsDomainName, pBuffer);
1463 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME_LEN:
1464 return utf16len(&context->DnsComputerName, pBuffer);
1465 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1466 return ntml_setUnicodeStringW(&context->Workstation, pBuffer, cbBuffer);
1467 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME:
1468 return ntml_setUnicodeStringW(&context->NbDomainName, pBuffer, cbBuffer);
1469 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME:
1470 return ntml_setUnicodeStringW(&context->NbComputerName, pBuffer, cbBuffer);
1471 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME:
1472 return ntml_setUnicodeStringW(&context->DnsDomainName, pBuffer, cbBuffer);
1473 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME:
1474 return ntml_setUnicodeStringW(&context->DnsComputerName, pBuffer, cbBuffer);
1477 return ntlm_SetContextAttributesCommon(phContext, ulAttribute, pBuffer, cbBuffer);
1481SECURITY_STATUS ntml_setUnicodeStringA(
UNICODE_STRING* str,
const char* val,
size_t charlen)
1484 ntlm_free_unicode_string(str);
1485 *str = ntlm_from_unicode_string_utf8(val, charlen);
1486 if (ntlm_is_unicode_string_empty(str))
1487 return SEC_E_INVALID_PARAMETER;
1492static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(
PCtxtHandle phContext,
1493 ULONG ulAttribute,
void* pBuffer,
1497 return SEC_E_INVALID_HANDLE;
1500 return SEC_E_INVALID_PARAMETER;
1504 return SEC_E_INVALID_HANDLE;
1506 switch (ulAttribute)
1508 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1509 return ntml_setUnicodeStringA(&context->Workstation, pBuffer, cbBuffer);
1510 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME:
1511 return ntml_setUnicodeStringA(&context->NbDomainName, pBuffer, cbBuffer);
1512 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME:
1513 return ntml_setUnicodeStringA(&context->NbComputerName, pBuffer, cbBuffer);
1514 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME:
1515 return ntml_setUnicodeStringA(&context->DnsDomainName, pBuffer, cbBuffer);
1516 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME:
1517 return ntml_setUnicodeStringA(&context->DnsComputerName, pBuffer, cbBuffer);
1519 return ntlm_SetContextAttributesCommon(phContext, ulAttribute, pBuffer, cbBuffer);
1524static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(
1525 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1526 WINPR_ATTR_UNUSED
void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1528 return SEC_E_UNSUPPORTED_FUNCTION;
1532static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(
1533 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1534 WINPR_ATTR_UNUSED
void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1536 return SEC_E_UNSUPPORTED_FUNCTION;
1540static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
1546static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(
PCtxtHandle phContext,
1547 WINPR_ATTR_UNUSED ULONG fQOP,
1550 const UINT32 SeqNo = MessageSeqNo;
1552 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1553 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1558 if (!check_context(context))
1559 return SEC_E_INVALID_HANDLE;
1561 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1563 SecBuffer* cur = &pMessage->pBuffers[index];
1565 if (cur->BufferType & SECBUFFER_DATA)
1567 else if (cur->BufferType & SECBUFFER_TOKEN)
1568 signature_buffer = cur;
1572 return SEC_E_INVALID_TOKEN;
1574 if (!signature_buffer)
1575 return SEC_E_INVALID_TOKEN;
1577 if (signature_buffer->cbBuffer < 16)
1578 return SEC_E_INSUFFICIENT_MEMORY;
1581 ULONG length = data_buffer->cbBuffer;
1582 void* data = malloc(length);
1585 return SEC_E_INSUFFICIENT_MEMORY;
1587 CopyMemory(data, data_buffer->pvBuffer, length);
1589 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1591 BOOL success = FALSE;
1595 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1598 winpr_Data_Write_UINT32(&value, SeqNo);
1600 if (!winpr_HMAC_Update(hmac, (
void*)&value, 4))
1602 if (!winpr_HMAC_Update(hmac, data, length))
1604 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1611 winpr_HMAC_Free(hmac);
1615 return SEC_E_INSUFFICIENT_MEMORY;
1619 if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
1621 if (context->confidentiality)
1623 if (!winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
1624 (BYTE*)data_buffer->pvBuffer))
1627 return SEC_E_INSUFFICIENT_MEMORY;
1631 CopyMemory(data_buffer->pvBuffer, data, length);
1634#ifdef WITH_DEBUG_NTLM
1635 WLog_DBG(TAG,
"Data Buffer (length = %" PRIu32
")", length);
1636 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1637 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1638 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1642 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1643 return SEC_E_INSUFFICIENT_MEMORY;
1644 if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
1646 BYTE* signature = signature_buffer->pvBuffer;
1648 winpr_Data_Write_UINT32(signature, version);
1649 CopyMemory(&signature[4], (
void*)checksum, 8);
1650 winpr_Data_Write_UINT32(&signature[12], SeqNo);
1652 context->SendSeqNum++;
1653#ifdef WITH_DEBUG_NTLM
1654 WLog_DBG(TAG,
"Signature (length = %" PRIu32
")", signature_buffer->cbBuffer);
1655 winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
1662 WINPR_ATTR_UNUSED PULONG pfQOP)
1664 const UINT32 SeqNo = (UINT32)MessageSeqNo;
1666 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1667 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1669 BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1673 if (!check_context(context))
1674 return SEC_E_INVALID_HANDLE;
1676 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1678 if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
1679 data_buffer = &pMessage->pBuffers[index];
1680 else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
1681 signature_buffer = &pMessage->pBuffers[index];
1685 return SEC_E_INVALID_TOKEN;
1687 if (!signature_buffer)
1688 return SEC_E_INVALID_TOKEN;
1690 if (signature_buffer->cbBuffer < 16)
1691 return SEC_E_INVALID_TOKEN;
1694 const ULONG length = data_buffer->cbBuffer;
1695 void* data = malloc(length);
1698 return SEC_E_INSUFFICIENT_MEMORY;
1700 CopyMemory(data, data_buffer->pvBuffer, length);
1704 if (context->confidentiality)
1706 if (!winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data,
1707 (BYTE*)data_buffer->pvBuffer))
1710 return SEC_E_INSUFFICIENT_MEMORY;
1714 CopyMemory(data_buffer->pvBuffer, data, length);
1717 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1719 BOOL success = FALSE;
1724 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1727 winpr_Data_Write_UINT32(&value, SeqNo);
1729 if (!winpr_HMAC_Update(hmac, (
void*)&value, 4))
1731 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1733 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1739 winpr_HMAC_Free(hmac);
1743 return SEC_E_INSUFFICIENT_MEMORY;
1746#ifdef WITH_DEBUG_NTLM
1747 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIu32
")", length);
1748 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1749 WLog_DBG(TAG,
"Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1750 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1754 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1755 return SEC_E_MESSAGE_ALTERED;
1758 winpr_Data_Write_UINT32(expected_signature, version);
1759 CopyMemory(&expected_signature[4], (
void*)checksum, 8);
1760 winpr_Data_Write_UINT32(&expected_signature[12], SeqNo);
1761 context->RecvSeqNum++;
1763 if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
1766 WLog_ERR(TAG,
"signature verification failed, something nasty is going on!");
1767#ifdef WITH_DEBUG_NTLM
1768 WLog_ERR(TAG,
"Expected Signature:");
1769 winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
1770 WLog_ERR(TAG,
"Actual Signature:");
1771 winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
1773 return SEC_E_MESSAGE_ALTERED;
1779static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(
PCtxtHandle phContext,
1780 WINPR_ATTR_UNUSED ULONG fQOP,
1783 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1787 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1788 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1790 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1791 if (!check_context(context))
1792 return SEC_E_INVALID_HANDLE;
1794 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1796 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1797 data_buffer = &pMessage->pBuffers[i];
1798 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1799 sig_buffer = &pMessage->pBuffers[i];
1802 if (!data_buffer || !sig_buffer)
1803 return SEC_E_INVALID_TOKEN;
1805 if (sig_buffer->cbBuffer < 16)
1806 return SEC_E_INSUFFICIENT_MEMORY;
1808 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1810 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1813 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1814 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1816 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1818 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1821 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1824 BYTE* signature = sig_buffer->pvBuffer;
1825 winpr_Data_Write_UINT32(signature, 1L);
1826 CopyMemory(&signature[4], checksum, 8);
1827 winpr_Data_Write_UINT32(&signature[12], seq_no);
1828 sig_buffer->cbBuffer = 16;
1833 winpr_HMAC_Free(hmac);
1838static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(
PCtxtHandle phContext,
1840 WINPR_ATTR_UNUSED PULONG pfQOP)
1842 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1846 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1847 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1848 BYTE signature[16] = WINPR_C_ARRAY_INIT;
1850 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1851 if (!check_context(context))
1852 return SEC_E_INVALID_HANDLE;
1854 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1856 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1857 data_buffer = &pMessage->pBuffers[i];
1858 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1859 sig_buffer = &pMessage->pBuffers[i];
1862 if (!data_buffer || !sig_buffer || (sig_buffer->cbBuffer < 16))
1863 return SEC_E_INVALID_TOKEN;
1865 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1867 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1870 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1871 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1873 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1875 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1878 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1881 winpr_Data_Write_UINT32(signature, 1L);
1882 CopyMemory(&signature[4], checksum, 8);
1883 winpr_Data_Write_UINT32(&signature[12], seq_no);
1886 if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
1887 status = SEC_E_MESSAGE_ALTERED;
1890 winpr_HMAC_Free(hmac);
1897 ntlm_QueryCredentialsAttributesA,
1898 ntlm_AcquireCredentialsHandleA,
1899 ntlm_FreeCredentialsHandle,
1901 ntlm_InitializeSecurityContextA,
1902 ntlm_AcceptSecurityContext,
1904 ntlm_DeleteSecurityContext,
1906 ntlm_QueryContextAttributesA,
1907 ntlm_ImpersonateSecurityContext,
1908 ntlm_RevertSecurityContext,
1910 ntlm_VerifySignature,
1920 ntlm_EncryptMessage,
1921 ntlm_DecryptMessage,
1922 ntlm_SetContextAttributesA,
1923 ntlm_SetCredentialsAttributesA,
1929 ntlm_QueryCredentialsAttributesW,
1930 ntlm_AcquireCredentialsHandleW,
1931 ntlm_FreeCredentialsHandle,
1933 ntlm_InitializeSecurityContextW,
1934 ntlm_AcceptSecurityContext,
1936 ntlm_DeleteSecurityContext,
1938 ntlm_QueryContextAttributesW,
1939 ntlm_ImpersonateSecurityContext,
1940 ntlm_RevertSecurityContext,
1942 ntlm_VerifySignature,
1952 ntlm_EncryptMessage,
1953 ntlm_DecryptMessage,
1954 ntlm_SetContextAttributesW,
1955 ntlm_SetCredentialsAttributesW,
1964 "NTLM Security Package"
1967static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = WINPR_C_ARRAY_INIT;
1968static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = WINPR_C_ARRAY_INIT;
1975 NTLM_SecPkgInfoW_NameBuffer,
1976 NTLM_SecPkgInfoW_CommentBuffer
1979char* ntlm_negotiate_flags_string(
char* buffer,
size_t size, UINT32 flags)
1981 if (!buffer || (size == 0))
1984 (void)_snprintf(buffer, size,
"[0x%08" PRIx32
"] ", flags);
1986 for (
int x = 0; x < 31; x++)
1988 const UINT32 mask = 1u << x;
1989 size_t len = strnlen(buffer, size);
1992 const char* str = ntlm_get_negotiate_string(mask);
1993 const size_t flen = strlen(str);
1995 if ((len > 0) && (buffer[len - 1] !=
' '))
1999 winpr_str_append(
"|", buffer, size,
nullptr);
2003 if (size - len < flen)
2005 winpr_str_append(str, buffer, size,
nullptr);
2012const char* ntlm_message_type_string(UINT32 messageType)
2014 switch (messageType)
2016 case MESSAGE_TYPE_NEGOTIATE:
2017 return "MESSAGE_TYPE_NEGOTIATE";
2018 case MESSAGE_TYPE_CHALLENGE:
2019 return "MESSAGE_TYPE_CHALLENGE";
2020 case MESSAGE_TYPE_AUTHENTICATE:
2021 return "MESSAGE_TYPE_AUTHENTICATE";
2023 return "MESSAGE_TYPE_UNKNOWN";
2027const char* ntlm_state_string(NTLM_STATE state)
2031 case NTLM_STATE_INITIAL:
2032 return "NTLM_STATE_INITIAL";
2033 case NTLM_STATE_NEGOTIATE:
2034 return "NTLM_STATE_NEGOTIATE";
2035 case NTLM_STATE_CHALLENGE:
2036 return "NTLM_STATE_CHALLENGE";
2037 case NTLM_STATE_AUTHENTICATE:
2038 return "NTLM_STATE_AUTHENTICATE";
2039 case NTLM_STATE_FINAL:
2040 return "NTLM_STATE_FINAL";
2042 return "NTLM_STATE_UNKNOWN";
2045void ntlm_change_state(
NTLM_CONTEXT* ntlm, NTLM_STATE state)
2048 WLog_DBG(TAG,
"change state from %s to %s", ntlm_state_string(ntlm->state),
2049 ntlm_state_string(state));
2050 ntlm->state = state;
2059BOOL ntlm_reset_cipher_state(
PSecHandle phContext)
2061 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
2065 if (!check_context(context))
2068 winpr_RC4_Free(context->SendRc4Seal);
2069 winpr_RC4_Free(context->RecvRc4Seal);
2070 context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
2071 context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
2073 if (!context->SendRc4Seal)
2075 WLog_ERR(TAG,
"Failed to allocate context->SendRc4Seal");
2078 if (!context->RecvRc4Seal)
2080 WLog_ERR(TAG,
"Failed to allocate context->RecvRc4Seal");
2090 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
2091 ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
2092 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
2093 ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));
2098BOOL ntlm_SecBufferRealloc(
SecBuffer* buffer, ULONG len)
2100 sspi_SecBufferFree(buffer);
2101 return sspi_SecBufferAlloc(buffer, len) !=
nullptr;