20 #include <winpr/config.h>
22 #include <winpr/crt.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 "../../log.h"
40 #define TAG WINPR_TAG("sspi.NTLM")
42 #define WINPR_KEY "Software\\" WINPR_VENDOR_STRING "\\" WINPR_PRODUCT_STRING "\\WinPR\\NTLM"
44 static char* NTLM_PACKAGE_NAME =
"NTLM";
46 #define check_context(ctx) check_context_((ctx), __FILE__, __func__, __LINE__)
47 static 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");
108 static 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))
132 static 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));
159 static 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);
181 static 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);
359 static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
360 SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
361 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
366 if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) &&
367 (fCredentialUse != SECPKG_CRED_BOTH))
369 return SEC_E_INVALID_PARAMETER;
375 return SEC_E_INTERNAL_ERROR;
377 credentials->fCredentialUse = fCredentialUse;
378 credentials->pGetKeyFn = pGetKeyFn;
379 credentials->pvGetKeyArgument = pvGetKeyArgument;
383 UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData);
385 sspi_CopyAuthIdentity(&(credentials->identity),
388 if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED)
394 if (settings->samFile)
396 credentials->ntlmSettings.samFile = _strdup(settings->samFile);
397 if (!credentials->ntlmSettings.samFile)
399 sspi_CredentialsFree(credentials);
400 return SEC_E_INSUFFICIENT_MEMORY;
403 credentials->ntlmSettings.hashCallback = settings->hashCallback;
404 credentials->ntlmSettings.hashCallbackArg = settings->hashCallbackArg;
407 sspi_SecureHandleSetLowerPointer(phCredential, (
void*)credentials);
408 sspi_SecureHandleSetUpperPointer(phCredential, (
void*)NTLM_PACKAGE_NAME);
412 static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
413 SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse,
void* pvLogonID,
414 void* pAuthData, SEC_GET_KEY_FN pGetKeyFn,
void* pvGetKeyArgument,
PCredHandle phCredential,
417 SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
418 SEC_WCHAR* principal = NULL;
419 SEC_WCHAR*
package = NULL;
423 principal = ConvertUtf8ToWCharAlloc(pszPrincipal, NULL);
429 package = ConvertUtf8ToWCharAlloc(pszPackage, NULL);
435 ntlm_AcquireCredentialsHandleW(principal, package, fCredentialUse, pvLogonID, pAuthData,
436 pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
445 static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(
PCredHandle phCredential)
448 return SEC_E_INVALID_HANDLE;
454 return SEC_E_INVALID_HANDLE;
456 sspi_CredentialsFree(credentials);
460 static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(
PCredHandle phCredential,
461 ULONG ulAttribute,
void* pBuffer)
463 if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
468 WLog_ERR(TAG,
"TODO: Implement");
469 return SEC_E_UNSUPPORTED_FUNCTION;
472 static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(
PCredHandle phCredential,
473 ULONG ulAttribute,
void* pBuffer)
475 return ntlm_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
481 static SECURITY_STATUS SEC_ENTRY
483 ULONG fContextReq, ULONG TargetDataRep,
PCtxtHandle phNewContext,
486 SECURITY_STATUS status = 0;
492 if (phContext && !phContext->dwLower && !phContext->dwUpper)
493 return SEC_E_INVALID_HANDLE;
499 context = ntlm_ContextNew();
502 return SEC_E_INSUFFICIENT_MEMORY;
504 context->server = TRUE;
506 if (fContextReq & ASC_REQ_CONFIDENTIALITY)
507 context->confidentiality = TRUE;
509 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
510 context->credentials = credentials;
511 context->SamFile = credentials->ntlmSettings.samFile;
512 context->HashCallback = credentials->ntlmSettings.hashCallback;
513 context->HashCallbackArg = credentials->ntlmSettings.hashCallbackArg;
515 ntlm_SetContextTargetName(context, NULL);
516 sspi_SecureHandleSetLowerPointer(phNewContext, context);
517 sspi_SecureHandleSetUpperPointer(phNewContext, (
void*)NTLM_PACKAGE_NAME);
520 switch (ntlm_get_state(context))
522 case NTLM_STATE_INITIAL:
524 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
527 return SEC_E_INVALID_TOKEN;
529 if (pInput->cBuffers < 1)
530 return SEC_E_INVALID_TOKEN;
532 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
535 return SEC_E_INVALID_TOKEN;
537 if (input_buffer->cbBuffer < 1)
538 return SEC_E_INVALID_TOKEN;
540 status = ntlm_read_NegotiateMessage(context, input_buffer);
541 if (status != SEC_I_CONTINUE_NEEDED)
544 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
547 return SEC_E_INVALID_TOKEN;
549 if (pOutput->cBuffers < 1)
550 return SEC_E_INVALID_TOKEN;
552 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
554 if (!output_buffer->BufferType)
555 return SEC_E_INVALID_TOKEN;
557 if (output_buffer->cbBuffer < 1)
558 return SEC_E_INSUFFICIENT_MEMORY;
560 return ntlm_write_ChallengeMessage(context, output_buffer);
563 return SEC_E_OUT_OF_SEQUENCE;
566 case NTLM_STATE_AUTHENTICATE:
569 return SEC_E_INVALID_TOKEN;
571 if (pInput->cBuffers < 1)
572 return SEC_E_INVALID_TOKEN;
574 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
577 return SEC_E_INVALID_TOKEN;
579 if (input_buffer->cbBuffer < 1)
580 return SEC_E_INVALID_TOKEN;
582 status = ntlm_read_AuthenticateMessage(context, input_buffer);
586 for (ULONG i = 0; i < pOutput->cBuffers; i++)
588 pOutput->pBuffers[i].cbBuffer = 0;
589 pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN;
597 return SEC_E_OUT_OF_SEQUENCE;
601 static SECURITY_STATUS SEC_ENTRY ntlm_ImpersonateSecurityContext(
PCtxtHandle phContext)
606 static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
608 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
611 SECURITY_STATUS status = 0;
617 if (phContext && !phContext->dwLower && !phContext->dwUpper)
618 return SEC_E_INVALID_HANDLE;
624 input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
629 context = ntlm_ContextNew();
632 return SEC_E_INSUFFICIENT_MEMORY;
634 if (fContextReq & ISC_REQ_CONFIDENTIALITY)
635 context->confidentiality = TRUE;
637 credentials = (
SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
638 context->credentials = credentials;
640 if (context->Workstation.Length < 1)
642 if (ntlm_SetContextWorkstation(context, NULL) < 0)
644 ntlm_ContextFree(context);
645 return SEC_E_INTERNAL_ERROR;
649 if (ntlm_SetContextServicePrincipalNameW(context, pszTargetName) < 0)
651 ntlm_ContextFree(context);
652 return SEC_E_INTERNAL_ERROR;
655 sspi_SecureHandleSetLowerPointer(phNewContext, context);
656 sspi_SecureHandleSetUpperPointer(phNewContext, NTLM_SSP_NAME);
659 if ((!input_buffer) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE))
662 return SEC_E_INVALID_TOKEN;
664 if (pOutput->cBuffers < 1)
665 return SEC_E_INVALID_TOKEN;
667 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
670 return SEC_E_INVALID_TOKEN;
672 if (output_buffer->cbBuffer < 1)
673 return SEC_E_INVALID_TOKEN;
675 if (ntlm_get_state(context) == NTLM_STATE_INITIAL)
676 ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
678 if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE)
679 return ntlm_write_NegotiateMessage(context, output_buffer);
681 return SEC_E_OUT_OF_SEQUENCE;
686 return SEC_E_INVALID_TOKEN;
688 if (input_buffer->cbBuffer < 1)
689 return SEC_E_INVALID_TOKEN;
691 PSecBuffer channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
693 if (channel_bindings)
695 context->Bindings.BindingsLength = channel_bindings->cbBuffer;
699 if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
701 status = ntlm_read_ChallengeMessage(context, input_buffer);
703 if (status != SEC_I_CONTINUE_NEEDED)
707 return SEC_E_INVALID_TOKEN;
709 if (pOutput->cBuffers < 1)
710 return SEC_E_INVALID_TOKEN;
712 output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
715 return SEC_E_INVALID_TOKEN;
717 if (output_buffer->cbBuffer < 1)
718 return SEC_E_INSUFFICIENT_MEMORY;
720 if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)
721 return ntlm_write_AuthenticateMessage(context, output_buffer);
724 return SEC_E_OUT_OF_SEQUENCE;
727 return SEC_E_OUT_OF_SEQUENCE;
733 static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
735 ULONG Reserved1, ULONG TargetDataRep,
PSecBufferDesc pInput, ULONG Reserved2,
738 SECURITY_STATUS status = 0;
739 SEC_WCHAR* pszTargetNameW = NULL;
743 pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL);
745 return SEC_E_INTERNAL_ERROR;
748 status = ntlm_InitializeSecurityContextW(phCredential, phContext, pszTargetNameW, fContextReq,
749 Reserved1, TargetDataRep, pInput, Reserved2,
750 phNewContext, pOutput, pfContextAttr, ptsExpiry);
751 free(pszTargetNameW);
757 static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(
PCtxtHandle phContext)
760 ntlm_ContextFree(context);
770 WINPR_ASSERT(ntproof);
772 target = &ntlm->ChallengeTargetInfo;
774 if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer))
775 return SEC_E_INSUFFICIENT_MEMORY;
777 blob = (BYTE*)ntproof->pvBuffer;
778 CopyMemory(blob, ntlm->ServerChallenge, 8);
782 CopyMemory(&blob[16], ntlm->Timestamp, 8);
783 CopyMemory(&blob[24], ntlm->ClientChallenge, 8);
786 CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer);
796 WINPR_ASSERT(micvalue);
798 msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer +
799 ntlm->AuthenticateMessage.cbBuffer;
801 if (!sspi_SecBufferAlloc(micvalue, msgSize))
802 return SEC_E_INSUFFICIENT_MEMORY;
804 blob = (BYTE*)micvalue->pvBuffer;
805 CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer);
806 blob += ntlm->NegotiateMessage.cbBuffer;
807 CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer);
808 blob += ntlm->ChallengeMessage.cbBuffer;
809 CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer);
810 blob += ntlm->MessageIntegrityCheckOffset;
811 ZeroMemory(blob, 16);
817 static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(
PCtxtHandle phContext,
818 ULONG ulAttribute,
void* pBuffer)
821 return SEC_E_INVALID_HANDLE;
824 return SEC_E_INSUFFICIENT_MEMORY;
827 if (!check_context(context))
828 return SEC_E_INVALID_HANDLE;
830 if (ulAttribute == SECPKG_ATTR_SIZES)
833 ContextSizes->cbMaxToken = 2010;
834 ContextSizes->cbMaxSignature = 16;
835 ContextSizes->cbBlockSize = 0;
836 ContextSizes->cbSecurityTrailer = 16;
840 else if (ulAttribute == SECPKG_ATTR_AUTH_IDENTITY)
846 WINPR_ASSERT(AuthIdentity);
847 *AuthIdentity = empty;
849 context->UseSamFileDatabase = FALSE;
850 credentials = context->credentials;
852 if (credentials->identity.UserLength > 0)
854 if (ConvertWCharNToUtf8(credentials->identity.User, credentials->identity.UserLength,
855 AuthIdentity->User, ARRAYSIZE(AuthIdentity->User)) <= 0)
856 return SEC_E_INTERNAL_ERROR;
859 if (credentials->identity.DomainLength > 0)
861 if (ConvertWCharNToUtf8(credentials->identity.Domain,
862 credentials->identity.DomainLength, AuthIdentity->Domain,
863 ARRAYSIZE(AuthIdentity->Domain)) <= 0)
864 return SEC_E_INTERNAL_ERROR;
869 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE)
871 return ntlm_computeProofValue(context, (
SecBuffer*)pBuffer);
873 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_RANDKEY)
878 if (!sspi_SecBufferAlloc(randkey, 16))
879 return (SEC_E_INSUFFICIENT_MEMORY);
881 CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16);
884 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC)
889 if (!sspi_SecBufferAlloc(mic, 16))
890 return (SEC_E_INSUFFICIENT_MEMORY);
892 CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16);
895 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC_VALUE)
897 return ntlm_computeMicValue(context, (
SecBuffer*)pBuffer);
900 WLog_ERR(TAG,
"TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute);
901 return SEC_E_UNSUPPORTED_FUNCTION;
904 static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(
PCtxtHandle phContext,
905 ULONG ulAttribute,
void* pBuffer)
907 return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
910 static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(
PCtxtHandle phContext,
911 ULONG ulAttribute,
void* pBuffer,
915 return SEC_E_INVALID_HANDLE;
918 return SEC_E_INVALID_PARAMETER;
922 return SEC_E_INVALID_HANDLE;
924 if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_HASH)
929 return SEC_E_INVALID_PARAMETER;
931 if (AuthNtlmHash->Version == 1)
932 CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16);
933 else if (AuthNtlmHash->Version == 2)
934 CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16);
938 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MESSAGE)
943 return SEC_E_INVALID_PARAMETER;
945 if (AuthNtlmMessage->type == 1)
947 sspi_SecBufferFree(&context->NegotiateMessage);
949 if (!sspi_SecBufferAlloc(&context->NegotiateMessage, AuthNtlmMessage->length))
950 return SEC_E_INSUFFICIENT_MEMORY;
952 CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer,
953 AuthNtlmMessage->length);
955 else if (AuthNtlmMessage->type == 2)
957 sspi_SecBufferFree(&context->ChallengeMessage);
959 if (!sspi_SecBufferAlloc(&context->ChallengeMessage, AuthNtlmMessage->length))
960 return SEC_E_INSUFFICIENT_MEMORY;
962 CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer,
963 AuthNtlmMessage->length);
965 else if (AuthNtlmMessage->type == 3)
967 sspi_SecBufferFree(&context->AuthenticateMessage);
969 if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, AuthNtlmMessage->length))
970 return SEC_E_INSUFFICIENT_MEMORY;
972 CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer,
973 AuthNtlmMessage->length);
978 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_TIMESTAMP)
984 return SEC_E_INVALID_PARAMETER;
986 if (AuthNtlmTimestamp->ChallengeOrResponse)
987 CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8);
989 CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8);
993 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE)
999 return SEC_E_INVALID_PARAMETER;
1001 CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8);
1004 else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE)
1010 return SEC_E_INVALID_PARAMETER;
1012 CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8);
1016 WLog_ERR(TAG,
"TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
1017 return SEC_E_UNSUPPORTED_FUNCTION;
1020 static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(
PCtxtHandle phContext,
1021 ULONG ulAttribute,
void* pBuffer,
1024 return ntlm_SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer);
1027 static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(
PCredHandle phCredential,
1028 ULONG ulAttribute,
void* pBuffer,
1031 return SEC_E_UNSUPPORTED_FUNCTION;
1034 static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(
PCredHandle phCredential,
1035 ULONG ulAttribute,
void* pBuffer,
1038 return SEC_E_UNSUPPORTED_FUNCTION;
1041 static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(
PCtxtHandle phContext)
1046 static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(
PCtxtHandle phContext, ULONG fQOP,
1049 const UINT32 SeqNo = MessageSeqNo;
1051 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1052 BYTE checksum[8] = { 0 };
1057 if (!check_context(context))
1058 return SEC_E_INVALID_HANDLE;
1060 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1062 SecBuffer* cur = &pMessage->pBuffers[index];
1064 if (cur->BufferType & SECBUFFER_DATA)
1066 else if (cur->BufferType & SECBUFFER_TOKEN)
1067 signature_buffer = cur;
1071 return SEC_E_INVALID_TOKEN;
1073 if (!signature_buffer)
1074 return SEC_E_INVALID_TOKEN;
1077 ULONG length = data_buffer->cbBuffer;
1078 void* data = malloc(length);
1081 return SEC_E_INSUFFICIENT_MEMORY;
1083 CopyMemory(data, data_buffer->pvBuffer, length);
1085 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1088 winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1090 winpr_Data_Write_UINT32(&value, SeqNo);
1091 winpr_HMAC_Update(hmac, (
void*)&value, 4);
1092 winpr_HMAC_Update(hmac, data, length);
1093 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1094 winpr_HMAC_Free(hmac);
1098 winpr_HMAC_Free(hmac);
1100 return SEC_E_INSUFFICIENT_MEMORY;
1104 if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
1106 if (context->confidentiality)
1107 winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
1108 (BYTE*)data_buffer->pvBuffer);
1110 CopyMemory(data_buffer->pvBuffer, data, length);
1113 #ifdef WITH_DEBUG_NTLM
1114 WLog_DBG(TAG,
"Data Buffer (length = %" PRIuz
")", length);
1115 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1116 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1117 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1121 winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum);
1122 if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
1124 BYTE* signature = signature_buffer->pvBuffer;
1126 winpr_Data_Write_UINT32(signature, version);
1127 CopyMemory(&signature[4], (
void*)checksum, 8);
1128 winpr_Data_Write_UINT32(&signature[12], SeqNo);
1130 context->SendSeqNum++;
1131 #ifdef WITH_DEBUG_NTLM
1132 WLog_DBG(TAG,
"Signature (length = %" PRIu32
")", signature_buffer->cbBuffer);
1133 winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
1139 ULONG MessageSeqNo, PULONG pfQOP)
1141 const UINT32 SeqNo = (UINT32)MessageSeqNo;
1143 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1144 BYTE checksum[8] = { 0 };
1146 BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1150 if (!check_context(context))
1151 return SEC_E_INVALID_HANDLE;
1153 for (ULONG index = 0; index < pMessage->cBuffers; index++)
1155 if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
1156 data_buffer = &pMessage->pBuffers[index];
1157 else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
1158 signature_buffer = &pMessage->pBuffers[index];
1162 return SEC_E_INVALID_TOKEN;
1164 if (!signature_buffer)
1165 return SEC_E_INVALID_TOKEN;
1168 const ULONG length = data_buffer->cbBuffer;
1169 void* data = malloc(length);
1172 return SEC_E_INSUFFICIENT_MEMORY;
1174 CopyMemory(data, data_buffer->pvBuffer, length);
1178 if (context->confidentiality)
1179 winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data, (BYTE*)data_buffer->pvBuffer);
1181 CopyMemory(data_buffer->pvBuffer, data, length);
1184 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1187 winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1189 winpr_Data_Write_UINT32(&value, SeqNo);
1190 winpr_HMAC_Update(hmac, (
void*)&value, 4);
1191 winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
1192 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1193 winpr_HMAC_Free(hmac);
1197 winpr_HMAC_Free(hmac);
1199 return SEC_E_INSUFFICIENT_MEMORY;
1202 #ifdef WITH_DEBUG_NTLM
1203 WLog_DBG(TAG,
"Encrypted Data Buffer (length = %" PRIuz
")", length);
1204 winpr_HexDump(TAG, WLOG_DEBUG, data, length);
1205 WLog_DBG(TAG,
"Data Buffer (length = %" PRIu32
")", data_buffer->cbBuffer);
1206 winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
1210 winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum);
1212 winpr_Data_Write_UINT32(expected_signature, version);
1213 CopyMemory(&expected_signature[4], (
void*)checksum, 8);
1214 winpr_Data_Write_UINT32(&expected_signature[12], SeqNo);
1215 context->RecvSeqNum++;
1217 if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
1220 WLog_ERR(TAG,
"signature verification failed, something nasty is going on!");
1221 #ifdef WITH_DEBUG_NTLM
1222 WLog_ERR(TAG,
"Expected Signature:");
1223 winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
1224 WLog_ERR(TAG,
"Actual Signature:");
1225 winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
1227 return SEC_E_MESSAGE_ALTERED;
1233 static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(
PCtxtHandle phContext, ULONG fQOP,
1239 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1240 BYTE checksum[8] = { 0 };
1242 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1243 if (!check_context(context))
1244 return SEC_E_INVALID_HANDLE;
1246 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1248 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1249 data_buffer = &pMessage->pBuffers[i];
1250 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1251 sig_buffer = &pMessage->pBuffers[i];
1254 if (!data_buffer || !sig_buffer)
1255 return SEC_E_INVALID_TOKEN;
1257 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1259 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
1261 winpr_HMAC_Free(hmac);
1262 return SEC_E_INTERNAL_ERROR;
1265 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1266 winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4);
1267 winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
1268 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1269 winpr_HMAC_Free(hmac);
1271 winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum);
1273 BYTE* signature = sig_buffer->pvBuffer;
1274 winpr_Data_Write_UINT32(signature, 1L);
1275 CopyMemory(&signature[4], checksum, 8);
1276 winpr_Data_Write_UINT32(&signature[12], seq_no);
1277 sig_buffer->cbBuffer = 16;
1282 static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(
PCtxtHandle phContext,
1289 BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
1290 BYTE checksum[8] = { 0 };
1291 BYTE signature[16] = { 0 };
1293 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1294 if (!check_context(context))
1295 return SEC_E_INVALID_HANDLE;
1297 for (ULONG i = 0; i < pMessage->cBuffers; i++)
1299 if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
1300 data_buffer = &pMessage->pBuffers[i];
1301 else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
1302 sig_buffer = &pMessage->pBuffers[i];
1305 if (!data_buffer || !sig_buffer)
1306 return SEC_E_INVALID_TOKEN;
1308 WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
1310 if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
1312 winpr_HMAC_Free(hmac);
1313 return SEC_E_INTERNAL_ERROR;
1316 winpr_Data_Write_UINT32(&seq_no, MessageSeqNo);
1317 winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4);
1318 winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
1319 winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
1320 winpr_HMAC_Free(hmac);
1322 winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum);
1324 winpr_Data_Write_UINT32(signature, 1L);
1325 CopyMemory(&signature[4], checksum, 8);
1326 winpr_Data_Write_UINT32(&signature[12], seq_no);
1328 if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
1329 return SEC_E_MESSAGE_ALTERED;
1337 ntlm_QueryCredentialsAttributesA,
1338 ntlm_AcquireCredentialsHandleA,
1339 ntlm_FreeCredentialsHandle,
1341 ntlm_InitializeSecurityContextA,
1342 ntlm_AcceptSecurityContext,
1344 ntlm_DeleteSecurityContext,
1346 ntlm_QueryContextAttributesA,
1347 ntlm_ImpersonateSecurityContext,
1348 ntlm_RevertSecurityContext,
1350 ntlm_VerifySignature,
1360 ntlm_EncryptMessage,
1361 ntlm_DecryptMessage,
1362 ntlm_SetContextAttributesA,
1363 ntlm_SetCredentialsAttributesA,
1369 ntlm_QueryCredentialsAttributesW,
1370 ntlm_AcquireCredentialsHandleW,
1371 ntlm_FreeCredentialsHandle,
1373 ntlm_InitializeSecurityContextW,
1374 ntlm_AcceptSecurityContext,
1376 ntlm_DeleteSecurityContext,
1378 ntlm_QueryContextAttributesW,
1379 ntlm_ImpersonateSecurityContext,
1380 ntlm_RevertSecurityContext,
1382 ntlm_VerifySignature,
1392 ntlm_EncryptMessage,
1393 ntlm_DecryptMessage,
1394 ntlm_SetContextAttributesW,
1395 ntlm_SetCredentialsAttributesW,
1404 "NTLM Security Package"
1407 static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = { 0 };
1408 static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = { 0 };
1415 NTLM_SecPkgInfoW_NameBuffer,
1416 NTLM_SecPkgInfoW_CommentBuffer
1419 char* ntlm_negotiate_flags_string(
char* buffer,
size_t size, UINT32 flags)
1421 if (!buffer || (size == 0))
1424 (void)_snprintf(buffer, size,
"[0x%08" PRIx32
"] ", flags);
1426 for (
int x = 0; x < 31; x++)
1428 const UINT32 mask = 1 << x;
1429 size_t len = strnlen(buffer, size);
1432 const char* str = ntlm_get_negotiate_string(mask);
1433 const size_t flen = strlen(str);
1435 if ((len > 0) && (buffer[len - 1] !=
' '))
1439 winpr_str_append(
"|", buffer, size, NULL);
1443 if (size - len < flen)
1445 winpr_str_append(str, buffer, size, NULL);
1452 const char* ntlm_message_type_string(UINT32 messageType)
1454 switch (messageType)
1456 case MESSAGE_TYPE_NEGOTIATE:
1457 return "MESSAGE_TYPE_NEGOTIATE";
1458 case MESSAGE_TYPE_CHALLENGE:
1459 return "MESSAGE_TYPE_CHALLENGE";
1460 case MESSAGE_TYPE_AUTHENTICATE:
1461 return "MESSAGE_TYPE_AUTHENTICATE";
1463 return "MESSAGE_TYPE_UNKNOWN";
1467 const char* ntlm_state_string(NTLM_STATE state)
1471 case NTLM_STATE_INITIAL:
1472 return "NTLM_STATE_INITIAL";
1473 case NTLM_STATE_NEGOTIATE:
1474 return "NTLM_STATE_NEGOTIATE";
1475 case NTLM_STATE_CHALLENGE:
1476 return "NTLM_STATE_CHALLENGE";
1477 case NTLM_STATE_AUTHENTICATE:
1478 return "NTLM_STATE_AUTHENTICATE";
1479 case NTLM_STATE_FINAL:
1480 return "NTLM_STATE_FINAL";
1482 return "NTLM_STATE_UNKNOWN";
1485 void ntlm_change_state(
NTLM_CONTEXT* ntlm, NTLM_STATE state)
1488 WLog_DBG(TAG,
"change state from %s to %s", ntlm_state_string(ntlm->state),
1489 ntlm_state_string(state));
1490 ntlm->state = state;
1499 BOOL ntlm_reset_cipher_state(
PSecHandle phContext)
1501 NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
1505 check_context(context);
1506 winpr_RC4_Free(context->SendRc4Seal);
1507 winpr_RC4_Free(context->RecvRc4Seal);
1508 context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
1509 context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
1511 if (!context->SendRc4Seal)
1513 WLog_ERR(TAG,
"Failed to allocate context->SendRc4Seal");
1516 if (!context->RecvRc4Seal)
1518 WLog_ERR(TAG,
"Failed to allocate context->RecvRc4Seal");
1526 BOOL NTLM_init(
void)
1528 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
1529 ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
1530 InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
1531 ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));