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, NULL, &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 = NULL;
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 = NULL;
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 = NULL;
186 WINPR_ASSERT(context);
190 if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &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 = NULL;
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"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) ==
269 context->NTLMv2 = dwValue ? 1 : 0;
271 if (RegQueryValueEx(hKey, _T(
"UseMIC"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) ==
273 context->UseMIC = dwValue ? 1 : 0;
275 if (RegQueryValueEx(hKey, _T(
"SendVersionInfo"), NULL, &dwType, (BYTE*)&dwValue,
276 &dwSize) == ERROR_SUCCESS)
277 context->SendVersionInfo = dwValue ? 1 : 0;
279 if (RegQueryValueEx(hKey, _T(
"SendSingleHostData"), NULL, &dwType, (BYTE*)&dwValue,
280 &dwSize) == ERROR_SUCCESS)
281 context->SendSingleHostData = dwValue ? 1 : 0;
283 if (RegQueryValueEx(hKey, _T(
"SendWorkstationName"), NULL, &dwType, (BYTE*)&dwValue,
284 &dwSize) == ERROR_SUCCESS)
285 context->SendWorkstationName = dwValue ? 1 : 0;
287 if (RegQueryValueEx(hKey, _T(
"WorkstationName"), NULL, &dwType, NULL, &dwSize) ==
290 char* workstation = (
char*)malloc(dwSize + 1);
298 const LONG rc = RegQueryValueExA(hKey,
"WorkstationName", NULL, &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"), NULL, &dwType, (BYTE*)&dwValue,
331 &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 sspi_CopyAuthIdentity(&(credentials->identity),
397 if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED)
403 if (settings->samFile)
405 credentials->ntlmSettings.samFile = _strdup(settings->samFile);
406 if (!credentials->ntlmSettings.samFile)
408 sspi_CredentialsFree(credentials);
409 return SEC_E_INSUFFICIENT_MEMORY;
412 credentials->ntlmSettings.hashCallback = settings->hashCallback;
413 credentials->ntlmSettings.hashCallbackArg = settings->hashCallbackArg;
416 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)credentials);
417 sspi_SecureHandleSetUpperPointer(phCredential, (
void*)NTLM_PACKAGE_NAME);
421static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
422 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
423 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
426 SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
427 SEC_WCHAR* principal = NULL;
428 SEC_WCHAR*
package = NULL;
432 principal = ConvertUtf8ToWCharAlloc(pszPrincipal, NULL);
438 package = ConvertUtf8ToWCharAlloc(pszPackage, NULL);
444 ntlm_AcquireCredentialsHandleW(principal, package, fCredentialUse, pvLogonID, pAuthData,
445 pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
454static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(
PCredHandle phCredential)
457 return SEC_E_INVALID_HANDLE;
463 return SEC_E_INVALID_HANDLE;
465 sspi_CredentialsFree(credentials);
469static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(
470 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
471 WINPR_ATTR_UNUSED
void* pBuffer)
473 if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
478 WLog_ERR(TAG,
"TODO: Implement");
479 return SEC_E_UNSUPPORTED_FUNCTION;
482static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(
PCredHandle phCredential,
483 ULONG ulAttribute,
void* pBuffer)
485 return ntlm_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
491static SECURITY_STATUS SEC_ENTRY ntlm_AcceptSecurityContext(
494 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED
PTimeStamp ptsTimeStamp)
496 SECURITY_STATUS status = 0;
502 if (phContext && !phContext->dwLower && !phContext->dwUpper)
503 return SEC_E_INVALID_HANDLE;
509 context = ntlm_ContextNew();
512 return SEC_E_INSUFFICIENT_MEMORY;
514 context->server = TRUE;
516 if (fContextReq & ASC_REQ_CONFIDENTIALITY)
517 context->confidentiality = TRUE;
519 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
520 context->credentials = credentials;
521 context->SamFile = credentials->ntlmSettings.samFile;
522 context->HashCallback = credentials->ntlmSettings.hashCallback;
523 context->HashCallbackArg = credentials->ntlmSettings.hashCallbackArg;
525 ntlm_SetContextTargetName(context, NULL);
526 sspi_SecureHandleSetLowerPointer(phNewContext, context);
527 sspi_SecureHandleSetUpperPointer(phNewContext, (
void*)NTLM_PACKAGE_NAME);
530 switch (ntlm_get_state(context))
532 case NTLM_STATE_INITIAL:
534 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
537 return SEC_E_INVALID_TOKEN;
539 if (pInput->cBuffers < 1)
540 return SEC_E_INVALID_TOKEN;
542 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
545 return SEC_E_INVALID_TOKEN;
547 if (input_buffer->cbBuffer < 1)
548 return SEC_E_INVALID_TOKEN;
550 status = ntlm_read_NegotiateMessage(context, input_buffer);
551 if (status != SEC_I_CONTINUE_NEEDED)
554 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
557 return SEC_E_INVALID_TOKEN;
559 if (pOutput->cBuffers < 1)
560 return SEC_E_INVALID_TOKEN;
562 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
564 if (!output_buffer->BufferType)
565 return SEC_E_INVALID_TOKEN;
567 if (output_buffer->cbBuffer < 1)
568 return SEC_E_INSUFFICIENT_MEMORY;
570 return ntlm_write_ChallengeMessage(context, output_buffer);
573 return SEC_E_OUT_OF_SEQUENCE;
576 case NTLM_STATE_AUTHENTICATE:
579 return SEC_E_INVALID_TOKEN;
581 if (pInput->cBuffers < 1)
582 return SEC_E_INVALID_TOKEN;
584 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
587 return SEC_E_INVALID_TOKEN;
589 if (input_buffer->cbBuffer < 1)
590 return SEC_E_INVALID_TOKEN;
592 status = ntlm_read_AuthenticateMessage(context, input_buffer);
596 for (ULONG i = 0; i < pOutput->cBuffers; i++)
598 pOutput->pBuffers[i].cbBuffer = 0;
599 pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN;
607 return SEC_E_OUT_OF_SEQUENCE;
611static SECURITY_STATUS SEC_ENTRY
612ntlm_ImpersonateSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
617static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
619 WINPR_ATTR_UNUSED ULONG Reserved1, WINPR_ATTR_UNUSED ULONG TargetDataRep,
PSecBufferDesc pInput,
621 WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED
PTimeStamp ptsExpiry)
623 SECURITY_STATUS status = 0;
629 if (phContext && !phContext->dwLower && !phContext->dwUpper)
630 return SEC_E_INVALID_HANDLE;
636 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
641 context = ntlm_ContextNew();
644 return SEC_E_INSUFFICIENT_MEMORY;
646 if (fContextReq & ISC_REQ_CONFIDENTIALITY)
647 context->confidentiality = TRUE;
649 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
650 context->credentials = credentials;
652 if (context->Workstation.Length < 1)
654 if (ntlm_SetContextWorkstation(context, NULL) < 0)
656 ntlm_ContextFree(context);
657 return SEC_E_INTERNAL_ERROR;
661 if (ntlm_SetContextServicePrincipalNameW(context, pszTargetName) < 0)
663 ntlm_ContextFree(context);
664 return SEC_E_INTERNAL_ERROR;
667 sspi_SecureHandleSetLowerPointer(phNewContext, context);
668 sspi_SecureHandleSetUpperPointer(phNewContext, NTLM_SSP_NAME);
671 if ((!input_buffer) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE))
674 return SEC_E_INVALID_TOKEN;
676 if (pOutput->cBuffers < 1)
677 return SEC_E_INVALID_TOKEN;
679 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
682 return SEC_E_INVALID_TOKEN;
684 if (output_buffer->cbBuffer < 1)
685 return SEC_E_INVALID_TOKEN;
687 if (ntlm_get_state(context) == NTLM_STATE_INITIAL)
688 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
690 if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE)
691 return ntlm_write_NegotiateMessage(context, output_buffer);
693 return SEC_E_OUT_OF_SEQUENCE;
698 return SEC_E_INVALID_TOKEN;
700 if (input_buffer->cbBuffer < 1)
701 return SEC_E_INVALID_TOKEN;
703 PSecBuffer channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
705 if (channel_bindings)
707 context->Bindings.BindingsLength = channel_bindings->cbBuffer;
711 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
713 status = ntlm_read_ChallengeMessage(context, input_buffer);
715 if (status != SEC_I_CONTINUE_NEEDED)
719 return SEC_E_INVALID_TOKEN;
721 if (pOutput->cBuffers < 1)
722 return SEC_E_INVALID_TOKEN;
724 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
727 return SEC_E_INVALID_TOKEN;
729 if (output_buffer->cbBuffer < 1)
730 return SEC_E_INSUFFICIENT_MEMORY;
732 if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)
733 return ntlm_write_AuthenticateMessage(context, output_buffer);
736 return SEC_E_OUT_OF_SEQUENCE;
739 return SEC_E_OUT_OF_SEQUENCE;
745static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
747 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
750 SECURITY_STATUS status = 0;
751 SEC_WCHAR* pszTargetNameW = NULL;
755 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL);
757 return SEC_E_INTERNAL_ERROR;
760 status = ntlm_InitializeSecurityContextW(phCredential, phContext, pszTargetNameW, fContextReq,
761 Reserved1, TargetDataRep, pInput, Reserved2,
762 phNewContext, pOutput, pfContextAttr, ptsExpiry);
763 free(pszTargetNameW);
769static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(
PCtxtHandle phContext)
772 ntlm_ContextFree(context);
782 WINPR_ASSERT(ntproof);
784 target = &ntlm->ChallengeTargetInfo;
786 if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer))
787 return SEC_E_INSUFFICIENT_MEMORY;
789 blob = (BYTE*)ntproof->pvBuffer;
790 CopyMemory(blob, ntlm->ServerChallenge, 8);
794 CopyMemory(&blob[16], ntlm->Timestamp, 8);
795 CopyMemory(&blob[24], ntlm->ClientChallenge, 8);
798 CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer);
808 WINPR_ASSERT(micvalue);
810 msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer +
811 ntlm->AuthenticateMessage.cbBuffer;
813 if (!sspi_SecBufferAlloc(micvalue, msgSize))
814 return SEC_E_INSUFFICIENT_MEMORY;
816 blob = (BYTE*)micvalue->pvBuffer;
817 CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer);
818 blob += ntlm->NegotiateMessage.cbBuffer;
819 CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer);
820 blob += ntlm->ChallengeMessage.cbBuffer;
821 CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer);
822 blob += ntlm->MessageIntegrityCheckOffset;
823 ZeroMemory(blob, 16);
829static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(
PCtxtHandle phContext,
830 ULONG ulAttribute,
void* pBuffer)
833 return SEC_E_INVALID_HANDLE;
836 return SEC_E_INSUFFICIENT_MEMORY;
839 if (!check_context(context))
840 return SEC_E_INVALID_HANDLE;
842 if (ulAttribute == SECPKG_ATTR_SIZES)
845 ContextSizes->cbMaxToken = 2010;
846 ContextSizes->cbMaxSignature = 16;
847 ContextSizes->cbBlockSize = 0;
848 ContextSizes->cbSecurityTrailer = 16;
852 else if (ulAttribute == SECPKG_ATTR_AUTH_IDENTITY)
858 WINPR_ASSERT(AuthIdentity);
859 *AuthIdentity = empty;
861 context->UseSamFileDatabase = FALSE;
862 credentials = context->credentials;
864 if (credentials->identity.UserLength > 0)
866 if (ConvertWCharNToUtf8(credentials->identity.User, credentials->identity.UserLength,
867 AuthIdentity->User, ARRAYSIZE(AuthIdentity->User)) <= 0)
868 return SEC_E_INTERNAL_ERROR;
871 if (credentials->identity.DomainLength > 0)
873 if (ConvertWCharNToUtf8(credentials->identity.Domain,
874 credentials->identity.DomainLength, AuthIdentity->Domain,
875 ARRAYSIZE(AuthIdentity->Domain)) <= 0)
876 return SEC_E_INTERNAL_ERROR;
881 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE)
883 return ntlm_computeProofValue(context, (
SecBuffer*)pBuffer);
885 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_RANDKEY)
890 if (!sspi_SecBufferAlloc(randkey, 16))
891 return (SEC_E_INSUFFICIENT_MEMORY);
893 CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16);
896 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC)
901 if (!sspi_SecBufferAlloc(mic, 16))
902 return (SEC_E_INSUFFICIENT_MEMORY);
904 CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16);
907 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC_VALUE)
909 return ntlm_computeMicValue(context, (
SecBuffer*)pBuffer);
911 else if (ulAttribute == SECPKG_ATTR_PACKAGE_INFO)
916 (
SecPkgInfoA*)sspi_ContextBufferAlloc(QuerySecurityPackageInfoIndex, size);
919 return SEC_E_INSUFFICIENT_MEMORY;
921 pPackageInfo->fCapabilities = NTLM_SecPkgInfoA.fCapabilities;
922 pPackageInfo->wVersion = NTLM_SecPkgInfoA.wVersion;
923 pPackageInfo->wRPCID = NTLM_SecPkgInfoA.wRPCID;
924 pPackageInfo->cbMaxToken = NTLM_SecPkgInfoA.cbMaxToken;
925 pPackageInfo->Name = _strdup(NTLM_SecPkgInfoA.Name);
926 pPackageInfo->Comment = _strdup(NTLM_SecPkgInfoA.Comment);
928 if (!pPackageInfo->Name || !pPackageInfo->Comment)
930 sspi_ContextBufferFree(pPackageInfo);
931 return SEC_E_INSUFFICIENT_MEMORY;
933 PackageInfo->PackageInfo = pPackageInfo;
937 WLog_ERR(TAG,
"TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute);
938 return SEC_E_UNSUPPORTED_FUNCTION;
941static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(
PCtxtHandle phContext,
942 ULONG ulAttribute,
void* pBuffer)
944 return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
947static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(
PCtxtHandle phContext,
948 ULONG ulAttribute,
void* pBuffer,
952 return SEC_E_INVALID_HANDLE;
955 return SEC_E_INVALID_PARAMETER;
959 return SEC_E_INVALID_HANDLE;
961 if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_HASH)
966 return SEC_E_INVALID_PARAMETER;
968 if (AuthNtlmHash->Version == 1)
969 CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16);
970 else if (AuthNtlmHash->Version == 2)
971 CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16);
975 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MESSAGE)
980 return SEC_E_INVALID_PARAMETER;
982 if (AuthNtlmMessage->type == 1)
984 sspi_SecBufferFree(&context->NegotiateMessage);
986 if (!sspi_SecBufferAlloc(&context->NegotiateMessage, AuthNtlmMessage->length))
987 return SEC_E_INSUFFICIENT_MEMORY;
989 CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer,
990 AuthNtlmMessage->length);
992 else if (AuthNtlmMessage->type == 2)
994 sspi_SecBufferFree(&context->ChallengeMessage);
996 if (!sspi_SecBufferAlloc(&context->ChallengeMessage, AuthNtlmMessage->length))
997 return SEC_E_INSUFFICIENT_MEMORY;
999 CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer,
1000 AuthNtlmMessage->length);
1002 else if (AuthNtlmMessage->type == 3)
1004 sspi_SecBufferFree(&context->AuthenticateMessage);
1006 if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, AuthNtlmMessage->length))
1007 return SEC_E_INSUFFICIENT_MEMORY;
1009 CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer,
1010 AuthNtlmMessage->length);
1015 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_TIMESTAMP)
1021 return SEC_E_INVALID_PARAMETER;
1023 if (AuthNtlmTimestamp->ChallengeOrResponse)
1024 CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8);
1026 CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8);
1030 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE)
1036 return SEC_E_INVALID_PARAMETER;
1038 CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8);
1041 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE)
1047 return SEC_E_INVALID_PARAMETER;
1049 CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8);
1053 WLog_ERR(TAG,
"TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
1054 return SEC_E_UNSUPPORTED_FUNCTION;
1057static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(
PCtxtHandle phContext,
1058 ULONG ulAttribute,
void* pBuffer,
1061 return ntlm_SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer);
1064static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(
1065 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1066 WINPR_ATTR_UNUSED
void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1068 return SEC_E_UNSUPPORTED_FUNCTION;
1071static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(
1072 WINPR_ATTR_UNUSED
PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
1073 WINPR_ATTR_UNUSED
void* pBuffer, WINPR_ATTR_UNUSED ULONG cbBuffer)
1075 return SEC_E_UNSUPPORTED_FUNCTION;
1078static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(WINPR_ATTR_UNUSED
PCtxtHandle phContext)
1083static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(
PCtxtHandle phContext,
1084 WINPR_ATTR_UNUSED ULONG fQOP,
1087 const UINT32 SeqNo = MessageSeqNo;
1089 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1090 BYTE checksum[8] = { 0 };
1095 if (!check_context(context))
1096 return SEC_E_INVALID_HANDLE;
1098 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1100 SecBuffer* cur = &pMessage->pBuffers[index];
1102 if (cur->BufferType & SECBUFFER_DATA)
1104 else if (cur->BufferType & SECBUFFER_TOKEN)
1105 signature_buffer = cur;
1109 return SEC_E_INVALID_TOKEN;
1111 if (!signature_buffer)
1112 return SEC_E_INVALID_TOKEN;
1115 ULONG length = data_buffer->cbBuffer;
1116 void* data = malloc(length);
1119 return SEC_E_INSUFFICIENT_MEMORY;
1121 CopyMemory(data, data_buffer->pvBuffer, length);
1123 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1126 winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1128 winpr_Data_Write_UINT32(&value, SeqNo);
1129 winpr_HMAC_Update(hmac, (
void*)&value, 4);
1130 winpr_HMAC_Update(hmac, data, length);
1131 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1132 winpr_HMAC_Free(hmac);
1136 winpr_HMAC_Free(hmac);
1138 return SEC_E_INSUFFICIENT_MEMORY;
1142 if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
1144 if (context->confidentiality)
1145 winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
1146 (BYTE*)data_buffer->pvBuffer);
1148 CopyMemory(data_buffer->pvBuffer, data, length);
1151#ifdef WITH_DEBUG_NTLM
1152 WLog_DBG(TAG,
"Data Buffer (length = %" PRIuz
")", length);
1153 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1154 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1155 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1159 winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum);
1160 if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
1162 BYTE* signature = signature_buffer->pvBuffer;
1164 winpr_Data_Write_UINT32(signature, version);
1165 CopyMemory(&signature[4], (
void*)checksum, 8);
1166 winpr_Data_Write_UINT32(&signature[12], SeqNo);
1168 context->SendSeqNum++;
1169#ifdef WITH_DEBUG_NTLM
1170 WLog_DBG(TAG,
"Signature (length = %" PRIu32
")", signature_buffer->cbBuffer);
1171 winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
1178 WINPR_ATTR_UNUSED PULONG pfQOP)
1180 const UINT32 SeqNo = (UINT32)MessageSeqNo;
1182 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1183 BYTE checksum[8] = { 0 };
1185 BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1189 if (!check_context(context))
1190 return SEC_E_INVALID_HANDLE;
1192 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1194 if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
1195 data_buffer = &pMessage->pBuffers[index];
1196 else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
1197 signature_buffer = &pMessage->pBuffers[index];
1201 return SEC_E_INVALID_TOKEN;
1203 if (!signature_buffer)
1204 return SEC_E_INVALID_TOKEN;
1207 const ULONG length = data_buffer->cbBuffer;
1208 void* data = malloc(length);
1211 return SEC_E_INSUFFICIENT_MEMORY;
1213 CopyMemory(data, data_buffer->pvBuffer, length);
1217 if (context->confidentiality)
1218 winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data, (BYTE*)data_buffer->pvBuffer);
1220 CopyMemory(data_buffer->pvBuffer, data, length);
1223 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1226 winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1228 winpr_Data_Write_UINT32(&value, SeqNo);
1229 winpr_HMAC_Update(hmac, (
void*)&value, 4);
1230 winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
1231 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1232 winpr_HMAC_Free(hmac);
1236 winpr_HMAC_Free(hmac);
1238 return SEC_E_INSUFFICIENT_MEMORY;
1241#ifdef WITH_DEBUG_NTLM
1242 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIuz
")", length);
1243 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1244 WLog_DBG(TAG,
"Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1245 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1249 winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum);
1251 winpr_Data_Write_UINT32(expected_signature, version);
1252 CopyMemory(&expected_signature[4], (
void*)checksum, 8);
1253 winpr_Data_Write_UINT32(&expected_signature[12], SeqNo);
1254 context->RecvSeqNum++;
1256 if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
1259 WLog_ERR(TAG,
"signature verification failed, something nasty is going on!");
1260#ifdef WITH_DEBUG_NTLM
1261 WLog_ERR(TAG,
"Expected Signature:");
1262 winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
1263 WLog_ERR(TAG,
"Actual Signature:");
1264 winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
1266 return SEC_E_MESSAGE_ALTERED;
1272static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(
PCtxtHandle phContext,
1273 WINPR_ATTR_UNUSED ULONG fQOP,
1279 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1280 BYTE checksum[8] = { 0 };
1282 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1283 if (!check_context(context))
1284 return SEC_E_INVALID_HANDLE;
1286 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1288 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1289 data_buffer = &pMessage->pBuffers[i];
1290 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1291 sig_buffer = &pMessage->pBuffers[i];
1294 if (!data_buffer || !sig_buffer)
1295 return SEC_E_INVALID_TOKEN;
1297 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1299 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1301 winpr_HMAC_Free(hmac);
1302 return SEC_E_INTERNAL_ERROR;
1305 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1306 winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4);
1307 winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
1308 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1309 winpr_HMAC_Free(hmac);
1311 winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum);
1313 BYTE* signature = sig_buffer->pvBuffer;
1314 winpr_Data_Write_UINT32(signature, 1L);
1315 CopyMemory(&signature[4], checksum, 8);
1316 winpr_Data_Write_UINT32(&signature[12], seq_no);
1317 sig_buffer->cbBuffer = 16;
1322static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(
PCtxtHandle phContext,
1324 WINPR_ATTR_UNUSED PULONG pfQOP)
1329 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1330 BYTE checksum[8] = { 0 };
1331 BYTE signature[16] = { 0 };
1333 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1334 if (!check_context(context))
1335 return SEC_E_INVALID_HANDLE;
1337 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1339 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1340 data_buffer = &pMessage->pBuffers[i];
1341 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1342 sig_buffer = &pMessage->pBuffers[i];
1345 if (!data_buffer || !sig_buffer)
1346 return SEC_E_INVALID_TOKEN;
1348 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1350 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1352 winpr_HMAC_Free(hmac);
1353 return SEC_E_INTERNAL_ERROR;
1356 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1357 winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4);
1358 winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
1359 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1360 winpr_HMAC_Free(hmac);
1362 winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum);
1364 winpr_Data_Write_UINT32(signature, 1L);
1365 CopyMemory(&signature[4], checksum, 8);
1366 winpr_Data_Write_UINT32(&signature[12], seq_no);
1368 if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
1369 return SEC_E_MESSAGE_ALTERED;
1377 ntlm_QueryCredentialsAttributesA,
1378 ntlm_AcquireCredentialsHandleA,
1379 ntlm_FreeCredentialsHandle,
1381 ntlm_InitializeSecurityContextA,
1382 ntlm_AcceptSecurityContext,
1384 ntlm_DeleteSecurityContext,
1386 ntlm_QueryContextAttributesA,
1387 ntlm_ImpersonateSecurityContext,
1388 ntlm_RevertSecurityContext,
1390 ntlm_VerifySignature,
1400 ntlm_EncryptMessage,
1401 ntlm_DecryptMessage,
1402 ntlm_SetContextAttributesA,
1403 ntlm_SetCredentialsAttributesA,
1409 ntlm_QueryCredentialsAttributesW,
1410 ntlm_AcquireCredentialsHandleW,
1411 ntlm_FreeCredentialsHandle,
1413 ntlm_InitializeSecurityContextW,
1414 ntlm_AcceptSecurityContext,
1416 ntlm_DeleteSecurityContext,
1418 ntlm_QueryContextAttributesW,
1419 ntlm_ImpersonateSecurityContext,
1420 ntlm_RevertSecurityContext,
1422 ntlm_VerifySignature,
1432 ntlm_EncryptMessage,
1433 ntlm_DecryptMessage,
1434 ntlm_SetContextAttributesW,
1435 ntlm_SetCredentialsAttributesW,
1444 "NTLM Security Package"
1447static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = { 0 };
1448static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = { 0 };
1455 NTLM_SecPkgInfoW_NameBuffer,
1456 NTLM_SecPkgInfoW_CommentBuffer
1459char* ntlm_negotiate_flags_string(
char* buffer,
size_t size, UINT32 flags)
1461 if (!buffer || (size == 0))
1464 (void)_snprintf(buffer, size,
"[0x%08" PRIx32
"] ", flags);
1466 for (
int x = 0; x < 31; x++)
1468 const UINT32 mask = 1 << x;
1469 size_t len = strnlen(buffer, size);
1472 const char* str = ntlm_get_negotiate_string(mask);
1473 const size_t flen = strlen(str);
1475 if ((len > 0) && (buffer[len - 1] !=
' '))
1479 winpr_str_append(
"|", buffer, size, NULL);
1483 if (size - len < flen)
1485 winpr_str_append(str, buffer, size, NULL);
1492const char* ntlm_message_type_string(UINT32 messageType)
1494 switch (messageType)
1496 case MESSAGE_TYPE_NEGOTIATE:
1497 return "MESSAGE_TYPE_NEGOTIATE";
1498 case MESSAGE_TYPE_CHALLENGE:
1499 return "MESSAGE_TYPE_CHALLENGE";
1500 case MESSAGE_TYPE_AUTHENTICATE:
1501 return "MESSAGE_TYPE_AUTHENTICATE";
1503 return "MESSAGE_TYPE_UNKNOWN";
1507const char* ntlm_state_string(NTLM_STATE state)
1511 case NTLM_STATE_INITIAL:
1512 return "NTLM_STATE_INITIAL";
1513 case NTLM_STATE_NEGOTIATE:
1514 return "NTLM_STATE_NEGOTIATE";
1515 case NTLM_STATE_CHALLENGE:
1516 return "NTLM_STATE_CHALLENGE";
1517 case NTLM_STATE_AUTHENTICATE:
1518 return "NTLM_STATE_AUTHENTICATE";
1519 case NTLM_STATE_FINAL:
1520 return "NTLM_STATE_FINAL";
1522 return "NTLM_STATE_UNKNOWN";
1525void ntlm_change_state(
NTLM_CONTEXT* ntlm, NTLM_STATE state)
1528 WLog_DBG(TAG,
"change state from %s to %s", ntlm_state_string(ntlm->state),
1529 ntlm_state_string(state));
1530 ntlm->state = state;
1539BOOL ntlm_reset_cipher_state(
PSecHandle phContext)
1541 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1545 check_context(context);
1546 winpr_RC4_Free(context->SendRc4Seal);
1547 winpr_RC4_Free(context->RecvRc4Seal);
1548 context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
1549 context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
1551 if (!context->SendRc4Seal)
1553 WLog_ERR(TAG,
"Failed to allocate context->SendRc4Seal");
1556 if (!context->RecvRc4Seal)
1558 WLog_ERR(TAG,
"Failed to allocate context->RecvRc4Seal");
1568 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
1569 ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
1570 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
1571 ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));