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"
40#define TAG WINPR_TAG("sspi.NTLM")
42#define WINPR_KEY "Software\\" WINPR_VENDOR_STRING "\\" WINPR_PRODUCT_STRING "\\WinPR\\NTLM"
44static char* NTLM_PACKAGE_NAME =
"NTLM";
46#define check_context(ctx) check_context_((ctx), __FILE__, __func__, __LINE__)
47static BOOL check_context_(
NTLM_CONTEXT* context,
const char* file,
const char* fkt,
size_t line)
50 wLog* log = WLog_Get(TAG);
51 const DWORD log_level = WLOG_ERROR;
55 if (WLog_IsLevelActive(log, log_level))
56 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
62 if (!context->RecvRc4Seal)
64 if (WLog_IsLevelActive(log, log_level))
65 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
66 "invalid context->RecvRc4Seal");
69 if (!context->SendRc4Seal)
71 if (WLog_IsLevelActive(log, log_level))
72 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
73 "invalid context->SendRc4Seal");
77 if (!context->SendSigningKey)
79 if (WLog_IsLevelActive(log, log_level))
80 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
81 "invalid context->SendSigningKey");
84 if (!context->RecvSigningKey)
86 if (WLog_IsLevelActive(log, log_level))
87 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
88 "invalid context->RecvSigningKey");
91 if (!context->SendSealingKey)
93 if (WLog_IsLevelActive(log, log_level))
94 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
95 "invalid context->SendSealingKey");
98 if (!context->RecvSealingKey)
100 if (WLog_IsLevelActive(log, log_level))
101 WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
102 "invalid context->RecvSealingKey");
108static char* get_name(COMPUTER_NAME_FORMAT type)
112 if (GetComputerNameExA(type, NULL, &nSize))
115 if (GetLastError() != ERROR_MORE_DATA)
118 char* computerName = calloc(1, nSize);
123 if (!GetComputerNameExA(type, computerName, &nSize))
132static int ntlm_SetContextWorkstation(
NTLM_CONTEXT* context,
char* Workstation)
134 char* ws = Workstation;
135 CHAR* computerName = NULL;
137 WINPR_ASSERT(context);
141 computerName = get_name(ComputerNameNetBIOS);
148 context->Workstation.Buffer = ConvertUtf8ToWCharAlloc(ws, &len);
152 if (!context->Workstation.Buffer || (len > UINT16_MAX /
sizeof(WCHAR)))
155 context->Workstation.Length = (USHORT)(len *
sizeof(WCHAR));
159static int ntlm_SetContextServicePrincipalNameW(
NTLM_CONTEXT* context, LPWSTR ServicePrincipalName)
161 WINPR_ASSERT(context);
163 if (!ServicePrincipalName)
165 context->ServicePrincipalName.Buffer = NULL;
166 context->ServicePrincipalName.Length = 0;
170 context->ServicePrincipalName.Length = (USHORT)(_wcslen(ServicePrincipalName) * 2);
171 context->ServicePrincipalName.Buffer = (PWSTR)malloc(context->ServicePrincipalName.Length + 2);
173 if (!context->ServicePrincipalName.Buffer)
176 memcpy(context->ServicePrincipalName.Buffer, ServicePrincipalName,
177 context->ServicePrincipalName.Length + 2);
181static int ntlm_SetContextTargetName(
NTLM_CONTEXT* context,
char* TargetName)
183 char* name = TargetName;
185 CHAR* computerName = NULL;
187 WINPR_ASSERT(context);
191 if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) ||
192 GetLastError() != ERROR_MORE_DATA)
195 computerName = calloc(nSize,
sizeof(CHAR));
200 if (!GetComputerNameExA(ComputerNameNetBIOS, computerName, &nSize))
206 if (nSize > MAX_COMPUTERNAME_LENGTH)
207 computerName[MAX_COMPUTERNAME_LENGTH] =
'\0';
218 context->TargetName.pvBuffer = ConvertUtf8ToWCharAlloc(name, &len);
220 if (!context->TargetName.pvBuffer || (len > UINT16_MAX /
sizeof(WCHAR)))
222 free(context->TargetName.pvBuffer);
223 context->TargetName.pvBuffer = NULL;
231 context->TargetName.cbBuffer = (USHORT)(len *
sizeof(WCHAR));
251 context->NTLMv2 = TRUE;
252 context->UseMIC = FALSE;
253 context->SendVersionInfo = TRUE;
254 context->SendSingleHostData = FALSE;
255 context->SendWorkstationName = TRUE;
256 context->NegotiateKeyExchange = TRUE;
257 context->UseSamFileDatabase = TRUE;
258 status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, WINPR_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
260 if (status == ERROR_SUCCESS)
262 if (RegQueryValueEx(hKey, _T(
"NTLMv2"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) ==
264 context->NTLMv2 = dwValue ? 1 : 0;
266 if (RegQueryValueEx(hKey, _T(
"UseMIC"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) ==
268 context->UseMIC = dwValue ? 1 : 0;
270 if (RegQueryValueEx(hKey, _T(
"SendVersionInfo"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) ==
272 context->SendVersionInfo = dwValue ? 1 : 0;
274 if (RegQueryValueEx(hKey, _T(
"SendSingleHostData"), NULL, &dwType, (BYTE*)&dwValue,
275 &dwSize) == ERROR_SUCCESS)
276 context->SendSingleHostData = dwValue ? 1 : 0;
278 if (RegQueryValueEx(hKey, _T(
"SendWorkstationName"), NULL, &dwType, (BYTE*)&dwValue,
279 &dwSize) == ERROR_SUCCESS)
280 context->SendWorkstationName = dwValue ? 1 : 0;
282 if (RegQueryValueEx(hKey, _T(
"WorkstationName"), NULL, &dwType, NULL, &dwSize) ==
285 char* workstation = (
char*)malloc(dwSize + 1);
293 status = RegQueryValueExA(hKey,
"WorkstationName", NULL, &dwType, (BYTE*)workstation,
295 if (status != ERROR_SUCCESS)
296 WLog_WARN(TAG,
"Key ''WorkstationName' not found");
297 workstation[dwSize] =
'\0';
299 if (ntlm_SetContextWorkstation(context, workstation) < 0)
316 context->SuppressExtendedProtection = FALSE;
317 status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(
"System\\CurrentControlSet\\Control\\LSA"), 0,
318 KEY_READ | KEY_WOW64_64KEY, &hKey);
320 if (status == ERROR_SUCCESS)
322 if (RegQueryValueEx(hKey, _T(
"SuppressExtendedProtection"), NULL, &dwType, (BYTE*)&dwValue,
323 &dwSize) == ERROR_SUCCESS)
324 context->SuppressExtendedProtection = dwValue ? 1 : 0;
329 context->NegotiateFlags = 0;
330 context->LmCompatibilityLevel = 3;
331 ntlm_change_state(context, NTLM_STATE_INITIAL);
332 FillMemory(context->MachineID,
sizeof(context->MachineID), 0xAA);
335 context->UseMIC = TRUE;
345 winpr_RC4_Free(context->SendRc4Seal);
346 winpr_RC4_Free(context->RecvRc4Seal);
347 sspi_SecBufferFree(&context->NegotiateMessage);
348 sspi_SecBufferFree(&context->ChallengeMessage);
349 sspi_SecBufferFree(&context->AuthenticateMessage);
350 sspi_SecBufferFree(&context->ChallengeTargetInfo);
351 sspi_SecBufferFree(&context->TargetName);
352 sspi_SecBufferFree(&context->NtChallengeResponse);
353 sspi_SecBufferFree(&context->LmChallengeResponse);
354 free(context->ServicePrincipalName.Buffer);
355 free(context->Workstation.Buffer);
359static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
360 WINPR_ATTR_UNUSED SEC_WCHAR* pszPrincipal, WINPR_ATTR_UNUSED SEC_WCHAR* pszPackage,
361 ULONG fCredentialUse, WINPR_ATTR_UNUSED
void* pvLogonID,
void* pAuthData,
362 SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
367 if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) &&
368 (fCredentialUse != SECPKG_CRED_BOTH))
370 return SEC_E_INVALID_PARAMETER;
376 return SEC_E_INTERNAL_ERROR;
378 credentials->fCredentialUse = fCredentialUse;
379 credentials->pGetKeyFn = pGetKeyFn;
380 credentials->pvGetKeyArgument = pvGetKeyArgument;
384 UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData);
386 sspi_CopyAuthIdentity(&(credentials->identity),
389 if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED)
395 if (settings->samFile)
397 credentials->ntlmSettings.samFile = _strdup(settings->samFile);
398 if (!credentials->ntlmSettings.samFile)
400 sspi_CredentialsFree(credentials);
401 return SEC_E_INSUFFICIENT_MEMORY;
404 credentials->ntlmSettings.hashCallback = settings->hashCallback;
405 credentials->ntlmSettings.hashCallbackArg = settings->hashCallbackArg;
408 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)credentials);
409 sspi_SecureHandleSetUpperPointer(phCredential, (
void*)NTLM_PACKAGE_NAME);
413static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
414 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
415 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
418 SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
419 SEC_WCHAR* principal = NULL;
420 SEC_WCHAR*
package = NULL;
424 principal = ConvertUtf8ToWCharAlloc(pszPrincipal, NULL);
430 package = ConvertUtf8ToWCharAlloc(pszPackage, NULL);
436 ntlm_AcquireCredentialsHandleW(principal, package, fCredentialUse, pvLogonID, pAuthData,
437 pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
446static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(
PCredHandle phCredential)
449 return SEC_E_INVALID_HANDLE;
455 return SEC_E_INVALID_HANDLE;
457 sspi_CredentialsFree(credentials);
461static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(
462 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
463 WINPR_ATTR_UNUSED
void* pBuffer)
465 if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
470 WLog_ERR(TAG,
"TODO: Implement");
471 return SEC_E_UNSUPPORTED_FUNCTION;
474static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(
PCredHandle phCredential,
475 ULONG ulAttribute,
void* pBuffer)
477 return ntlm_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
483static SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(
486 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED
PTimeStamp ptsTimeStamp)
488 SECURITY_STATUS status = 0;
494 if (phContext && !phContext->dwLower && !phContext->dwUpper)
495 return SEC_E_INVALID_HANDLE;
501 context = ntlm_ContextNew();
504 return SEC_E_INSUFFICIENT_MEMORY;
506 context->server = TRUE;
508 if (fContextReq & ASC_REQ_CONFIDENTIALITY)
509 context->confidentiality = TRUE;
511 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
512 context->credentials = credentials;
513 context->SamFile = credentials->ntlmSettings.samFile;
514 context->HashCallback = credentials->ntlmSettings.hashCallback;
515 context->HashCallbackArg = credentials->ntlmSettings.hashCallbackArg;
517 ntlm_SetContextTargetName(context, NULL);
518 sspi_SecureHandleSetLowerPointer(phNewContext, context);
519 sspi_SecureHandleSetUpperPointer(phNewContext, (
void*)NTLM_PACKAGE_NAME);
522 switch (ntlm_get_state(context))
524 case NTLM_STATE_INITIAL:
526 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
529 return SEC_E_INVALID_TOKEN;
531 if (pInput->cBuffers < 1)
532 return SEC_E_INVALID_TOKEN;
534 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
537 return SEC_E_INVALID_TOKEN;
539 if (input_buffer->cbBuffer < 1)
540 return SEC_E_INVALID_TOKEN;
542 status = ntlm_read_NegotiateMessage(context, input_buffer);
543 if (status != SEC_I_CONTINUE_NEEDED)
546 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
549 return SEC_E_INVALID_TOKEN;
551 if (pOutput->cBuffers < 1)
552 return SEC_E_INVALID_TOKEN;
554 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
556 if (!output_buffer->BufferType)
557 return SEC_E_INVALID_TOKEN;
559 if (output_buffer->cbBuffer < 1)
560 return SEC_E_INSUFFICIENT_MEMORY;
562 return ntlm_write_ChallengeMessage(context, output_buffer);
565 return SEC_E_OUT_OF_SEQUENCE;
568 case NTLM_STATE_AUTHENTICATE:
571 return SEC_E_INVALID_TOKEN;
573 if (pInput->cBuffers < 1)
574 return SEC_E_INVALID_TOKEN;
576 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
579 return SEC_E_INVALID_TOKEN;
581 if (input_buffer->cbBuffer < 1)
582 return SEC_E_INVALID_TOKEN;
584 status = ntlm_read_AuthenticateMessage(context, input_buffer);
588 for (ULONG i = 0; i < pOutput->cBuffers; i++)
590 pOutput->pBuffers[i].cbBuffer = 0;
591 pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN;
599 return SEC_E_OUT_OF_SEQUENCE;
603static SECURITY_STATUS SEC_ENTRY
604ntlm_ImpersonateSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
609static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
611 WINPR_ATTR_UNUSED ULONG Reserved1, WINPR_ATTR_UNUSED ULONG TargetDataRep,
PSecBufferDesc pInput,
613 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED
PTimeStamp ptsExpiry)
615 SECURITY_STATUS status = 0;
621 if (phContext && !phContext->dwLower && !phContext->dwUpper)
622 return SEC_E_INVALID_HANDLE;
628 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
633 context = ntlm_ContextNew();
636 return SEC_E_INSUFFICIENT_MEMORY;
638 if (fContextReq & ISC_REQ_CONFIDENTIALITY)
639 context->confidentiality = TRUE;
641 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
642 context->credentials = credentials;
644 if (context->Workstation.Length < 1)
646 if (ntlm_SetContextWorkstation(context, NULL) < 0)
648 ntlm_ContextFree(context);
649 return SEC_E_INTERNAL_ERROR;
653 if (ntlm_SetContextServicePrincipalNameW(context, pszTargetName) < 0)
655 ntlm_ContextFree(context);
656 return SEC_E_INTERNAL_ERROR;
659 sspi_SecureHandleSetLowerPointer(phNewContext, context);
660 sspi_SecureHandleSetUpperPointer(phNewContext, NTLM_SSP_NAME);
663 if ((!input_buffer) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE))
666 return SEC_E_INVALID_TOKEN;
668 if (pOutput->cBuffers < 1)
669 return SEC_E_INVALID_TOKEN;
671 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
674 return SEC_E_INVALID_TOKEN;
676 if (output_buffer->cbBuffer < 1)
677 return SEC_E_INVALID_TOKEN;
679 if (ntlm_get_state(context) == NTLM_STATE_INITIAL)
680 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
682 if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE)
683 return ntlm_write_NegotiateMessage(context, output_buffer);
685 return SEC_E_OUT_OF_SEQUENCE;
690 return SEC_E_INVALID_TOKEN;
692 if (input_buffer->cbBuffer < 1)
693 return SEC_E_INVALID_TOKEN;
695 PSecBuffer channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
697 if (channel_bindings)
699 context->Bindings.BindingsLength = channel_bindings->cbBuffer;
703 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
705 status = ntlm_read_ChallengeMessage(context, input_buffer);
707 if (status != SEC_I_CONTINUE_NEEDED)
711 return SEC_E_INVALID_TOKEN;
713 if (pOutput->cBuffers < 1)
714 return SEC_E_INVALID_TOKEN;
716 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
719 return SEC_E_INVALID_TOKEN;
721 if (output_buffer->cbBuffer < 1)
722 return SEC_E_INSUFFICIENT_MEMORY;
724 if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)
725 return ntlm_write_AuthenticateMessage(context, output_buffer);
728 return SEC_E_OUT_OF_SEQUENCE;
731 return SEC_E_OUT_OF_SEQUENCE;
737static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
739 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
742 SECURITY_STATUS status = 0;
743 SEC_WCHAR* pszTargetNameW = NULL;
747 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL);
749 return SEC_E_INTERNAL_ERROR;
752 status = ntlm_InitializeSecurityContextW(phCredential, phContext, pszTargetNameW, fContextReq,
753 Reserved1, TargetDataRep, pInput, Reserved2,
754 phNewContext, pOutput, pfContextAttr, ptsExpiry);
755 free(pszTargetNameW);
761static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(
PCtxtHandle phContext)
764 ntlm_ContextFree(context);
774 WINPR_ASSERT(ntproof);
776 target = &ntlm->ChallengeTargetInfo;
778 if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer))
779 return SEC_E_INSUFFICIENT_MEMORY;
781 blob = (BYTE*)ntproof->pvBuffer;
782 CopyMemory(blob, ntlm->ServerChallenge, 8);
786 CopyMemory(&blob[16], ntlm->Timestamp, 8);
787 CopyMemory(&blob[24], ntlm->ClientChallenge, 8);
790 CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer);
800 WINPR_ASSERT(micvalue);
802 msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer +
803 ntlm->AuthenticateMessage.cbBuffer;
805 if (!sspi_SecBufferAlloc(micvalue, msgSize))
806 return SEC_E_INSUFFICIENT_MEMORY;
808 blob = (BYTE*)micvalue->pvBuffer;
809 CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer);
810 blob += ntlm->NegotiateMessage.cbBuffer;
811 CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer);
812 blob += ntlm->ChallengeMessage.cbBuffer;
813 CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer);
814 blob += ntlm->MessageIntegrityCheckOffset;
815 ZeroMemory(blob, 16);
821static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(
PCtxtHandle phContext,
822 ULONG ulAttribute,
void* pBuffer)
825 return SEC_E_INVALID_HANDLE;
828 return SEC_E_INSUFFICIENT_MEMORY;
831 if (!check_context(context))
832 return SEC_E_INVALID_HANDLE;
834 if (ulAttribute == SECPKG_ATTR_SIZES)
837 ContextSizes->cbMaxToken = 2010;
838 ContextSizes->cbMaxSignature = 16;
839 ContextSizes->cbBlockSize = 0;
840 ContextSizes->cbSecurityTrailer = 16;
844 else if (ulAttribute == SECPKG_ATTR_AUTH_IDENTITY)
850 WINPR_ASSERT(AuthIdentity);
851 *AuthIdentity = empty;
853 context->UseSamFileDatabase = FALSE;
854 credentials = context->credentials;
856 if (credentials->identity.UserLength > 0)
858 if (ConvertWCharNToUtf8(credentials->identity.User, credentials->identity.UserLength,
859 AuthIdentity->User, ARRAYSIZE(AuthIdentity->User)) <= 0)
860 return SEC_E_INTERNAL_ERROR;
863 if (credentials->identity.DomainLength > 0)
865 if (ConvertWCharNToUtf8(credentials->identity.Domain,
866 credentials->identity.DomainLength, AuthIdentity->Domain,
867 ARRAYSIZE(AuthIdentity->Domain)) <= 0)
868 return SEC_E_INTERNAL_ERROR;
873 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE)
875 return ntlm_computeProofValue(context, (
SecBuffer*)pBuffer);
877 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_RANDKEY)
882 if (!sspi_SecBufferAlloc(randkey, 16))
883 return (SEC_E_INSUFFICIENT_MEMORY);
885 CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16);
888 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC)
893 if (!sspi_SecBufferAlloc(mic, 16))
894 return (SEC_E_INSUFFICIENT_MEMORY);
896 CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16);
899 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC_VALUE)
901 return ntlm_computeMicValue(context, (
SecBuffer*)pBuffer);
904 WLog_ERR(TAG,
"TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute);
905 return SEC_E_UNSUPPORTED_FUNCTION;
908static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(
PCtxtHandle phContext,
909 ULONG ulAttribute,
void* pBuffer)
911 return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
914static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(
PCtxtHandle phContext,
915 ULONG ulAttribute,
void* pBuffer,
919 return SEC_E_INVALID_HANDLE;
922 return SEC_E_INVALID_PARAMETER;
926 return SEC_E_INVALID_HANDLE;
928 if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_HASH)
933 return SEC_E_INVALID_PARAMETER;
935 if (AuthNtlmHash->Version == 1)
936 CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16);
937 else if (AuthNtlmHash->Version == 2)
938 CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16);
942 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MESSAGE)
947 return SEC_E_INVALID_PARAMETER;
949 if (AuthNtlmMessage->type == 1)
951 sspi_SecBufferFree(&context->NegotiateMessage);
953 if (!sspi_SecBufferAlloc(&context->NegotiateMessage, AuthNtlmMessage->length))
954 return SEC_E_INSUFFICIENT_MEMORY;
956 CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer,
957 AuthNtlmMessage->length);
959 else if (AuthNtlmMessage->type == 2)
961 sspi_SecBufferFree(&context->ChallengeMessage);
963 if (!sspi_SecBufferAlloc(&context->ChallengeMessage, AuthNtlmMessage->length))
964 return SEC_E_INSUFFICIENT_MEMORY;
966 CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer,
967 AuthNtlmMessage->length);
969 else if (AuthNtlmMessage->type == 3)
971 sspi_SecBufferFree(&context->AuthenticateMessage);
973 if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, AuthNtlmMessage->length))
974 return SEC_E_INSUFFICIENT_MEMORY;
976 CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer,
977 AuthNtlmMessage->length);
982 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_TIMESTAMP)
988 return SEC_E_INVALID_PARAMETER;
990 if (AuthNtlmTimestamp->ChallengeOrResponse)
991 CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8);
993 CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8);
997 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE)
1003 return SEC_E_INVALID_PARAMETER;
1005 CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8);
1008 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE)
1014 return SEC_E_INVALID_PARAMETER;
1016 CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8);
1020 WLog_ERR(TAG,
"TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
1021 return SEC_E_UNSUPPORTED_FUNCTION;
1024static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(
PCtxtHandle phContext,
1025 ULONG ulAttribute,
void* pBuffer,
1028 return ntlm_SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer);
1031static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(
1032 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1033 WINPR_ATTR_UNUSED
void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1035 return SEC_E_UNSUPPORTED_FUNCTION;
1038static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(
1039 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1040 WINPR_ATTR_UNUSED
void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1042 return SEC_E_UNSUPPORTED_FUNCTION;
1045static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
1050static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(
PCtxtHandle phContext,
1051 WINPR_ATTR_UNUSED ULONG fQOP,
1054 const UINT32 SeqNo = MessageSeqNo;
1056 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1057 BYTE checksum[8] = { 0 };
1062 if (!check_context(context))
1063 return SEC_E_INVALID_HANDLE;
1065 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1067 SecBuffer* cur = &pMessage->pBuffers[index];
1069 if (cur->BufferType & SECBUFFER_DATA)
1071 else if (cur->BufferType & SECBUFFER_TOKEN)
1072 signature_buffer = cur;
1076 return SEC_E_INVALID_TOKEN;
1078 if (!signature_buffer)
1079 return SEC_E_INVALID_TOKEN;
1082 ULONG length = data_buffer->cbBuffer;
1083 void* data = malloc(length);
1086 return SEC_E_INSUFFICIENT_MEMORY;
1088 CopyMemory(data, data_buffer->pvBuffer, length);
1090 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1093 winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1095 winpr_Data_Write_UINT32(&value, SeqNo);
1096 winpr_HMAC_Update(hmac, (
void*)&value, 4);
1097 winpr_HMAC_Update(hmac, data, length);
1098 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1099 winpr_HMAC_Free(hmac);
1103 winpr_HMAC_Free(hmac);
1105 return SEC_E_INSUFFICIENT_MEMORY;
1109 if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
1111 if (context->confidentiality)
1112 winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
1113 (BYTE*)data_buffer->pvBuffer);
1115 CopyMemory(data_buffer->pvBuffer, data, length);
1118#ifdef WITH_DEBUG_NTLM
1119 WLog_DBG(TAG,
"Data Buffer (length = %" PRIuz
")", length);
1120 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1121 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1122 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1126 winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum);
1127 if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
1129 BYTE* signature = signature_buffer->pvBuffer;
1131 winpr_Data_Write_UINT32(signature, version);
1132 CopyMemory(&signature[4], (
void*)checksum, 8);
1133 winpr_Data_Write_UINT32(&signature[12], SeqNo);
1135 context->SendSeqNum++;
1136#ifdef WITH_DEBUG_NTLM
1137 WLog_DBG(TAG,
"Signature (length = %" PRIu32
")", signature_buffer->cbBuffer);
1138 winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
1145 WINPR_ATTR_UNUSED PULONG pfQOP)
1147 const UINT32 SeqNo = (UINT32)MessageSeqNo;
1149 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1150 BYTE checksum[8] = { 0 };
1152 BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1156 if (!check_context(context))
1157 return SEC_E_INVALID_HANDLE;
1159 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1161 if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
1162 data_buffer = &pMessage->pBuffers[index];
1163 else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
1164 signature_buffer = &pMessage->pBuffers[index];
1168 return SEC_E_INVALID_TOKEN;
1170 if (!signature_buffer)
1171 return SEC_E_INVALID_TOKEN;
1174 const ULONG length = data_buffer->cbBuffer;
1175 void* data = malloc(length);
1178 return SEC_E_INSUFFICIENT_MEMORY;
1180 CopyMemory(data, data_buffer->pvBuffer, length);
1184 if (context->confidentiality)
1185 winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data, (BYTE*)data_buffer->pvBuffer);
1187 CopyMemory(data_buffer->pvBuffer, data, length);
1190 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1193 winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1195 winpr_Data_Write_UINT32(&value, SeqNo);
1196 winpr_HMAC_Update(hmac, (
void*)&value, 4);
1197 winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
1198 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1199 winpr_HMAC_Free(hmac);
1203 winpr_HMAC_Free(hmac);
1205 return SEC_E_INSUFFICIENT_MEMORY;
1208#ifdef WITH_DEBUG_NTLM
1209 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIuz
")", length);
1210 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1211 WLog_DBG(TAG,
"Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1212 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1216 winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum);
1218 winpr_Data_Write_UINT32(expected_signature, version);
1219 CopyMemory(&expected_signature[4], (
void*)checksum, 8);
1220 winpr_Data_Write_UINT32(&expected_signature[12], SeqNo);
1221 context->RecvSeqNum++;
1223 if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
1226 WLog_ERR(TAG,
"signature verification failed, something nasty is going on!");
1227#ifdef WITH_DEBUG_NTLM
1228 WLog_ERR(TAG,
"Expected Signature:");
1229 winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
1230 WLog_ERR(TAG,
"Actual Signature:");
1231 winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
1233 return SEC_E_MESSAGE_ALTERED;
1239static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(
PCtxtHandle phContext,
1240 WINPR_ATTR_UNUSED ULONG fQOP,
1246 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1247 BYTE checksum[8] = { 0 };
1249 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1250 if (!check_context(context))
1251 return SEC_E_INVALID_HANDLE;
1253 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1255 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1256 data_buffer = &pMessage->pBuffers[i];
1257 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1258 sig_buffer = &pMessage->pBuffers[i];
1261 if (!data_buffer || !sig_buffer)
1262 return SEC_E_INVALID_TOKEN;
1264 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1266 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1268 winpr_HMAC_Free(hmac);
1269 return SEC_E_INTERNAL_ERROR;
1272 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1273 winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4);
1274 winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
1275 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1276 winpr_HMAC_Free(hmac);
1278 winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum);
1280 BYTE* signature = sig_buffer->pvBuffer;
1281 winpr_Data_Write_UINT32(signature, 1L);
1282 CopyMemory(&signature[4], checksum, 8);
1283 winpr_Data_Write_UINT32(&signature[12], seq_no);
1284 sig_buffer->cbBuffer = 16;
1289static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(
PCtxtHandle phContext,
1291 WINPR_ATTR_UNUSED PULONG pfQOP)
1296 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1297 BYTE checksum[8] = { 0 };
1298 BYTE signature[16] = { 0 };
1300 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1301 if (!check_context(context))
1302 return SEC_E_INVALID_HANDLE;
1304 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1306 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1307 data_buffer = &pMessage->pBuffers[i];
1308 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1309 sig_buffer = &pMessage->pBuffers[i];
1312 if (!data_buffer || !sig_buffer)
1313 return SEC_E_INVALID_TOKEN;
1315 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1317 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1319 winpr_HMAC_Free(hmac);
1320 return SEC_E_INTERNAL_ERROR;
1323 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1324 winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4);
1325 winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
1326 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1327 winpr_HMAC_Free(hmac);
1329 winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum);
1331 winpr_Data_Write_UINT32(signature, 1L);
1332 CopyMemory(&signature[4], checksum, 8);
1333 winpr_Data_Write_UINT32(&signature[12], seq_no);
1335 if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
1336 return SEC_E_MESSAGE_ALTERED;
1344 ntlm_QueryCredentialsAttributesA,
1345 ntlm_AcquireCredentialsHandleA,
1346 ntlm_FreeCredentialsHandle,
1348 ntlm_InitializeSecurityContextA,
1349 ntlm_AcceptSecurityContext,
1351 ntlm_DeleteSecurityContext,
1353 ntlm_QueryContextAttributesA,
1354 ntlm_ImpersonateSecurityContext,
1355 ntlm_RevertSecurityContext,
1357 ntlm_VerifySignature,
1367 ntlm_EncryptMessage,
1368 ntlm_DecryptMessage,
1369 ntlm_SetContextAttributesA,
1370 ntlm_SetCredentialsAttributesA,
1376 ntlm_QueryCredentialsAttributesW,
1377 ntlm_AcquireCredentialsHandleW,
1378 ntlm_FreeCredentialsHandle,
1380 ntlm_InitializeSecurityContextW,
1381 ntlm_AcceptSecurityContext,
1383 ntlm_DeleteSecurityContext,
1385 ntlm_QueryContextAttributesW,
1386 ntlm_ImpersonateSecurityContext,
1387 ntlm_RevertSecurityContext,
1389 ntlm_VerifySignature,
1399 ntlm_EncryptMessage,
1400 ntlm_DecryptMessage,
1401 ntlm_SetContextAttributesW,
1402 ntlm_SetCredentialsAttributesW,
1411 "NTLM Security Package"
1414static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = { 0 };
1415static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = { 0 };
1422 NTLM_SecPkgInfoW_NameBuffer,
1423 NTLM_SecPkgInfoW_CommentBuffer
1426char* ntlm_negotiate_flags_string(
char* buffer,
size_t size, UINT32 flags)
1428 if (!buffer || (size == 0))
1431 (void)_snprintf(buffer, size,
"[0x%08" PRIx32
"] ", flags);
1433 for (
int x = 0; x < 31; x++)
1435 const UINT32 mask = 1 << x;
1436 size_t len = strnlen(buffer, size);
1439 const char* str = ntlm_get_negotiate_string(mask);
1440 const size_t flen = strlen(str);
1442 if ((len > 0) && (buffer[len - 1] !=
' '))
1446 winpr_str_append(
"|", buffer, size, NULL);
1450 if (size - len < flen)
1452 winpr_str_append(str, buffer, size, NULL);
1459const char* ntlm_message_type_string(UINT32 messageType)
1461 switch (messageType)
1463 case MESSAGE_TYPE_NEGOTIATE:
1464 return "MESSAGE_TYPE_NEGOTIATE";
1465 case MESSAGE_TYPE_CHALLENGE:
1466 return "MESSAGE_TYPE_CHALLENGE";
1467 case MESSAGE_TYPE_AUTHENTICATE:
1468 return "MESSAGE_TYPE_AUTHENTICATE";
1470 return "MESSAGE_TYPE_UNKNOWN";
1474const char* ntlm_state_string(NTLM_STATE state)
1478 case NTLM_STATE_INITIAL:
1479 return "NTLM_STATE_INITIAL";
1480 case NTLM_STATE_NEGOTIATE:
1481 return "NTLM_STATE_NEGOTIATE";
1482 case NTLM_STATE_CHALLENGE:
1483 return "NTLM_STATE_CHALLENGE";
1484 case NTLM_STATE_AUTHENTICATE:
1485 return "NTLM_STATE_AUTHENTICATE";
1486 case NTLM_STATE_FINAL:
1487 return "NTLM_STATE_FINAL";
1489 return "NTLM_STATE_UNKNOWN";
1492void ntlm_change_state(
NTLM_CONTEXT* ntlm, NTLM_STATE state)
1495 WLog_DBG(TAG,
"change state from %s to %s", ntlm_state_string(ntlm->state),
1496 ntlm_state_string(state));
1497 ntlm->state = state;
1506BOOL ntlm_reset_cipher_state(
PSecHandle phContext)
1508 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1512 check_context(context);
1513 winpr_RC4_Free(context->SendRc4Seal);
1514 winpr_RC4_Free(context->RecvRc4Seal);
1515 context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
1516 context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
1518 if (!context->SendRc4Seal)
1520 WLog_ERR(TAG,
"Failed to allocate context->SendRc4Seal");
1523 if (!context->RecvRc4Seal)
1525 WLog_ERR(TAG,
"Failed to allocate context->RecvRc4Seal");
1535 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
1536 ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
1537 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
1538 ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));