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#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
30#include <winpr/registry.h>
32#include <winpr/endian.h>
33#include <winpr/build-config.h>
34#include <winpr/path.h>
35#include <winpr/config-readers.h>
38#include "ntlm_export.h"
41#include "ntlm_message.h"
43#include "../../utils.h"
46#define TAG WINPR_TAG("sspi.NTLM")
49#define MIN(a, b) ((a) < (b)) ? (a) : (b)
52#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
53#define WINPR_KEY "Software\\%s\\WinPR\\NTLM"
56#define check_context(ctx) check_context_((ctx), __FILE__, __func__, __LINE__)
59static BOOL check_context_(
NTLM_CONTEXT* context,
const char* file,
const char* fkt,
size_t line)
62 wLog* log = WLog_Get(TAG);
63 const DWORD log_level = WLOG_ERROR;
67 if (WLog_IsLevelActive(log, log_level))
68 WLog_PrintTextMessage(log, log_level, line, file, fkt,
"invalid context");
73 if (!context->RecvRc4Seal)
75 if (WLog_IsLevelActive(log, log_level))
76 WLog_PrintTextMessage(log, log_level, line, file, fkt,
"invalid context->RecvRc4Seal");
79 if (!context->SendRc4Seal)
81 if (WLog_IsLevelActive(log, log_level))
82 WLog_PrintTextMessage(log, log_level, line, file, fkt,
"invalid context->SendRc4Seal");
86 if (!context->SendSigningKey)
88 if (WLog_IsLevelActive(log, log_level))
89 WLog_PrintTextMessage(log, log_level, line, file, fkt,
90 "invalid context->SendSigningKey");
93 if (!context->RecvSigningKey)
95 if (WLog_IsLevelActive(log, log_level))
96 WLog_PrintTextMessage(log, log_level, line, file, fkt,
97 "invalid context->RecvSigningKey");
100 if (!context->SendSealingKey)
102 if (WLog_IsLevelActive(log, log_level))
103 WLog_PrintTextMessage(log, log_level, line, file, fkt,
104 "invalid context->SendSealingKey");
107 if (!context->RecvSealingKey)
109 if (WLog_IsLevelActive(log, log_level))
110 WLog_PrintTextMessage(log, log_level, line, file, fkt,
111 "invalid context->RecvSealingKey");
117WINPR_ATTR_MALLOC(free, 1)
118static
char* get_computer_name(COMPUTER_NAME_FORMAT type,
size_t* pSize)
125 if (GetComputerNameExA(type,
nullptr, &nSize))
128 if (GetLastError() != ERROR_MORE_DATA)
131 char* computerName = calloc(1, nSize);
136 if (!GetComputerNameExA(type, computerName, &nSize))
148SECURITY_STATUS ntlm_SetContextWorkstationX(
NTLM_CONTEXT* context, BOOL unicode,
const void* data,
151 WINPR_ASSERT(context);
152 ntlm_free_unicode_string(&context->Workstation);
159 context->Workstation = ntlm_from_unicode_string_w(data, length /
sizeof(WCHAR));
161 context->Workstation = ntlm_from_unicode_string_utf8(data, length);
163 if (ntlm_is_unicode_string_empty(&context->Workstation))
164 return SEC_E_INSUFFICIENT_MEMORY;
170static int ntlm_SetContextWorkstation(
NTLM_CONTEXT* context,
const char* Workstation)
172 const char* ws = Workstation;
173 CHAR* computerName =
nullptr;
177 computerName = get_computer_name(ComputerNameNetBIOS,
nullptr);
183 const size_t len = strlen(ws);
184 const SECURITY_STATUS status = ntlm_SetContextWorkstationX(context, FALSE, ws, len);
187 return (status == SEC_E_OK) ? 1 : -1;
191static int ntlm_SetContextServicePrincipalNameW(
NTLM_CONTEXT* context, LPWSTR ServicePrincipalName)
193 WINPR_ASSERT(context);
195 ntlm_free_unicode_string(&context->ServicePrincipalName);
196 if (!ServicePrincipalName)
199 const size_t len = _wcslen(ServicePrincipalName);
200 context->ServicePrincipalName = ntlm_from_unicode_string_w(ServicePrincipalName, len);
201 if (ntlm_is_unicode_string_empty(&context->ServicePrincipalName))
208static int ntlm_SetContextTargetName(
NTLM_CONTEXT* context,
char* TargetName)
210 char* name = TargetName;
211 WINPR_ASSERT(context);
216 char* computerName = get_computer_name(ComputerNameNetBIOS, &nSize);
221 if (nSize > MAX_COMPUTERNAME_LENGTH)
222 computerName[MAX_COMPUTERNAME_LENGTH] =
'\0';
233 sspi_SecBufferFree(&context->TargetName);
234 context->TargetName.pvBuffer = ConvertUtf8ToWCharAlloc(name, &len);
236 if (!context->TargetName.pvBuffer || (len > UINT16_MAX /
sizeof(WCHAR)))
238 free(context->TargetName.pvBuffer);
239 context->TargetName.pvBuffer =
nullptr;
247 context->TargetName.cbBuffer = (USHORT)(len *
sizeof(WCHAR));
260 winpr_RC4_Free(context->SendRc4Seal);
261 winpr_RC4_Free(context->RecvRc4Seal);
262 sspi_SecBufferFree(&context->NegotiateMessage);
263 sspi_SecBufferFree(&context->ChallengeMessage);
264 sspi_SecBufferFree(&context->AuthenticateMessage);
265 sspi_SecBufferFree(&context->ChallengeTargetInfo);
266 sspi_SecBufferFree(&context->AuthenticateTargetInfo);
267 sspi_SecBufferFree(&context->TargetName);
268 sspi_SecBufferFree(&context->NtChallengeResponse);
269 sspi_SecBufferFree(&context->LmChallengeResponse);
270 ntlm_free_unicode_string(&context->ServicePrincipalName);
271 ntlm_free_unicode_string(&context->Workstation);
272 ntlm_free_unicode_string(&context->NbComputerName);
273 ntlm_free_unicode_string(&context->NbDomainName);
274 ntlm_free_unicode_string(&context->DnsComputerName);
275 ntlm_free_unicode_string(&context->DnsDomainName);
277 ntlm_free_messages(context);
280 memset(context->NtlmHash, 0,
sizeof(context->NtlmHash));
281 memset(context->NtlmV2Hash, 0,
sizeof(context->NtlmV2Hash));
282 memset(context->SessionBaseKey, 0,
sizeof(context->SessionBaseKey));
283 memset(context->KeyExchangeKey, 0,
sizeof(context->KeyExchangeKey));
284 memset(context->RandomSessionKey, 0,
sizeof(context->RandomSessionKey));
285 memset(context->ExportedSessionKey, 0,
sizeof(context->ExportedSessionKey));
286 memset(context->EncryptedRandomSessionKey, 0,
sizeof(context->EncryptedRandomSessionKey));
287 memset(context->NtProofString, 0,
sizeof(context->NtProofString));
293 WINPR_ATTR_UNUSED COMPUTER_NAME_FORMAT type)
296 ntlm_free_unicode_string(pName);
299 char* name = get_computer_name(ComputerNameNetBIOS, &len);
305 *pName = ntlm_from_unicode_string_utf8(name, len);
308 return !ntlm_is_unicode_string_empty(pName);
312static BOOL ntlm_ContextFillDefaultNames(
NTLM_CONTEXT* context)
314 WINPR_ASSERT(context);
316 if (ntlm_SetContextWorkstation(context,
nullptr) < 0)
319 if (ntlm_get_target_computer_name(&context->NbDomainName, ComputerNameNetBIOS) < 0)
322 if (ntlm_get_target_computer_name(&context->NbComputerName, ComputerNameNetBIOS) < 0)
325 if (ntlm_get_target_computer_name(&context->DnsDomainName, ComputerNameDnsDomain) < 0)
328 if (ntlm_get_target_computer_name(&context->DnsComputerName, ComputerNameDnsHostname) < 0)
333#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
334static BOOL ntlm_try_set_from_registry(HKEY hKey,
const char* key,
UNICODE_STRING* ustr)
341 WCHAR wkey[64] = WINPR_C_ARRAY_INIT;
342 const SSIZE_T res = ConvertUtf8ToWChar(key, wkey, ARRAYSIZE(wkey));
345 WINPR_ASSERT((
size_t)res < ARRAYSIZE(wkey));
349 if (RegQueryValueExW(hKey, wkey,
nullptr, &dwType,
nullptr, &dwSize) != ERROR_SUCCESS)
352 if ((dwSize > UINT16_MAX) || ((dwSize % 2) != 0))
355 str.Buffer = calloc(dwSize /
sizeof(WCHAR) + 1,
sizeof(WCHAR));
358 str.Length = WINPR_ASSERTING_INT_CAST(UINT16, dwSize);
359 str.MaximumLength = WINPR_ASSERTING_INT_CAST(UINT16, dwSize);
361 const LONG rc = RegQueryValueExW(hKey, wkey,
nullptr, &dwType, (BYTE*)str.Buffer, &dwSize);
362 if (rc != ERROR_SUCCESS)
364 ntlm_free_unicode_string(ustr);
369 ntlm_free_unicode_string(&str);
375static SECURITY_STATUS ntml_setUnicodeStringA(
UNICODE_STRING* str,
const char* val,
size_t charlen);
377static void apply_string(
const char* config, WINPR_JSON* json,
const char* name,
383 if (!winpr_config_apply_string(config, json, name, &str))
386 const BOOL rc = ntml_setUnicodeStringA(dst, str, strlen(str));
389 WLog_WARN(TAG,
"[%s] Invalid setting %s: must be of type string", config, name);
393static BOOL ntlm_ContextFromConfig(
NTLM_CONTEXT* context)
395 const char config[] =
"ntlm.json";
397 WINPR_ASSERT(context);
399#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
401 char* key = winpr_getApplicatonDetailsRegKey(WINPR_KEY);
407 RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
410 if (status == ERROR_SUCCESS)
413 "HKLM.reg is deprecated since 3.33.0. ATTENTION: Use "
421 if (RegQueryValueEx(hKey, _T(
"NTLMv2"),
nullptr, &dwType, (BYTE*)&dwValue,
422 &dwSize) == ERROR_SUCCESS)
423 context->NTLMv2 = dwValue ? 1 : 0;
425 if (RegQueryValueEx(hKey, _T(
"UseMIC"),
nullptr, &dwType, (BYTE*)&dwValue,
426 &dwSize) == ERROR_SUCCESS)
427 context->UseMIC = dwValue ? 1 : 0;
429 if (RegQueryValueEx(hKey, _T(
"SendVersionInfo"),
nullptr, &dwType, (BYTE*)&dwValue,
430 &dwSize) == ERROR_SUCCESS)
431 context->SendVersionInfo = dwValue ? 1 : 0;
433 if (RegQueryValueEx(hKey, _T(
"SendSingleHostData"),
nullptr, &dwType,
434 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
435 context->SendSingleHostData = dwValue ? 1 : 0;
437 if (RegQueryValueEx(hKey, _T(
"SendWorkstationName"),
nullptr, &dwType,
438 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
439 context->SendWorkstationName = dwValue ? 1 : 0;
441 (void)ntlm_try_set_from_registry(hKey,
"WorkstationName", &context->Workstation);
442 (void)ntlm_try_set_from_registry(hKey,
"NbDomainName", &context->NbDomainName);
443 (void)ntlm_try_set_from_registry(hKey,
"NbComputerName", &context->NbComputerName);
444 (void)ntlm_try_set_from_registry(hKey,
"DnsDomainName", &context->DnsDomainName);
445 (void)ntlm_try_set_from_registry(hKey,
"DnsComputerName",
446 &context->DnsComputerName);
454 RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(
"System\\CurrentControlSet\\Control\\LSA"), 0,
455 KEY_READ | KEY_WOW64_64KEY, &hKey);
457 if (status == ERROR_SUCCESS)
459 WLog_WARN(TAG,
"HKLM.reg is deprecated since 3.33.0. ATTENTION: Use %s instead!", config);
464 if (RegQueryValueEx(hKey, _T(
"SuppressExtendedProtection"),
nullptr, &dwType,
465 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
466 context->SuppressExtendedProtection = dwValue != 0;
472 WINPR_JSON* json = winpr_GetJSONConfigFile(TRUE, config);
475 winpr_config_apply_bool(config, json,
"NTLMv2", &context->NTLMv2);
476 winpr_config_apply_bool(config, json,
"UseMIC", &context->UseMIC);
477 winpr_config_apply_bool(config, json,
"SendVersionInfo", &context->SendVersionInfo);
478 winpr_config_apply_bool(config, json,
"SendSingleHostData",
479 &context->SendSingleHostData);
480 winpr_config_apply_bool(config, json,
"SendWorkstationName",
481 &context->SendWorkstationName);
482 winpr_config_apply_bool(config, json,
"SuppressExtendedProtection",
483 &context->SuppressExtendedProtection);
485 apply_string(config, json,
"WorkstationName", &context->Workstation);
486 apply_string(config, json,
"NbDomainName", &context->NbDomainName);
487 apply_string(config, json,
"NbComputerName", &context->NbComputerName);
488 apply_string(config, json,
"DnsDomainName", &context->DnsDomainName);
489 apply_string(config, json,
"DnsComputerName", &context->DnsComputerName);
499 context->SuppressExtendedProtection = FALSE;
503WINPR_ATTR_MALLOC(ntlm_ContextFree, 1)
511 context->NTLMv2 = TRUE;
512 context->UseMIC = FALSE;
513 context->SendVersionInfo = TRUE;
514 context->SendSingleHostData = FALSE;
515 context->SendWorkstationName = TRUE;
516 context->NegotiateKeyExchange = TRUE;
517 context->UseSamFileDatabase = TRUE;
519 context->NegotiateFlags = 0;
520 context->LmCompatibilityLevel = 3;
521 ntlm_change_state(context, NTLM_STATE_INITIAL);
522 FillMemory(context->MachineID,
sizeof(context->MachineID), 0xAA);
525 context->UseMIC = TRUE;
527 if (!ntlm_ContextFillDefaultNames(context))
529 if (!ntlm_ContextFromConfig(context))
535 ntlm_ContextFree(context);
540static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
541 WINPR_ATTR_UNUSED SEC_WCHAR* pszPrincipal, WINPR_ATTR_UNUSED SEC_WCHAR* pszPackage,
542 ULONG fCredentialUse, WINPR_ATTR_UNUSED
void* pvLogonID,
void* pAuthData,
543 SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
546 if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) &&
547 (fCredentialUse != SECPKG_CRED_BOTH))
549 return SEC_E_INVALID_PARAMETER;
555 return SEC_E_INTERNAL_ERROR;
557 credentials->fCredentialUse = fCredentialUse;
558 credentials->pGetKeyFn = pGetKeyFn;
559 credentials->pvGetKeyArgument = pvGetKeyArgument;
561#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
564 SEC_WINPR_NTLM_SETTINGS_V2* settingsV2 =
nullptr;
567 UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData);
569 if (sspi_CopyAuthIdentity(&(credentials->identity),
572 sspi_CredentialsFree(credentials);
573 return SEC_E_INVALID_PARAMETER;
576#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
577 if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED)
581 if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED_v2)
583 const SEC_WINNT_AUTH_IDENTITY_WINPR_V2* auth =
584 (
const SEC_WINNT_AUTH_IDENTITY_WINPR_V2*)pAuthData;
586 if (auth->version < SEC_WINNT_AUTH_IDENTITY_WINPR_V2_REVISION_1)
587 return SEC_E_INVALID_PARAMETER;
588 settingsV2 = auth->ntlmSettingsV2;
592#if !defined(WITHOUT_WINPR_3x_DEPRECATED)
597 if (!sspi_CloneSecSettingsString(&credentials->ntlmSettingsV2->samFile,
600 sspi_CredentialsFree(credentials);
601 return SEC_E_INSUFFICIENT_MEMORY;
604 credentials->ntlmSettingsV2->hashCallback = settingsV1->
hashCallback;
605 credentials->ntlmSettingsV2->hashCallbackArg = settingsV1->
hashCallbackArg;
611 sspi_FreeSecNtlmSettings(credentials->ntlmSettingsV2);
612 credentials->ntlmSettingsV2 = sspi_CloneSecNtlmSettings(settingsV2);
613 if (!credentials->ntlmSettingsV2)
615 sspi_CredentialsFree(credentials);
616 return SEC_E_INVALID_PARAMETER;
620 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)credentials);
621 sspi_SecureHandleSetPackageId(phCredential, SSPI_PACKAGE_NTLM);
626static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
627 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
628 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
631 SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
632 SEC_WCHAR* principal =
nullptr;
633 SEC_WCHAR*
package = nullptr;
637 principal = ConvertUtf8ToWCharAlloc(pszPrincipal,
nullptr);
643 package = ConvertUtf8ToWCharAlloc(pszPackage, nullptr);
649 ntlm_AcquireCredentialsHandleW(principal, package, fCredentialUse, pvLogonID, pAuthData,
650 pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
660static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(
PCredHandle phCredential)
663 return SEC_E_INVALID_HANDLE;
667 sspi_SecureHandleInvalidate(phCredential);
669 return SEC_E_INVALID_HANDLE;
671 sspi_CredentialsFree(credentials);
676static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(
677 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
678 WINPR_ATTR_UNUSED
void* pBuffer)
680 if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
685 WLog_ERR(TAG,
"TODO: Implement");
686 return SEC_E_UNSUPPORTED_FUNCTION;
690static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(
PCredHandle phCredential,
691 ULONG ulAttribute,
void* pBuffer)
693 return ntlm_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
700static SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(
703 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED
PTimeStamp ptsTimeStamp)
705 SECURITY_STATUS status = 0;
711 if (phContext && !phContext->dwLower && !phContext->dwUpper)
712 return SEC_E_INVALID_HANDLE;
718 context = ntlm_ContextNew();
721 return SEC_E_INSUFFICIENT_MEMORY;
723 context->server = TRUE;
725 if (fContextReq & ASC_REQ_CONFIDENTIALITY)
726 context->confidentiality = TRUE;
728 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
729 context->credentials = credentials;
730 context->SamFile = credentials->ntlmSettingsV2->samFile;
731 context->HashCallback = credentials->ntlmSettingsV2->hashCallback;
732 context->HashCallbackArg = credentials->ntlmSettingsV2->hashCallbackArg;
734 if (credentials->ntlmSettingsV2->dnsComputerName)
736 const SECURITY_STATUS rc = ntml_setUnicodeStringA(
737 &context->DnsComputerName, credentials->ntlmSettingsV2->dnsComputerName,
738 strlen(credentials->ntlmSettingsV2->dnsComputerName));
743 if (credentials->ntlmSettingsV2->dnsDomainName)
745 const SECURITY_STATUS rc = ntml_setUnicodeStringA(
746 &context->DnsDomainName, credentials->ntlmSettingsV2->dnsDomainName,
747 strlen(credentials->ntlmSettingsV2->dnsDomainName));
752 if (credentials->ntlmSettingsV2->netBiosComputerName)
754 const SECURITY_STATUS rc = ntml_setUnicodeStringA(
755 &context->NbComputerName, credentials->ntlmSettingsV2->netBiosComputerName,
756 strlen(credentials->ntlmSettingsV2->netBiosComputerName));
761 if (credentials->ntlmSettingsV2->netBiosDomainName)
763 const SECURITY_STATUS rc = ntml_setUnicodeStringA(
764 &context->NbDomainName, credentials->ntlmSettingsV2->netBiosDomainName,
765 strlen(credentials->ntlmSettingsV2->netBiosDomainName));
770 if (!ntlm_SetContextTargetName(context, credentials->ntlmSettingsV2->targetName))
771 return SEC_E_INVALID_HANDLE;
772 sspi_SecureHandleSetLowerPointer(phNewContext, context);
773 sspi_SecureHandleSetPackageId(phNewContext, SSPI_PACKAGE_NTLM);
776 switch (ntlm_get_state(context))
778 case NTLM_STATE_INITIAL:
780 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
783 return SEC_E_INVALID_TOKEN;
785 if (pInput->cBuffers < 1)
786 return SEC_E_INVALID_TOKEN;
788 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
791 return SEC_E_INVALID_TOKEN;
793 if (input_buffer->cbBuffer < 1)
794 return SEC_E_INVALID_TOKEN;
796 status = ntlm_read_NegotiateMessage(context, input_buffer);
797 if (status != SEC_I_CONTINUE_NEEDED)
800 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
803 return SEC_E_INVALID_TOKEN;
805 if (pOutput->cBuffers < 1)
806 return SEC_E_INVALID_TOKEN;
808 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
810 if (!output_buffer->BufferType)
811 return SEC_E_INVALID_TOKEN;
813 if (output_buffer->cbBuffer < 1)
814 return SEC_E_INSUFFICIENT_MEMORY;
816 return ntlm_write_ChallengeMessage(context, output_buffer);
819 return SEC_E_OUT_OF_SEQUENCE;
822 case NTLM_STATE_AUTHENTICATE:
825 return SEC_E_INVALID_TOKEN;
827 if (pInput->cBuffers < 1)
828 return SEC_E_INVALID_TOKEN;
830 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
833 return SEC_E_INVALID_TOKEN;
835 if (input_buffer->cbBuffer < 1)
836 return SEC_E_INVALID_TOKEN;
838 status = ntlm_read_AuthenticateMessage(context, input_buffer);
842 for (ULONG i = 0; i < pOutput->cBuffers; i++)
844 pOutput->pBuffers[i].cbBuffer = 0;
845 pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN;
853 return SEC_E_OUT_OF_SEQUENCE;
858static SECURITY_STATUS SEC_ENTRY
859ntlm_ImpersonateSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
865static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
867 WINPR_ATTR_UNUSED ULONG Reserved1, WINPR_ATTR_UNUSED ULONG TargetDataRep,
PSecBufferDesc pInput,
869 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED
PTimeStamp ptsExpiry)
871 SECURITY_STATUS status = 0;
877 if (phContext && !phContext->dwLower && !phContext->dwUpper)
878 return SEC_E_INVALID_HANDLE;
884 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
889 context = ntlm_ContextNew();
892 return SEC_E_INSUFFICIENT_MEMORY;
894 if (fContextReq & ISC_REQ_CONFIDENTIALITY)
895 context->confidentiality = TRUE;
897 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
898 context->credentials = credentials;
900 if (ntlm_SetContextServicePrincipalNameW(context, pszTargetName) < 0)
902 ntlm_ContextFree(context);
903 return SEC_E_INTERNAL_ERROR;
906 sspi_SecureHandleSetLowerPointer(phNewContext, context);
907 sspi_SecureHandleSetPackageId(phNewContext, SSPI_PACKAGE_NTLM);
910 if ((!input_buffer) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE))
913 return SEC_E_INVALID_TOKEN;
915 if (pOutput->cBuffers < 1)
916 return SEC_E_INVALID_TOKEN;
918 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
921 return SEC_E_INVALID_TOKEN;
923 if (output_buffer->cbBuffer < 1)
924 return SEC_E_INVALID_TOKEN;
926 if (ntlm_get_state(context) == NTLM_STATE_INITIAL)
927 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
929 if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE)
930 return ntlm_write_NegotiateMessage(context, output_buffer);
932 return SEC_E_OUT_OF_SEQUENCE;
937 return SEC_E_INVALID_TOKEN;
939 if (input_buffer->cbBuffer < 1)
940 return SEC_E_INVALID_TOKEN;
942 PSecBuffer channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
944 if (channel_bindings)
946 context->Bindings.BindingsLength = channel_bindings->cbBuffer;
950 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
952 status = ntlm_read_ChallengeMessage(context, input_buffer);
954 if (status != SEC_I_CONTINUE_NEEDED)
958 return SEC_E_INVALID_TOKEN;
960 if (pOutput->cBuffers < 1)
961 return SEC_E_INVALID_TOKEN;
963 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
966 return SEC_E_INVALID_TOKEN;
968 if (output_buffer->cbBuffer < 1)
969 return SEC_E_INSUFFICIENT_MEMORY;
971 if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)
972 return ntlm_write_AuthenticateMessage(context, output_buffer);
975 return SEC_E_OUT_OF_SEQUENCE;
978 return SEC_E_OUT_OF_SEQUENCE;
985static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
987 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
990 SECURITY_STATUS status = 0;
991 SEC_WCHAR* pszTargetNameW =
nullptr;
995 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName,
nullptr);
997 return SEC_E_INTERNAL_ERROR;
1000 status = ntlm_InitializeSecurityContextW(phCredential, phContext, pszTargetNameW, fContextReq,
1001 Reserved1, TargetDataRep, pInput, Reserved2,
1002 phNewContext, pOutput, pfContextAttr, ptsExpiry);
1003 free(pszTargetNameW);
1009static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(
PCtxtHandle phContext)
1012 sspi_SecureHandleInvalidate(phContext);
1013 ntlm_ContextFree(context);
1019 BYTE* blob =
nullptr;
1023 WINPR_ASSERT(ntproof);
1025 target = &ntlm->ChallengeTargetInfo;
1027 if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer))
1028 return SEC_E_INSUFFICIENT_MEMORY;
1030 blob = (BYTE*)ntproof->pvBuffer;
1031 CopyMemory(blob, ntlm->ServerChallenge, 8);
1035 CopyMemory(&blob[16], ntlm->Timestamp, 8);
1036 CopyMemory(&blob[24], ntlm->ClientChallenge, 8);
1039 CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer);
1046 WINPR_ASSERT(micvalue);
1048 const UINT64 msgSize = 1ull * ntlm->NegotiateMessage.cbBuffer +
1049 ntlm->ChallengeMessage.cbBuffer + ntlm->AuthenticateMessage.cbBuffer;
1050 if (msgSize > UINT32_MAX)
1051 return SEC_E_INSUFFICIENT_MEMORY;
1053 if (!sspi_SecBufferAlloc(micvalue, WINPR_ASSERTING_INT_CAST(ULONG, msgSize)))
1054 return SEC_E_INSUFFICIENT_MEMORY;
1056 if (micvalue->cbBuffer < msgSize)
1057 return SEC_E_INVALID_TOKEN;
1058 if (ntlm->AuthenticateMessage.cbBuffer < ntlm->MessageIntegrityCheckOffset + 16ull)
1059 return SEC_E_INVALID_TOKEN;
1061 BYTE* blob = (BYTE*)micvalue->pvBuffer;
1063 return SEC_E_INVALID_TOKEN;
1065 CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer);
1066 blob += ntlm->NegotiateMessage.cbBuffer;
1067 CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer);
1068 blob += ntlm->ChallengeMessage.cbBuffer;
1069 CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer);
1070 blob += ntlm->MessageIntegrityCheckOffset;
1071 ZeroMemory(blob, 16);
1076static bool identityToAuthIdentity(
const SEC_WINNT_AUTH_IDENTITY* identity,
1079 WINPR_ASSERT(identity);
1085 *pAuthIdentity = empty;
1087 if ((identity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0)
1089 if (identity->UserLength > 0)
1091 if (ConvertWCharNToUtf8(identity->User, identity->UserLength, pAuthIdentity->User,
1092 ARRAYSIZE(pAuthIdentity->User)) <= 0)
1096 if (identity->DomainLength > 0)
1098 if (ConvertWCharNToUtf8(identity->Domain, identity->DomainLength, pAuthIdentity->Domain,
1099 ARRAYSIZE(pAuthIdentity->Domain)) <= 0)
1103 else if ((identity->Flags & SEC_WINNT_AUTH_IDENTITY_ANSI) != 0)
1105 if (identity->UserLength > 0)
1107 const size_t len = MIN(ARRAYSIZE(pAuthIdentity->User) - 1, identity->UserLength);
1108 strncpy(pAuthIdentity->User, (
char*)identity->User, len);
1109 pAuthIdentity->User[len] =
'\0';
1112 if (identity->DomainLength > 0)
1114 const size_t len = MIN(ARRAYSIZE(pAuthIdentity->Domain) - 1, identity->DomainLength);
1115 strncpy(pAuthIdentity->Domain, (
char*)identity->Domain, len);
1116 pAuthIdentity->Domain[len] =
'\0';
1125static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesCommon(
PCtxtHandle phContext,
1126 ULONG ulAttribute,
void* pBuffer)
1129 return SEC_E_INVALID_HANDLE;
1132 return SEC_E_INSUFFICIENT_MEMORY;
1135 if (!check_context(context))
1136 return SEC_E_INVALID_HANDLE;
1138 switch (ulAttribute)
1140 case SECPKG_ATTR_AUTH_IDENTITY:
1145 return SEC_E_INTERNAL_ERROR;
1146 if (!identityToAuthIdentity(&credentials->identity, AuthIdentity))
1147 return SEC_E_INTERNAL_ERROR;
1148 context->UseSamFileDatabase = FALSE;
1151 case SECPKG_ATTR_SIZES:
1154 ContextSizes->cbMaxToken = 2010;
1155 ContextSizes->cbMaxSignature = 16;
1156 ContextSizes->cbBlockSize = 0;
1157 ContextSizes->cbSecurityTrailer = 16;
1161 case SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE:
1162 return ntlm_computeProofValue(context, (
SecBuffer*)pBuffer);
1164 case SECPKG_ATTR_AUTH_NTLM_RANDKEY:
1168 if (!sspi_SecBufferAlloc(randkey, 16))
1169 return (SEC_E_INSUFFICIENT_MEMORY);
1171 CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16);
1175 case SECPKG_ATTR_AUTH_NTLM_MIC:
1180 if (!sspi_SecBufferAlloc(mic, 16))
1181 return (SEC_E_INSUFFICIENT_MEMORY);
1183 CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16);
1187 case SECPKG_ATTR_AUTH_NTLM_MIC_VALUE:
1188 return ntlm_computeMicValue(context, (
SecBuffer*)pBuffer);
1191 WLog_ERR(TAG,
"TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute);
1192 return SEC_E_UNSUPPORTED_FUNCTION;
1198static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(
PCtxtHandle phContext,
1199 ULONG ulAttribute,
void* pBuffer)
1202 return SEC_E_INVALID_HANDLE;
1205 return SEC_E_INSUFFICIENT_MEMORY;
1208 if (!check_context(context))
1209 return SEC_E_INVALID_HANDLE;
1211 switch (ulAttribute)
1213 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1215 memcpy(pBuffer, context->Workstation.Buffer, context->Workstation.Length);
1218 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME:
1220 memcpy(pBuffer, context->NbDomainName.Buffer, context->NbDomainName.Length);
1223 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME:
1225 memcpy(pBuffer, context->NbComputerName.Buffer, context->NbComputerName.Length);
1228 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME:
1230 memcpy(pBuffer, context->DnsDomainName.Buffer, context->DnsDomainName.Length);
1233 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME:
1235 memcpy(pBuffer, context->DnsComputerName.Buffer, context->DnsComputerName.Length);
1239 case SECPKG_ATTR_PACKAGE_INFO:
1244 (
SecPkgInfoW*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
1247 return SEC_E_INSUFFICIENT_MEMORY;
1249 pPackageInfo->fCapabilities = NTLM_SecPkgInfoW.fCapabilities;
1250 pPackageInfo->wVersion = NTLM_SecPkgInfoW.wVersion;
1251 pPackageInfo->wRPCID = NTLM_SecPkgInfoW.wRPCID;
1252 pPackageInfo->cbMaxToken = NTLM_SecPkgInfoW.cbMaxToken;
1253 pPackageInfo->Name = _wcsdup(NTLM_SecPkgInfoW.Name);
1254 pPackageInfo->Comment = _wcsdup(NTLM_SecPkgInfoW.Comment);
1256 if (!pPackageInfo->Name || !pPackageInfo->Comment)
1258 sspi_ContextBufferFree(pPackageInfo);
1259 return SEC_E_INSUFFICIENT_MEMORY;
1261 PackageInfo->PackageInfo = pPackageInfo;
1265 return ntlm_QueryContextAttributesCommon(phContext, ulAttribute, pBuffer);
1270static SECURITY_STATUS utf8len(
const UNICODE_STRING* str,
void* pBuffer)
1273 WINPR_ASSERT(pBuffer);
1274 ULONG* val = (ULONG*)pBuffer;
1275 const size_t wlen = str->Length /
sizeof(WCHAR);
1276 const SSIZE_T rc = ConvertWCharNToUtf8(str->Buffer, wlen,
nullptr, 0);
1278 return SEC_E_INVALID_PARAMETER;
1279 *val = WINPR_ASSERTING_INT_CAST(ULONG, rc);
1284static SECURITY_STATUS utf8str(
const UNICODE_STRING* str,
void* pBuffer)
1287 WINPR_ASSERT(pBuffer);
1290 const SECURITY_STATUS status = utf8len(str, &len);
1291 if (status != SEC_E_OK)
1296 const size_t wlen = str->Length /
sizeof(WCHAR);
1297 const SSIZE_T rc = ConvertWCharNToUtf8(str->Buffer, wlen, pBuffer, (
size_t)len);
1298 return rc < 0 ? SEC_E_INVALID_PARAMETER : SEC_E_OK;
1302static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(
PCtxtHandle phContext,
1303 ULONG ulAttribute,
void* pBuffer)
1306 return SEC_E_INVALID_HANDLE;
1309 return SEC_E_INSUFFICIENT_MEMORY;
1313 switch (ulAttribute)
1315 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME_LEN:
1316 return utf8len(&context->Workstation, pBuffer);
1317 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME_LEN:
1318 return utf8len(&context->NbDomainName, pBuffer);
1319 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME_LEN:
1320 return utf8len(&context->NbComputerName, pBuffer);
1321 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME_LEN:
1322 return utf8len(&context->DnsDomainName, pBuffer);
1323 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME_LEN:
1324 return utf8len(&context->DnsComputerName, pBuffer);
1325 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1326 return utf8str(&context->Workstation, pBuffer);
1327 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME:
1328 return utf8str(&context->NbDomainName, pBuffer);
1329 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME:
1330 return utf8str(&context->NbComputerName, pBuffer);
1331 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME:
1332 return utf8str(&context->DnsDomainName, pBuffer);
1333 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME:
1334 return utf8str(&context->DnsComputerName, pBuffer);
1335 case SECPKG_ATTR_PACKAGE_INFO:
1340 (
SecPkgInfoA*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
1343 return SEC_E_INSUFFICIENT_MEMORY;
1345 pPackageInfo->fCapabilities = NTLM_SecPkgInfoA.fCapabilities;
1346 pPackageInfo->wVersion = NTLM_SecPkgInfoA.wVersion;
1347 pPackageInfo->wRPCID = NTLM_SecPkgInfoA.wRPCID;
1348 pPackageInfo->cbMaxToken = NTLM_SecPkgInfoA.cbMaxToken;
1349 pPackageInfo->Name = _strdup(NTLM_SecPkgInfoA.Name);
1350 pPackageInfo->Comment = _strdup(NTLM_SecPkgInfoA.Comment);
1352 if (!pPackageInfo->Name || !pPackageInfo->Comment)
1354 sspi_ContextBufferFree(pPackageInfo);
1355 return SEC_E_INSUFFICIENT_MEMORY;
1357 PackageInfo->PackageInfo = pPackageInfo;
1362 return ntlm_QueryContextAttributesCommon(phContext, ulAttribute, pBuffer);
1367static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesCommon(
PCtxtHandle phContext,
1368 ULONG ulAttribute,
void* pBuffer,
1372 return SEC_E_INVALID_HANDLE;
1375 return SEC_E_INVALID_PARAMETER;
1379 return SEC_E_INVALID_HANDLE;
1381 switch (ulAttribute)
1383 case SECPKG_ATTR_AUTH_NTLM_HASH:
1388 return SEC_E_INVALID_PARAMETER;
1390 if (AuthNtlmHash->Version == 1)
1391 CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16);
1392 else if (AuthNtlmHash->Version == 2)
1393 CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16);
1398 case SECPKG_ATTR_AUTH_NTLM_MESSAGE:
1404 return SEC_E_INVALID_PARAMETER;
1406 if (AuthNtlmMessage->type == 1)
1408 if (!ntlm_SecBufferRealloc(&context->NegotiateMessage, AuthNtlmMessage->length))
1409 return SEC_E_INSUFFICIENT_MEMORY;
1411 CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer,
1412 AuthNtlmMessage->length);
1414 else if (AuthNtlmMessage->type == 2)
1416 if (!ntlm_SecBufferRealloc(&context->ChallengeMessage, AuthNtlmMessage->length))
1417 return SEC_E_INSUFFICIENT_MEMORY;
1419 CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer,
1420 AuthNtlmMessage->length);
1422 else if (AuthNtlmMessage->type == 3)
1424 if (!ntlm_SecBufferRealloc(&context->AuthenticateMessage, AuthNtlmMessage->length))
1425 return SEC_E_INSUFFICIENT_MEMORY;
1427 CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer,
1428 AuthNtlmMessage->length);
1434 case SECPKG_ATTR_AUTH_NTLM_TIMESTAMP:
1440 return SEC_E_INVALID_PARAMETER;
1442 if (AuthNtlmTimestamp->ChallengeOrResponse)
1443 CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8);
1445 CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8);
1450 case SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE:
1456 return SEC_E_INVALID_PARAMETER;
1458 CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8);
1462 case SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE:
1468 return SEC_E_INVALID_PARAMETER;
1470 CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8);
1475 WLog_ERR(TAG,
"TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
1476 return SEC_E_UNSUPPORTED_FUNCTION;
1481static SECURITY_STATUS ntml_setUnicodeStringW(
UNICODE_STRING* str,
const WCHAR* val,
size_t bytelen)
1484 ntlm_free_unicode_string(str);
1485 *str = ntlm_from_unicode_string_w(val, bytelen /
sizeof(WCHAR));
1486 if (ntlm_is_unicode_string_empty(str))
1487 return SEC_E_INVALID_PARAMETER;
1492static SECURITY_STATUS utf16len(
const UNICODE_STRING* str,
void* pBuffer)
1495 WINPR_ASSERT(pBuffer);
1496 ULONG* val = (ULONG*)pBuffer;
1502static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(
PCtxtHandle phContext,
1503 ULONG ulAttribute,
void* pBuffer,
1507 return SEC_E_INVALID_HANDLE;
1510 return SEC_E_INVALID_PARAMETER;
1514 return SEC_E_INVALID_HANDLE;
1516 switch (ulAttribute)
1518 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME_LEN:
1519 return utf16len(&context->Workstation, pBuffer);
1520 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME_LEN:
1521 return utf16len(&context->NbDomainName, pBuffer);
1522 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME_LEN:
1523 return utf16len(&context->NbComputerName, pBuffer);
1524 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME_LEN:
1525 return utf16len(&context->DnsDomainName, pBuffer);
1526 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME_LEN:
1527 return utf16len(&context->DnsComputerName, pBuffer);
1528 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1529 return ntml_setUnicodeStringW(&context->Workstation, pBuffer, cbBuffer);
1530 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME:
1531 return ntml_setUnicodeStringW(&context->NbDomainName, pBuffer, cbBuffer);
1532 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME:
1533 return ntml_setUnicodeStringW(&context->NbComputerName, pBuffer, cbBuffer);
1534 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME:
1535 return ntml_setUnicodeStringW(&context->DnsDomainName, pBuffer, cbBuffer);
1536 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME:
1537 return ntml_setUnicodeStringW(&context->DnsComputerName, pBuffer, cbBuffer);
1540 return ntlm_SetContextAttributesCommon(phContext, ulAttribute, pBuffer, cbBuffer);
1544SECURITY_STATUS ntml_setUnicodeStringA(
UNICODE_STRING* str,
const char* val,
size_t charlen)
1547 ntlm_free_unicode_string(str);
1548 *str = ntlm_from_unicode_string_utf8(val, charlen);
1549 if (ntlm_is_unicode_string_empty(str))
1550 return SEC_E_INVALID_PARAMETER;
1555static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(
PCtxtHandle phContext,
1556 ULONG ulAttribute,
void* pBuffer,
1560 return SEC_E_INVALID_HANDLE;
1563 return SEC_E_INVALID_PARAMETER;
1567 return SEC_E_INVALID_HANDLE;
1569 switch (ulAttribute)
1571 case SECPKG_ATTR_AUTH_NTLM_HOSTNAME:
1572 return ntml_setUnicodeStringA(&context->Workstation, pBuffer, cbBuffer);
1573 case SECPKG_ATTR_AUTH_NTLM_NB_DOMAIN_NAME:
1574 return ntml_setUnicodeStringA(&context->NbDomainName, pBuffer, cbBuffer);
1575 case SECPKG_ATTR_AUTH_NTLM_NB_COMPUTER_NAME:
1576 return ntml_setUnicodeStringA(&context->NbComputerName, pBuffer, cbBuffer);
1577 case SECPKG_ATTR_AUTH_NTLM_DNS_DOMAIN_NAME:
1578 return ntml_setUnicodeStringA(&context->DnsDomainName, pBuffer, cbBuffer);
1579 case SECPKG_ATTR_AUTH_NTLM_DNS_COMPUTER_NAME:
1580 return ntml_setUnicodeStringA(&context->DnsComputerName, pBuffer, cbBuffer);
1582 return ntlm_SetContextAttributesCommon(phContext, ulAttribute, pBuffer, cbBuffer);
1587static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(
1588 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1589 WINPR_ATTR_UNUSED
void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1591 return SEC_E_UNSUPPORTED_FUNCTION;
1595static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(
1596 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1597 WINPR_ATTR_UNUSED
void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1599 return SEC_E_UNSUPPORTED_FUNCTION;
1603static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
1609static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(
PCtxtHandle phContext,
1610 WINPR_ATTR_UNUSED ULONG fQOP,
1613 const UINT32 SeqNo = MessageSeqNo;
1615 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1616 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1621 if (!check_context(context))
1622 return SEC_E_INVALID_HANDLE;
1624 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1626 SecBuffer* cur = &pMessage->pBuffers[index];
1628 if (cur->BufferType & SECBUFFER_DATA)
1630 else if (cur->BufferType & SECBUFFER_TOKEN)
1631 signature_buffer = cur;
1635 return SEC_E_INVALID_TOKEN;
1637 if (!signature_buffer)
1638 return SEC_E_INVALID_TOKEN;
1640 if (signature_buffer->cbBuffer < 16)
1641 return SEC_E_INSUFFICIENT_MEMORY;
1644 ULONG length = data_buffer->cbBuffer;
1645 void* data = malloc(length);
1648 return SEC_E_INSUFFICIENT_MEMORY;
1650 CopyMemory(data, data_buffer->pvBuffer, length);
1652 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1654 BOOL success = FALSE;
1658 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1661 winpr_Data_Write_UINT32(&value, SeqNo);
1663 if (!winpr_HMAC_Update(hmac, (
void*)&value, 4))
1665 if (!winpr_HMAC_Update(hmac, data, length))
1667 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1674 winpr_HMAC_Free(hmac);
1678 return SEC_E_INSUFFICIENT_MEMORY;
1682 if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
1684 if (context->confidentiality)
1686 if (!winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
1687 (BYTE*)data_buffer->pvBuffer))
1690 return SEC_E_INSUFFICIENT_MEMORY;
1694 CopyMemory(data_buffer->pvBuffer, data, length);
1697#ifdef WITH_DEBUG_NTLM
1698 WLog_DBG(TAG,
"Data Buffer (length = %" PRIu32
")", length);
1699 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1700 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1701 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1705 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1706 return SEC_E_INSUFFICIENT_MEMORY;
1707 if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
1709 BYTE* signature = signature_buffer->pvBuffer;
1711 winpr_Data_Write_UINT32(signature, version);
1712 CopyMemory(&signature[4], (
void*)checksum, 8);
1713 winpr_Data_Write_UINT32(&signature[12], SeqNo);
1715 context->SendSeqNum++;
1716#ifdef WITH_DEBUG_NTLM
1717 WLog_DBG(TAG,
"Signature (length = %" PRIu32
")", signature_buffer->cbBuffer);
1718 winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
1725 WINPR_ATTR_UNUSED PULONG pfQOP)
1727 const UINT32 SeqNo = (UINT32)MessageSeqNo;
1729 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1730 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1732 BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1736 if (!check_context(context))
1737 return SEC_E_INVALID_HANDLE;
1739 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1741 if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
1742 data_buffer = &pMessage->pBuffers[index];
1743 else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
1744 signature_buffer = &pMessage->pBuffers[index];
1748 return SEC_E_INVALID_TOKEN;
1750 if (!signature_buffer)
1751 return SEC_E_INVALID_TOKEN;
1753 if (signature_buffer->cbBuffer < 16)
1754 return SEC_E_INVALID_TOKEN;
1757 const ULONG length = data_buffer->cbBuffer;
1758 void* data = malloc(length);
1761 return SEC_E_INSUFFICIENT_MEMORY;
1763 CopyMemory(data, data_buffer->pvBuffer, length);
1767 if (context->confidentiality)
1769 if (!winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data,
1770 (BYTE*)data_buffer->pvBuffer))
1773 return SEC_E_INSUFFICIENT_MEMORY;
1777 CopyMemory(data_buffer->pvBuffer, data, length);
1780 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1782 BOOL success = FALSE;
1787 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1790 winpr_Data_Write_UINT32(&value, SeqNo);
1792 if (!winpr_HMAC_Update(hmac, (
void*)&value, 4))
1794 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1796 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1802 winpr_HMAC_Free(hmac);
1806 return SEC_E_INSUFFICIENT_MEMORY;
1809#ifdef WITH_DEBUG_NTLM
1810 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIu32
")", length);
1811 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1812 WLog_DBG(TAG,
"Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1813 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1817 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1818 return SEC_E_MESSAGE_ALTERED;
1821 winpr_Data_Write_UINT32(expected_signature, version);
1822 CopyMemory(&expected_signature[4], (
void*)checksum, 8);
1823 winpr_Data_Write_UINT32(&expected_signature[12], SeqNo);
1824 context->RecvSeqNum++;
1826 if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
1829 WLog_ERR(TAG,
"signature verification failed, something nasty is going on!");
1830#ifdef WITH_DEBUG_NTLM
1831 WLog_ERR(TAG,
"Expected Signature:");
1832 winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
1833 WLog_ERR(TAG,
"Actual Signature:");
1834 winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
1836 return SEC_E_MESSAGE_ALTERED;
1842static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(
PCtxtHandle phContext,
1843 WINPR_ATTR_UNUSED ULONG fQOP,
1846 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1850 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1851 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1853 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1854 if (!check_context(context))
1855 return SEC_E_INVALID_HANDLE;
1857 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1859 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1860 data_buffer = &pMessage->pBuffers[i];
1861 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1862 sig_buffer = &pMessage->pBuffers[i];
1865 if (!data_buffer || !sig_buffer)
1866 return SEC_E_INVALID_TOKEN;
1868 if (sig_buffer->cbBuffer < 16)
1869 return SEC_E_INSUFFICIENT_MEMORY;
1871 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1873 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1876 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1877 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1879 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1881 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1884 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1887 BYTE* signature = sig_buffer->pvBuffer;
1888 winpr_Data_Write_UINT32(signature, 1L);
1889 CopyMemory(&signature[4], checksum, 8);
1890 winpr_Data_Write_UINT32(&signature[12], seq_no);
1891 sig_buffer->cbBuffer = 16;
1896 winpr_HMAC_Free(hmac);
1901static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(
PCtxtHandle phContext,
1903 WINPR_ATTR_UNUSED PULONG pfQOP)
1905 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1909 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1910 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1911 BYTE signature[16] = WINPR_C_ARRAY_INIT;
1913 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1914 if (!check_context(context))
1915 return SEC_E_INVALID_HANDLE;
1917 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1919 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1920 data_buffer = &pMessage->pBuffers[i];
1921 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1922 sig_buffer = &pMessage->pBuffers[i];
1925 if (!data_buffer || !sig_buffer || (sig_buffer->cbBuffer < 16))
1926 return SEC_E_INVALID_TOKEN;
1928 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1930 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1933 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1934 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1936 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1938 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1941 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1944 winpr_Data_Write_UINT32(signature, 1L);
1945 CopyMemory(&signature[4], checksum, 8);
1946 winpr_Data_Write_UINT32(&signature[12], seq_no);
1949 if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
1950 status = SEC_E_MESSAGE_ALTERED;
1953 winpr_HMAC_Free(hmac);
1960 ntlm_QueryCredentialsAttributesA,
1961 ntlm_AcquireCredentialsHandleA,
1962 ntlm_FreeCredentialsHandle,
1964 ntlm_InitializeSecurityContextA,
1965 ntlm_AcceptSecurityContext,
1967 ntlm_DeleteSecurityContext,
1969 ntlm_QueryContextAttributesA,
1970 ntlm_ImpersonateSecurityContext,
1971 ntlm_RevertSecurityContext,
1973 ntlm_VerifySignature,
1983 ntlm_EncryptMessage,
1984 ntlm_DecryptMessage,
1985 ntlm_SetContextAttributesA,
1986 ntlm_SetCredentialsAttributesA,
1992 ntlm_QueryCredentialsAttributesW,
1993 ntlm_AcquireCredentialsHandleW,
1994 ntlm_FreeCredentialsHandle,
1996 ntlm_InitializeSecurityContextW,
1997 ntlm_AcceptSecurityContext,
1999 ntlm_DeleteSecurityContext,
2001 ntlm_QueryContextAttributesW,
2002 ntlm_ImpersonateSecurityContext,
2003 ntlm_RevertSecurityContext,
2005 ntlm_VerifySignature,
2015 ntlm_EncryptMessage,
2016 ntlm_DecryptMessage,
2017 ntlm_SetContextAttributesW,
2018 ntlm_SetCredentialsAttributesW,
2027 "NTLM Security Package"
2030static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = WINPR_C_ARRAY_INIT;
2031static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = WINPR_C_ARRAY_INIT;
2038 NTLM_SecPkgInfoW_NameBuffer,
2039 NTLM_SecPkgInfoW_CommentBuffer
2042char* ntlm_negotiate_flags_string(
char* buffer,
size_t size, UINT32 flags)
2044 if (!buffer || (size == 0))
2047 (void)_snprintf(buffer, size,
"[0x%08" PRIx32
"] ", flags);
2049 for (
int x = 0; x < 31; x++)
2051 const UINT32 mask = 1u << x;
2052 size_t len = strnlen(buffer, size);
2055 const char* str = ntlm_get_negotiate_string(mask);
2056 const size_t flen = strlen(str);
2058 if ((len > 0) && (buffer[len - 1] !=
' '))
2062 winpr_str_append(
"|", buffer, size,
nullptr);
2066 if (size - len < flen)
2068 winpr_str_append(str, buffer, size,
nullptr);
2075const char* ntlm_message_type_string(UINT32 messageType)
2077 switch (messageType)
2079 case MESSAGE_TYPE_NEGOTIATE:
2080 return "MESSAGE_TYPE_NEGOTIATE";
2081 case MESSAGE_TYPE_CHALLENGE:
2082 return "MESSAGE_TYPE_CHALLENGE";
2083 case MESSAGE_TYPE_AUTHENTICATE:
2084 return "MESSAGE_TYPE_AUTHENTICATE";
2086 return "MESSAGE_TYPE_UNKNOWN";
2090const char* ntlm_state_string(NTLM_STATE state)
2094 case NTLM_STATE_INITIAL:
2095 return "NTLM_STATE_INITIAL";
2096 case NTLM_STATE_NEGOTIATE:
2097 return "NTLM_STATE_NEGOTIATE";
2098 case NTLM_STATE_CHALLENGE:
2099 return "NTLM_STATE_CHALLENGE";
2100 case NTLM_STATE_AUTHENTICATE:
2101 return "NTLM_STATE_AUTHENTICATE";
2102 case NTLM_STATE_FINAL:
2103 return "NTLM_STATE_FINAL";
2105 return "NTLM_STATE_UNKNOWN";
2108void ntlm_change_state(
NTLM_CONTEXT* ntlm, NTLM_STATE state)
2111 WLog_DBG(TAG,
"change state from %s to %s", ntlm_state_string(ntlm->state),
2112 ntlm_state_string(state));
2113 ntlm->state = state;
2122BOOL ntlm_reset_cipher_state(
PSecHandle phContext)
2124 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
2128 if (!check_context(context))
2131 winpr_RC4_Free(context->SendRc4Seal);
2132 winpr_RC4_Free(context->RecvRc4Seal);
2133 context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
2134 context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
2136 if (!context->SendRc4Seal)
2138 WLog_ERR(TAG,
"Failed to allocate context->SendRc4Seal");
2141 if (!context->RecvRc4Seal)
2143 WLog_ERR(TAG,
"Failed to allocate context->RecvRc4Seal");
2153 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
2154 ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
2155 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
2156 ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));
2161BOOL ntlm_SecBufferRealloc(
SecBuffer* buffer, ULONG len)
2163 sspi_SecBufferFree(buffer);
2164 return sspi_SecBufferAlloc(buffer, len) !=
nullptr;
WINPR_API void WINPR_JSON_Delete(WINPR_JSON *item)
Delete a WinPR JSON wrapper object.
WINPR_ATTR_NODISCARD psSspiNtlmHashCallback hashCallback