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")
44#define WINPR_KEY "Software\\%s\\WinPR\\NTLM"
46static char* NTLM_PACKAGE_NAME =
"NTLM";
48#define check_context(ctx) check_context_((ctx), __FILE__, __func__, __LINE__)
49static BOOL check_context_(
NTLM_CONTEXT* context,
const char* file,
const char* fkt,
size_t line)
52 wLog* log = WLog_Get(TAG);
53 const DWORD log_level = WLOG_ERROR;
57 if (WLog_IsLevelActive(log, log_level))
58 WLog_PrintTextMessage(log, log_level, line, file, fkt,
"invalid context");
63 if (!context->RecvRc4Seal)
65 if (WLog_IsLevelActive(log, log_level))
66 WLog_PrintTextMessage(log, log_level, line, file, fkt,
"invalid context->RecvRc4Seal");
69 if (!context->SendRc4Seal)
71 if (WLog_IsLevelActive(log, log_level))
72 WLog_PrintTextMessage(log, log_level, line, file, fkt,
"invalid context->SendRc4Seal");
76 if (!context->SendSigningKey)
78 if (WLog_IsLevelActive(log, log_level))
79 WLog_PrintTextMessage(log, log_level, line, file, fkt,
80 "invalid context->SendSigningKey");
83 if (!context->RecvSigningKey)
85 if (WLog_IsLevelActive(log, log_level))
86 WLog_PrintTextMessage(log, log_level, line, file, fkt,
87 "invalid context->RecvSigningKey");
90 if (!context->SendSealingKey)
92 if (WLog_IsLevelActive(log, log_level))
93 WLog_PrintTextMessage(log, log_level, line, file, fkt,
94 "invalid context->SendSealingKey");
97 if (!context->RecvSealingKey)
99 if (WLog_IsLevelActive(log, log_level))
100 WLog_PrintTextMessage(log, log_level, line, file, fkt,
101 "invalid context->RecvSealingKey");
107static char* get_name(COMPUTER_NAME_FORMAT type)
111 if (GetComputerNameExA(type,
nullptr, &nSize))
114 if (GetLastError() != ERROR_MORE_DATA)
117 char* computerName = calloc(1, nSize);
122 if (!GetComputerNameExA(type, computerName, &nSize))
131static int ntlm_SetContextWorkstation(
NTLM_CONTEXT* context,
char* Workstation)
133 char* ws = Workstation;
134 CHAR* computerName =
nullptr;
136 WINPR_ASSERT(context);
140 computerName = get_name(ComputerNameNetBIOS);
147 context->Workstation.Buffer = ConvertUtf8ToWCharAlloc(ws, &len);
151 if (!context->Workstation.Buffer || (len > UINT16_MAX /
sizeof(WCHAR)))
154 context->Workstation.Length = (USHORT)(len *
sizeof(WCHAR));
158static int ntlm_SetContextServicePrincipalNameW(
NTLM_CONTEXT* context, LPWSTR ServicePrincipalName)
160 WINPR_ASSERT(context);
162 if (!ServicePrincipalName)
164 context->ServicePrincipalName.Buffer =
nullptr;
165 context->ServicePrincipalName.Length = 0;
169 context->ServicePrincipalName.Length = (USHORT)(_wcslen(ServicePrincipalName) * 2);
170 context->ServicePrincipalName.Buffer = (PWSTR)malloc(context->ServicePrincipalName.Length + 2);
172 if (!context->ServicePrincipalName.Buffer)
175 memcpy(context->ServicePrincipalName.Buffer, ServicePrincipalName,
176 context->ServicePrincipalName.Length + 2);
180static int ntlm_SetContextTargetName(
NTLM_CONTEXT* context,
char* TargetName)
182 char* name = TargetName;
184 CHAR* computerName =
nullptr;
186 WINPR_ASSERT(context);
190 if (GetComputerNameExA(ComputerNameNetBIOS,
nullptr, &nSize) ||
191 GetLastError() != ERROR_MORE_DATA)
194 computerName = calloc(nSize,
sizeof(CHAR));
199 if (!GetComputerNameExA(ComputerNameNetBIOS, computerName, &nSize))
205 if (nSize > MAX_COMPUTERNAME_LENGTH)
206 computerName[MAX_COMPUTERNAME_LENGTH] =
'\0';
217 context->TargetName.pvBuffer = ConvertUtf8ToWCharAlloc(name, &len);
219 if (!context->TargetName.pvBuffer || (len > UINT16_MAX /
sizeof(WCHAR)))
221 free(context->TargetName.pvBuffer);
222 context->TargetName.pvBuffer =
nullptr;
230 context->TargetName.cbBuffer = (USHORT)(len *
sizeof(WCHAR));
249 context->NTLMv2 = TRUE;
250 context->UseMIC = FALSE;
251 context->SendVersionInfo = TRUE;
252 context->SendSingleHostData = FALSE;
253 context->SendWorkstationName = TRUE;
254 context->NegotiateKeyExchange = TRUE;
255 context->UseSamFileDatabase = TRUE;
258 char* key = winpr_getApplicatonDetailsRegKey(WINPR_KEY);
262 RegOpenKeyExA(HKEY_LOCAL_MACHINE, key, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
265 if (status == ERROR_SUCCESS)
267 if (RegQueryValueEx(hKey, _T(
"NTLMv2"),
nullptr, &dwType, (BYTE*)&dwValue,
268 &dwSize) == ERROR_SUCCESS)
269 context->NTLMv2 = dwValue ? 1 : 0;
271 if (RegQueryValueEx(hKey, _T(
"UseMIC"),
nullptr, &dwType, (BYTE*)&dwValue,
272 &dwSize) == ERROR_SUCCESS)
273 context->UseMIC = dwValue ? 1 : 0;
275 if (RegQueryValueEx(hKey, _T(
"SendVersionInfo"),
nullptr, &dwType, (BYTE*)&dwValue,
276 &dwSize) == ERROR_SUCCESS)
277 context->SendVersionInfo = dwValue ? 1 : 0;
279 if (RegQueryValueEx(hKey, _T(
"SendSingleHostData"),
nullptr, &dwType,
280 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
281 context->SendSingleHostData = dwValue ? 1 : 0;
283 if (RegQueryValueEx(hKey, _T(
"SendWorkstationName"),
nullptr, &dwType,
284 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
285 context->SendWorkstationName = dwValue ? 1 : 0;
287 if (RegQueryValueEx(hKey, _T(
"WorkstationName"),
nullptr, &dwType,
nullptr,
288 &dwSize) == ERROR_SUCCESS)
290 char* workstation = (
char*)malloc(dwSize + 1);
298 const LONG rc = RegQueryValueExA(hKey,
"WorkstationName",
nullptr, &dwType,
299 (BYTE*)workstation, &dwSize);
300 if (rc != ERROR_SUCCESS)
301 WLog_WARN(TAG,
"Key ''WorkstationName' not found");
302 workstation[dwSize] =
'\0';
304 if (ntlm_SetContextWorkstation(context, workstation) < 0)
323 context->SuppressExtendedProtection = FALSE;
325 RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T(
"System\\CurrentControlSet\\Control\\LSA"), 0,
326 KEY_READ | KEY_WOW64_64KEY, &hKey);
328 if (status == ERROR_SUCCESS)
330 if (RegQueryValueEx(hKey, _T(
"SuppressExtendedProtection"),
nullptr, &dwType,
331 (BYTE*)&dwValue, &dwSize) == ERROR_SUCCESS)
332 context->SuppressExtendedProtection = dwValue ? 1 : 0;
337 context->NegotiateFlags = 0;
338 context->LmCompatibilityLevel = 3;
339 ntlm_change_state(context, NTLM_STATE_INITIAL);
340 FillMemory(context->MachineID,
sizeof(context->MachineID), 0xAA);
343 context->UseMIC = TRUE;
353 winpr_RC4_Free(context->SendRc4Seal);
354 winpr_RC4_Free(context->RecvRc4Seal);
355 sspi_SecBufferFree(&context->NegotiateMessage);
356 sspi_SecBufferFree(&context->ChallengeMessage);
357 sspi_SecBufferFree(&context->AuthenticateMessage);
358 sspi_SecBufferFree(&context->ChallengeTargetInfo);
359 sspi_SecBufferFree(&context->TargetName);
360 sspi_SecBufferFree(&context->NtChallengeResponse);
361 sspi_SecBufferFree(&context->LmChallengeResponse);
362 free(context->ServicePrincipalName.Buffer);
363 free(context->Workstation.Buffer);
367static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
368 WINPR_ATTR_UNUSED SEC_WCHAR* pszPrincipal, WINPR_ATTR_UNUSED SEC_WCHAR* pszPackage,
369 ULONG fCredentialUse, WINPR_ATTR_UNUSED
void* pvLogonID,
void* pAuthData,
370 SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
375 if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) &&
376 (fCredentialUse != SECPKG_CRED_BOTH))
378 return SEC_E_INVALID_PARAMETER;
384 return SEC_E_INTERNAL_ERROR;
386 credentials->fCredentialUse = fCredentialUse;
387 credentials->pGetKeyFn = pGetKeyFn;
388 credentials->pvGetKeyArgument = pvGetKeyArgument;
392 UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData);
394 if (sspi_CopyAuthIdentity(&(credentials->identity),
397 sspi_CredentialsFree(credentials);
398 return SEC_E_INVALID_PARAMETER;
401 if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED)
407 if (settings->samFile)
409 credentials->ntlmSettings.samFile = _strdup(settings->samFile);
410 if (!credentials->ntlmSettings.samFile)
412 sspi_CredentialsFree(credentials);
413 return SEC_E_INSUFFICIENT_MEMORY;
416 credentials->ntlmSettings.hashCallback = settings->hashCallback;
417 credentials->ntlmSettings.hashCallbackArg = settings->hashCallbackArg;
420 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)credentials);
421 sspi_SecureHandleSetUpperPointer(phCredential, (
void*)NTLM_PACKAGE_NAME);
425static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
426 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
427 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
430 SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
431 SEC_WCHAR* principal =
nullptr;
432 SEC_WCHAR*
package = nullptr;
436 principal = ConvertUtf8ToWCharAlloc(pszPrincipal,
nullptr);
442 package = ConvertUtf8ToWCharAlloc(pszPackage, nullptr);
448 ntlm_AcquireCredentialsHandleW(principal, package, fCredentialUse, pvLogonID, pAuthData,
449 pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
458static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(
PCredHandle phCredential)
461 return SEC_E_INVALID_HANDLE;
467 return SEC_E_INVALID_HANDLE;
469 sspi_CredentialsFree(credentials);
473static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(
474 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
475 WINPR_ATTR_UNUSED
void* pBuffer)
477 if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
482 WLog_ERR(TAG,
"TODO: Implement");
483 return SEC_E_UNSUPPORTED_FUNCTION;
486static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(
PCredHandle phCredential,
487 ULONG ulAttribute,
void* pBuffer)
489 return ntlm_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
495static SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(
498 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED
PTimeStamp ptsTimeStamp)
500 SECURITY_STATUS status = 0;
506 if (phContext && !phContext->dwLower && !phContext->dwUpper)
507 return SEC_E_INVALID_HANDLE;
513 context = ntlm_ContextNew();
516 return SEC_E_INSUFFICIENT_MEMORY;
518 context->server = TRUE;
520 if (fContextReq & ASC_REQ_CONFIDENTIALITY)
521 context->confidentiality = TRUE;
523 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
524 context->credentials = credentials;
525 context->SamFile = credentials->ntlmSettings.samFile;
526 context->HashCallback = credentials->ntlmSettings.hashCallback;
527 context->HashCallbackArg = credentials->ntlmSettings.hashCallbackArg;
529 ntlm_SetContextTargetName(context,
nullptr);
530 sspi_SecureHandleSetLowerPointer(phNewContext, context);
531 sspi_SecureHandleSetUpperPointer(phNewContext, (
void*)NTLM_PACKAGE_NAME);
534 switch (ntlm_get_state(context))
536 case NTLM_STATE_INITIAL:
538 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
541 return SEC_E_INVALID_TOKEN;
543 if (pInput->cBuffers < 1)
544 return SEC_E_INVALID_TOKEN;
546 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
549 return SEC_E_INVALID_TOKEN;
551 if (input_buffer->cbBuffer < 1)
552 return SEC_E_INVALID_TOKEN;
554 status = ntlm_read_NegotiateMessage(context, input_buffer);
555 if (status != SEC_I_CONTINUE_NEEDED)
558 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
561 return SEC_E_INVALID_TOKEN;
563 if (pOutput->cBuffers < 1)
564 return SEC_E_INVALID_TOKEN;
566 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
568 if (!output_buffer->BufferType)
569 return SEC_E_INVALID_TOKEN;
571 if (output_buffer->cbBuffer < 1)
572 return SEC_E_INSUFFICIENT_MEMORY;
574 return ntlm_write_ChallengeMessage(context, output_buffer);
577 return SEC_E_OUT_OF_SEQUENCE;
580 case NTLM_STATE_AUTHENTICATE:
583 return SEC_E_INVALID_TOKEN;
585 if (pInput->cBuffers < 1)
586 return SEC_E_INVALID_TOKEN;
588 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
591 return SEC_E_INVALID_TOKEN;
593 if (input_buffer->cbBuffer < 1)
594 return SEC_E_INVALID_TOKEN;
596 status = ntlm_read_AuthenticateMessage(context, input_buffer);
600 for (ULONG i = 0; i < pOutput->cBuffers; i++)
602 pOutput->pBuffers[i].cbBuffer = 0;
603 pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN;
611 return SEC_E_OUT_OF_SEQUENCE;
615static SECURITY_STATUS SEC_ENTRY
616ntlm_ImpersonateSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
621static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
623 WINPR_ATTR_UNUSED ULONG Reserved1, WINPR_ATTR_UNUSED ULONG TargetDataRep,
PSecBufferDesc pInput,
625 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED
PTimeStamp ptsExpiry)
627 SECURITY_STATUS status = 0;
633 if (phContext && !phContext->dwLower && !phContext->dwUpper)
634 return SEC_E_INVALID_HANDLE;
640 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
645 context = ntlm_ContextNew();
648 return SEC_E_INSUFFICIENT_MEMORY;
650 if (fContextReq & ISC_REQ_CONFIDENTIALITY)
651 context->confidentiality = TRUE;
653 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
654 context->credentials = credentials;
656 if (context->Workstation.Length < 1)
658 if (ntlm_SetContextWorkstation(context,
nullptr) < 0)
660 ntlm_ContextFree(context);
661 return SEC_E_INTERNAL_ERROR;
665 if (ntlm_SetContextServicePrincipalNameW(context, pszTargetName) < 0)
667 ntlm_ContextFree(context);
668 return SEC_E_INTERNAL_ERROR;
671 sspi_SecureHandleSetLowerPointer(phNewContext, context);
672 sspi_SecureHandleSetUpperPointer(phNewContext, NTLM_SSP_NAME);
675 if ((!input_buffer) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE))
678 return SEC_E_INVALID_TOKEN;
680 if (pOutput->cBuffers < 1)
681 return SEC_E_INVALID_TOKEN;
683 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
686 return SEC_E_INVALID_TOKEN;
688 if (output_buffer->cbBuffer < 1)
689 return SEC_E_INVALID_TOKEN;
691 if (ntlm_get_state(context) == NTLM_STATE_INITIAL)
692 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
694 if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE)
695 return ntlm_write_NegotiateMessage(context, output_buffer);
697 return SEC_E_OUT_OF_SEQUENCE;
702 return SEC_E_INVALID_TOKEN;
704 if (input_buffer->cbBuffer < 1)
705 return SEC_E_INVALID_TOKEN;
707 PSecBuffer channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
709 if (channel_bindings)
711 context->Bindings.BindingsLength = channel_bindings->cbBuffer;
715 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
717 status = ntlm_read_ChallengeMessage(context, input_buffer);
719 if (status != SEC_I_CONTINUE_NEEDED)
723 return SEC_E_INVALID_TOKEN;
725 if (pOutput->cBuffers < 1)
726 return SEC_E_INVALID_TOKEN;
728 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
731 return SEC_E_INVALID_TOKEN;
733 if (output_buffer->cbBuffer < 1)
734 return SEC_E_INSUFFICIENT_MEMORY;
736 if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)
737 return ntlm_write_AuthenticateMessage(context, output_buffer);
740 return SEC_E_OUT_OF_SEQUENCE;
743 return SEC_E_OUT_OF_SEQUENCE;
749static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
751 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
754 SECURITY_STATUS status = 0;
755 SEC_WCHAR* pszTargetNameW =
nullptr;
759 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName,
nullptr);
761 return SEC_E_INTERNAL_ERROR;
764 status = ntlm_InitializeSecurityContextW(phCredential, phContext, pszTargetNameW, fContextReq,
765 Reserved1, TargetDataRep, pInput, Reserved2,
766 phNewContext, pOutput, pfContextAttr, ptsExpiry);
767 free(pszTargetNameW);
773static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(
PCtxtHandle phContext)
776 ntlm_ContextFree(context);
782 BYTE* blob =
nullptr;
786 WINPR_ASSERT(ntproof);
788 target = &ntlm->ChallengeTargetInfo;
790 if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer))
791 return SEC_E_INSUFFICIENT_MEMORY;
793 blob = (BYTE*)ntproof->pvBuffer;
794 CopyMemory(blob, ntlm->ServerChallenge, 8);
798 CopyMemory(&blob[16], ntlm->Timestamp, 8);
799 CopyMemory(&blob[24], ntlm->ClientChallenge, 8);
802 CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer);
808 BYTE* blob =
nullptr;
812 WINPR_ASSERT(micvalue);
814 msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer +
815 ntlm->AuthenticateMessage.cbBuffer;
817 if (!sspi_SecBufferAlloc(micvalue, msgSize))
818 return SEC_E_INSUFFICIENT_MEMORY;
820 blob = (BYTE*)micvalue->pvBuffer;
821 CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer);
822 blob += ntlm->NegotiateMessage.cbBuffer;
823 CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer);
824 blob += ntlm->ChallengeMessage.cbBuffer;
825 CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer);
826 blob += ntlm->MessageIntegrityCheckOffset;
827 ZeroMemory(blob, 16);
833static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(
PCtxtHandle phContext,
834 ULONG ulAttribute,
void* pBuffer)
837 return SEC_E_INVALID_HANDLE;
840 return SEC_E_INSUFFICIENT_MEMORY;
843 if (!check_context(context))
844 return SEC_E_INVALID_HANDLE;
846 if (ulAttribute == SECPKG_ATTR_SIZES)
849 ContextSizes->cbMaxToken = 2010;
850 ContextSizes->cbMaxSignature = 16;
851 ContextSizes->cbBlockSize = 0;
852 ContextSizes->cbSecurityTrailer = 16;
856 else if (ulAttribute == SECPKG_ATTR_AUTH_IDENTITY)
862 WINPR_ASSERT(AuthIdentity);
863 *AuthIdentity = empty;
865 context->UseSamFileDatabase = FALSE;
866 credentials = context->credentials;
868 if (credentials->identity.UserLength > 0)
870 if (ConvertWCharNToUtf8(credentials->identity.User, credentials->identity.UserLength,
871 AuthIdentity->User, ARRAYSIZE(AuthIdentity->User)) <= 0)
872 return SEC_E_INTERNAL_ERROR;
875 if (credentials->identity.DomainLength > 0)
877 if (ConvertWCharNToUtf8(credentials->identity.Domain,
878 credentials->identity.DomainLength, AuthIdentity->Domain,
879 ARRAYSIZE(AuthIdentity->Domain)) <= 0)
880 return SEC_E_INTERNAL_ERROR;
885 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE)
887 return ntlm_computeProofValue(context, (
SecBuffer*)pBuffer);
889 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_RANDKEY)
894 if (!sspi_SecBufferAlloc(randkey, 16))
895 return (SEC_E_INSUFFICIENT_MEMORY);
897 CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16);
900 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC)
905 if (!sspi_SecBufferAlloc(mic, 16))
906 return (SEC_E_INSUFFICIENT_MEMORY);
908 CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16);
911 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC_VALUE)
913 return ntlm_computeMicValue(context, (
SecBuffer*)pBuffer);
915 else if (ulAttribute == SECPKG_ATTR_PACKAGE_INFO)
920 (
SecPkgInfoA*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
923 return SEC_E_INSUFFICIENT_MEMORY;
925 pPackageInfo->fCapabilities = NTLM_SecPkgInfoA.fCapabilities;
926 pPackageInfo->wVersion = NTLM_SecPkgInfoA.wVersion;
927 pPackageInfo->wRPCID = NTLM_SecPkgInfoA.wRPCID;
928 pPackageInfo->cbMaxToken = NTLM_SecPkgInfoA.cbMaxToken;
929 pPackageInfo->Name = _strdup(NTLM_SecPkgInfoA.Name);
930 pPackageInfo->Comment = _strdup(NTLM_SecPkgInfoA.Comment);
932 if (!pPackageInfo->Name || !pPackageInfo->Comment)
934 sspi_ContextBufferFree(pPackageInfo);
935 return SEC_E_INSUFFICIENT_MEMORY;
937 PackageInfo->PackageInfo = pPackageInfo;
941 WLog_ERR(TAG,
"TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute);
942 return SEC_E_UNSUPPORTED_FUNCTION;
945static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(
PCtxtHandle phContext,
946 ULONG ulAttribute,
void* pBuffer)
948 return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
951static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(
PCtxtHandle phContext,
952 ULONG ulAttribute,
void* pBuffer,
956 return SEC_E_INVALID_HANDLE;
959 return SEC_E_INVALID_PARAMETER;
963 return SEC_E_INVALID_HANDLE;
965 if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_HASH)
970 return SEC_E_INVALID_PARAMETER;
972 if (AuthNtlmHash->Version == 1)
973 CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16);
974 else if (AuthNtlmHash->Version == 2)
975 CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16);
979 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MESSAGE)
984 return SEC_E_INVALID_PARAMETER;
986 if (AuthNtlmMessage->type == 1)
988 sspi_SecBufferFree(&context->NegotiateMessage);
990 if (!sspi_SecBufferAlloc(&context->NegotiateMessage, AuthNtlmMessage->length))
991 return SEC_E_INSUFFICIENT_MEMORY;
993 CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer,
994 AuthNtlmMessage->length);
996 else if (AuthNtlmMessage->type == 2)
998 sspi_SecBufferFree(&context->ChallengeMessage);
1000 if (!sspi_SecBufferAlloc(&context->ChallengeMessage, AuthNtlmMessage->length))
1001 return SEC_E_INSUFFICIENT_MEMORY;
1003 CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer,
1004 AuthNtlmMessage->length);
1006 else if (AuthNtlmMessage->type == 3)
1008 sspi_SecBufferFree(&context->AuthenticateMessage);
1010 if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, AuthNtlmMessage->length))
1011 return SEC_E_INSUFFICIENT_MEMORY;
1013 CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer,
1014 AuthNtlmMessage->length);
1019 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_TIMESTAMP)
1025 return SEC_E_INVALID_PARAMETER;
1027 if (AuthNtlmTimestamp->ChallengeOrResponse)
1028 CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8);
1030 CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8);
1034 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE)
1040 return SEC_E_INVALID_PARAMETER;
1042 CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8);
1045 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE)
1051 return SEC_E_INVALID_PARAMETER;
1053 CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8);
1057 WLog_ERR(TAG,
"TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
1058 return SEC_E_UNSUPPORTED_FUNCTION;
1061static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(
PCtxtHandle phContext,
1062 ULONG ulAttribute,
void* pBuffer,
1065 return ntlm_SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer);
1068static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(
1069 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1070 WINPR_ATTR_UNUSED
void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1072 return SEC_E_UNSUPPORTED_FUNCTION;
1075static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(
1076 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1077 WINPR_ATTR_UNUSED
void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1079 return SEC_E_UNSUPPORTED_FUNCTION;
1082static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
1087static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(
PCtxtHandle phContext,
1088 WINPR_ATTR_UNUSED ULONG fQOP,
1091 const UINT32 SeqNo = MessageSeqNo;
1093 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1094 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1099 if (!check_context(context))
1100 return SEC_E_INVALID_HANDLE;
1102 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1104 SecBuffer* cur = &pMessage->pBuffers[index];
1106 if (cur->BufferType & SECBUFFER_DATA)
1108 else if (cur->BufferType & SECBUFFER_TOKEN)
1109 signature_buffer = cur;
1113 return SEC_E_INVALID_TOKEN;
1115 if (!signature_buffer)
1116 return SEC_E_INVALID_TOKEN;
1119 ULONG length = data_buffer->cbBuffer;
1120 void* data = malloc(length);
1123 return SEC_E_INSUFFICIENT_MEMORY;
1125 CopyMemory(data, data_buffer->pvBuffer, length);
1127 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1129 BOOL success = FALSE;
1133 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1136 winpr_Data_Write_UINT32(&value, SeqNo);
1138 if (!winpr_HMAC_Update(hmac, (
void*)&value, 4))
1140 if (!winpr_HMAC_Update(hmac, data, length))
1142 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1149 winpr_HMAC_Free(hmac);
1153 return SEC_E_INSUFFICIENT_MEMORY;
1157 if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
1159 if (context->confidentiality)
1161 if (!winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
1162 (BYTE*)data_buffer->pvBuffer))
1165 return SEC_E_INSUFFICIENT_MEMORY;
1169 CopyMemory(data_buffer->pvBuffer, data, length);
1172#ifdef WITH_DEBUG_NTLM
1173 WLog_DBG(TAG,
"Data Buffer (length = %" PRIu32
")", length);
1174 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1175 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1176 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1180 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1181 return SEC_E_INSUFFICIENT_MEMORY;
1182 if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
1184 BYTE* signature = signature_buffer->pvBuffer;
1186 winpr_Data_Write_UINT32(signature, version);
1187 CopyMemory(&signature[4], (
void*)checksum, 8);
1188 winpr_Data_Write_UINT32(&signature[12], SeqNo);
1190 context->SendSeqNum++;
1191#ifdef WITH_DEBUG_NTLM
1192 WLog_DBG(TAG,
"Signature (length = %" PRIu32
")", signature_buffer->cbBuffer);
1193 winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
1200 WINPR_ATTR_UNUSED PULONG pfQOP)
1202 const UINT32 SeqNo = (UINT32)MessageSeqNo;
1204 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1205 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1207 BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1211 if (!check_context(context))
1212 return SEC_E_INVALID_HANDLE;
1214 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1216 if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
1217 data_buffer = &pMessage->pBuffers[index];
1218 else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
1219 signature_buffer = &pMessage->pBuffers[index];
1223 return SEC_E_INVALID_TOKEN;
1225 if (!signature_buffer)
1226 return SEC_E_INVALID_TOKEN;
1229 const ULONG length = data_buffer->cbBuffer;
1230 void* data = malloc(length);
1233 return SEC_E_INSUFFICIENT_MEMORY;
1235 CopyMemory(data, data_buffer->pvBuffer, length);
1239 if (context->confidentiality)
1241 if (!winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data,
1242 (BYTE*)data_buffer->pvBuffer))
1245 return SEC_E_INSUFFICIENT_MEMORY;
1249 CopyMemory(data_buffer->pvBuffer, data, length);
1252 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1254 BOOL success = FALSE;
1259 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1262 winpr_Data_Write_UINT32(&value, SeqNo);
1264 if (!winpr_HMAC_Update(hmac, (
void*)&value, 4))
1266 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1268 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1274 winpr_HMAC_Free(hmac);
1278 return SEC_E_INSUFFICIENT_MEMORY;
1281#ifdef WITH_DEBUG_NTLM
1282 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIu32
")", length);
1283 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1284 WLog_DBG(TAG,
"Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1285 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1289 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1290 return SEC_E_MESSAGE_ALTERED;
1293 winpr_Data_Write_UINT32(expected_signature, version);
1294 CopyMemory(&expected_signature[4], (
void*)checksum, 8);
1295 winpr_Data_Write_UINT32(&expected_signature[12], SeqNo);
1296 context->RecvSeqNum++;
1298 if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
1301 WLog_ERR(TAG,
"signature verification failed, something nasty is going on!");
1302#ifdef WITH_DEBUG_NTLM
1303 WLog_ERR(TAG,
"Expected Signature:");
1304 winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
1305 WLog_ERR(TAG,
"Actual Signature:");
1306 winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
1308 return SEC_E_MESSAGE_ALTERED;
1314static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(
PCtxtHandle phContext,
1315 WINPR_ATTR_UNUSED ULONG fQOP,
1318 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1322 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1323 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1325 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1326 if (!check_context(context))
1327 return SEC_E_INVALID_HANDLE;
1329 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1331 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1332 data_buffer = &pMessage->pBuffers[i];
1333 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1334 sig_buffer = &pMessage->pBuffers[i];
1337 if (!data_buffer || !sig_buffer)
1338 return SEC_E_INVALID_TOKEN;
1340 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1342 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1345 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1346 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1348 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1350 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1353 if (!winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum))
1356 BYTE* signature = sig_buffer->pvBuffer;
1357 winpr_Data_Write_UINT32(signature, 1L);
1358 CopyMemory(&signature[4], checksum, 8);
1359 winpr_Data_Write_UINT32(&signature[12], seq_no);
1360 sig_buffer->cbBuffer = 16;
1365 winpr_HMAC_Free(hmac);
1369static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(
PCtxtHandle phContext,
1371 WINPR_ATTR_UNUSED PULONG pfQOP)
1373 SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
1377 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
1378 BYTE checksum[8] = WINPR_C_ARRAY_INIT;
1379 BYTE signature[16] = WINPR_C_ARRAY_INIT;
1381 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1382 if (!check_context(context))
1383 return SEC_E_INVALID_HANDLE;
1385 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1387 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1388 data_buffer = &pMessage->pBuffers[i];
1389 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1390 sig_buffer = &pMessage->pBuffers[i];
1393 if (!data_buffer || !sig_buffer)
1394 return SEC_E_INVALID_TOKEN;
1396 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1398 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1401 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1402 if (!winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4))
1404 if (!winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer))
1406 if (!winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH))
1409 if (!winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum))
1412 winpr_Data_Write_UINT32(signature, 1L);
1413 CopyMemory(&signature[4], checksum, 8);
1414 winpr_Data_Write_UINT32(&signature[12], seq_no);
1417 if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
1418 status = SEC_E_MESSAGE_ALTERED;
1421 winpr_HMAC_Free(hmac);
1428 ntlm_QueryCredentialsAttributesA,
1429 ntlm_AcquireCredentialsHandleA,
1430 ntlm_FreeCredentialsHandle,
1432 ntlm_InitializeSecurityContextA,
1433 ntlm_AcceptSecurityContext,
1435 ntlm_DeleteSecurityContext,
1437 ntlm_QueryContextAttributesA,
1438 ntlm_ImpersonateSecurityContext,
1439 ntlm_RevertSecurityContext,
1441 ntlm_VerifySignature,
1451 ntlm_EncryptMessage,
1452 ntlm_DecryptMessage,
1453 ntlm_SetContextAttributesA,
1454 ntlm_SetCredentialsAttributesA,
1460 ntlm_QueryCredentialsAttributesW,
1461 ntlm_AcquireCredentialsHandleW,
1462 ntlm_FreeCredentialsHandle,
1464 ntlm_InitializeSecurityContextW,
1465 ntlm_AcceptSecurityContext,
1467 ntlm_DeleteSecurityContext,
1469 ntlm_QueryContextAttributesW,
1470 ntlm_ImpersonateSecurityContext,
1471 ntlm_RevertSecurityContext,
1473 ntlm_VerifySignature,
1483 ntlm_EncryptMessage,
1484 ntlm_DecryptMessage,
1485 ntlm_SetContextAttributesW,
1486 ntlm_SetCredentialsAttributesW,
1495 "NTLM Security Package"
1498static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = WINPR_C_ARRAY_INIT;
1499static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = WINPR_C_ARRAY_INIT;
1506 NTLM_SecPkgInfoW_NameBuffer,
1507 NTLM_SecPkgInfoW_CommentBuffer
1510char* ntlm_negotiate_flags_string(
char* buffer,
size_t size, UINT32 flags)
1512 if (!buffer || (size == 0))
1515 (void)_snprintf(buffer, size,
"[0x%08" PRIx32
"] ", flags);
1517 for (
int x = 0; x < 31; x++)
1519 const UINT32 mask = 1 << x;
1520 size_t len = strnlen(buffer, size);
1523 const char* str = ntlm_get_negotiate_string(mask);
1524 const size_t flen = strlen(str);
1526 if ((len > 0) && (buffer[len - 1] !=
' '))
1530 winpr_str_append(
"|", buffer, size,
nullptr);
1534 if (size - len < flen)
1536 winpr_str_append(str, buffer, size,
nullptr);
1543const char* ntlm_message_type_string(UINT32 messageType)
1545 switch (messageType)
1547 case MESSAGE_TYPE_NEGOTIATE:
1548 return "MESSAGE_TYPE_NEGOTIATE";
1549 case MESSAGE_TYPE_CHALLENGE:
1550 return "MESSAGE_TYPE_CHALLENGE";
1551 case MESSAGE_TYPE_AUTHENTICATE:
1552 return "MESSAGE_TYPE_AUTHENTICATE";
1554 return "MESSAGE_TYPE_UNKNOWN";
1558const char* ntlm_state_string(NTLM_STATE state)
1562 case NTLM_STATE_INITIAL:
1563 return "NTLM_STATE_INITIAL";
1564 case NTLM_STATE_NEGOTIATE:
1565 return "NTLM_STATE_NEGOTIATE";
1566 case NTLM_STATE_CHALLENGE:
1567 return "NTLM_STATE_CHALLENGE";
1568 case NTLM_STATE_AUTHENTICATE:
1569 return "NTLM_STATE_AUTHENTICATE";
1570 case NTLM_STATE_FINAL:
1571 return "NTLM_STATE_FINAL";
1573 return "NTLM_STATE_UNKNOWN";
1576void ntlm_change_state(
NTLM_CONTEXT* ntlm, NTLM_STATE state)
1579 WLog_DBG(TAG,
"change state from %s to %s", ntlm_state_string(ntlm->state),
1580 ntlm_state_string(state));
1581 ntlm->state = state;
1590BOOL ntlm_reset_cipher_state(
PSecHandle phContext)
1592 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1596 check_context(context);
1597 winpr_RC4_Free(context->SendRc4Seal);
1598 winpr_RC4_Free(context->RecvRc4Seal);
1599 context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
1600 context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
1602 if (!context->SendRc4Seal)
1604 WLog_ERR(TAG,
"Failed to allocate context->SendRc4Seal");
1607 if (!context->RecvRc4Seal)
1609 WLog_ERR(TAG,
"Failed to allocate context->RecvRc4Seal");
1619 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
1620 ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
1621 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
1622 ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));