summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/sspi/NTLM
diff options
context:
space:
mode:
Diffstat (limited to 'winpr/libwinpr/sspi/NTLM')
-rw-r--r--winpr/libwinpr/sspi/NTLM/ntlm.c1526
-rw-r--r--winpr/libwinpr/sspi/NTLM/ntlm.h301
-rw-r--r--winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c775
-rw-r--r--winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h42
-rw-r--r--winpr/libwinpr/sspi/NTLM/ntlm_compute.c887
-rw-r--r--winpr/libwinpr/sspi/NTLM/ntlm_compute.h64
-rw-r--r--winpr/libwinpr/sspi/NTLM/ntlm_export.h40
-rw-r--r--winpr/libwinpr/sspi/NTLM/ntlm_message.c1397
-rw-r--r--winpr/libwinpr/sspi/NTLM/ntlm_message.h36
9 files changed, 5068 insertions, 0 deletions
diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.c b/winpr/libwinpr/sspi/NTLM/ntlm.c
new file mode 100644
index 0000000..6a2ee6a
--- /dev/null
+++ b/winpr/libwinpr/sspi/NTLM/ntlm.c
@@ -0,0 +1,1526 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * NTLM Security Package
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/sspi.h>
+#include <winpr/print.h>
+#include <winpr/string.h>
+#include <winpr/tchar.h>
+#include <winpr/sysinfo.h>
+#include <winpr/registry.h>
+#include <winpr/endian.h>
+#include <winpr/build-config.h>
+
+#include "ntlm.h"
+#include "ntlm_export.h"
+#include "../sspi.h"
+
+#include "ntlm_message.h"
+
+#include "../../log.h"
+#define TAG WINPR_TAG("sspi.NTLM")
+
+#define WINPR_KEY "Software\\" WINPR_VENDOR_STRING "\\" WINPR_PRODUCT_STRING "\\WinPR\\NTLM"
+
+static char* NTLM_PACKAGE_NAME = "NTLM";
+
+#define check_context(ctx) check_context_((ctx), __FILE__, __func__, __LINE__)
+static BOOL check_context_(NTLM_CONTEXT* context, const char* file, const char* fkt, size_t line)
+{
+ BOOL rc = TRUE;
+ wLog* log = WLog_Get(TAG);
+ const DWORD log_level = WLOG_ERROR;
+
+ if (!context)
+ {
+ if (WLog_IsLevelActive(log, log_level))
+ WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
+ "invalid context");
+
+ return FALSE;
+ }
+
+ if (!context->RecvRc4Seal)
+ {
+ if (WLog_IsLevelActive(log, log_level))
+ WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
+ "invalid context->RecvRc4Seal");
+ rc = FALSE;
+ }
+ if (!context->SendRc4Seal)
+ {
+ if (WLog_IsLevelActive(log, log_level))
+ WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
+ "invalid context->SendRc4Seal");
+ rc = FALSE;
+ }
+
+ if (!context->SendSigningKey)
+ {
+ if (WLog_IsLevelActive(log, log_level))
+ WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
+ "invalid context->SendSigningKey");
+ rc = FALSE;
+ }
+ if (!context->RecvSigningKey)
+ {
+ if (WLog_IsLevelActive(log, log_level))
+ WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
+ "invalid context->RecvSigningKey");
+ rc = FALSE;
+ }
+ if (!context->SendSealingKey)
+ {
+ if (WLog_IsLevelActive(log, log_level))
+ WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
+ "invalid context->SendSealingKey");
+ rc = FALSE;
+ }
+ if (!context->RecvSealingKey)
+ {
+ if (WLog_IsLevelActive(log, log_level))
+ WLog_PrintMessage(log, WLOG_MESSAGE_TEXT, log_level, line, file, fkt,
+ "invalid context->RecvSealingKey");
+ rc = FALSE;
+ }
+ return rc;
+}
+
+static int ntlm_SetContextWorkstation(NTLM_CONTEXT* context, char* Workstation)
+{
+ char* ws = Workstation;
+ DWORD nSize = 0;
+ CHAR* computerName = NULL;
+
+ WINPR_ASSERT(context);
+
+ if (!Workstation)
+ {
+ if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) ||
+ GetLastError() != ERROR_MORE_DATA)
+ return -1;
+
+ computerName = calloc(nSize, sizeof(CHAR));
+
+ if (!computerName)
+ return -1;
+
+ if (!GetComputerNameExA(ComputerNameNetBIOS, computerName, &nSize))
+ {
+ free(computerName);
+ return -1;
+ }
+
+ if (nSize > MAX_COMPUTERNAME_LENGTH)
+ computerName[MAX_COMPUTERNAME_LENGTH] = '\0';
+
+ ws = computerName;
+
+ if (!ws)
+ return -1;
+ }
+
+ size_t len = 0;
+ context->Workstation.Buffer = ConvertUtf8ToWCharAlloc(ws, &len);
+
+ if (!Workstation)
+ free(ws);
+
+ if (!context->Workstation.Buffer || (len > UINT16_MAX / sizeof(WCHAR)))
+ return -1;
+
+ context->Workstation.Length = (USHORT)(len * sizeof(WCHAR));
+ return 1;
+}
+
+static int ntlm_SetContextServicePrincipalNameW(NTLM_CONTEXT* context, LPWSTR ServicePrincipalName)
+{
+ WINPR_ASSERT(context);
+
+ if (!ServicePrincipalName)
+ {
+ context->ServicePrincipalName.Buffer = NULL;
+ context->ServicePrincipalName.Length = 0;
+ return 1;
+ }
+
+ context->ServicePrincipalName.Length = (USHORT)(_wcslen(ServicePrincipalName) * 2);
+ context->ServicePrincipalName.Buffer = (PWSTR)malloc(context->ServicePrincipalName.Length + 2);
+
+ if (!context->ServicePrincipalName.Buffer)
+ return -1;
+
+ memcpy(context->ServicePrincipalName.Buffer, ServicePrincipalName,
+ context->ServicePrincipalName.Length + 2);
+ return 1;
+}
+
+static int ntlm_SetContextTargetName(NTLM_CONTEXT* context, char* TargetName)
+{
+ char* name = TargetName;
+ DWORD nSize = 0;
+ CHAR* computerName = NULL;
+
+ WINPR_ASSERT(context);
+
+ if (!name)
+ {
+ if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) ||
+ GetLastError() != ERROR_MORE_DATA)
+ return -1;
+
+ computerName = calloc(nSize, sizeof(CHAR));
+
+ if (!computerName)
+ return -1;
+
+ if (!GetComputerNameExA(ComputerNameNetBIOS, computerName, &nSize))
+ {
+ free(computerName);
+ return -1;
+ }
+
+ if (nSize > MAX_COMPUTERNAME_LENGTH)
+ computerName[MAX_COMPUTERNAME_LENGTH] = '\0';
+
+ name = computerName;
+
+ if (!name)
+ return -1;
+
+ CharUpperA(name);
+ }
+
+ size_t len = 0;
+ context->TargetName.pvBuffer = ConvertUtf8ToWCharAlloc(name, &len);
+
+ if (!context->TargetName.pvBuffer || (len > UINT16_MAX / sizeof(WCHAR)))
+ {
+ free(context->TargetName.pvBuffer);
+ context->TargetName.pvBuffer = NULL;
+
+ if (!TargetName)
+ free(name);
+
+ return -1;
+ }
+
+ context->TargetName.cbBuffer = (USHORT)(len * sizeof(WCHAR));
+
+ if (!TargetName)
+ free(name);
+
+ return 1;
+}
+
+static NTLM_CONTEXT* ntlm_ContextNew(void)
+{
+ HKEY hKey = 0;
+ LONG status = 0;
+ DWORD dwType = 0;
+ DWORD dwSize = 0;
+ DWORD dwValue = 0;
+ NTLM_CONTEXT* context = (NTLM_CONTEXT*)calloc(1, sizeof(NTLM_CONTEXT));
+
+ if (!context)
+ return NULL;
+
+ context->NTLMv2 = TRUE;
+ context->UseMIC = FALSE;
+ context->SendVersionInfo = TRUE;
+ context->SendSingleHostData = FALSE;
+ context->SendWorkstationName = TRUE;
+ context->NegotiateKeyExchange = TRUE;
+ context->UseSamFileDatabase = TRUE;
+ status = RegOpenKeyExA(HKEY_LOCAL_MACHINE, WINPR_KEY, 0, KEY_READ | KEY_WOW64_64KEY, &hKey);
+
+ if (status == ERROR_SUCCESS)
+ {
+ if (RegQueryValueEx(hKey, _T("NTLMv2"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) ==
+ ERROR_SUCCESS)
+ context->NTLMv2 = dwValue ? 1 : 0;
+
+ if (RegQueryValueEx(hKey, _T("UseMIC"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) ==
+ ERROR_SUCCESS)
+ context->UseMIC = dwValue ? 1 : 0;
+
+ if (RegQueryValueEx(hKey, _T("SendVersionInfo"), NULL, &dwType, (BYTE*)&dwValue, &dwSize) ==
+ ERROR_SUCCESS)
+ context->SendVersionInfo = dwValue ? 1 : 0;
+
+ if (RegQueryValueEx(hKey, _T("SendSingleHostData"), NULL, &dwType, (BYTE*)&dwValue,
+ &dwSize) == ERROR_SUCCESS)
+ context->SendSingleHostData = dwValue ? 1 : 0;
+
+ if (RegQueryValueEx(hKey, _T("SendWorkstationName"), NULL, &dwType, (BYTE*)&dwValue,
+ &dwSize) == ERROR_SUCCESS)
+ context->SendWorkstationName = dwValue ? 1 : 0;
+
+ if (RegQueryValueEx(hKey, _T("WorkstationName"), NULL, &dwType, NULL, &dwSize) ==
+ ERROR_SUCCESS)
+ {
+ char* workstation = (char*)malloc(dwSize + 1);
+
+ if (!workstation)
+ {
+ free(context);
+ return NULL;
+ }
+
+ status = RegQueryValueExA(hKey, "WorkstationName", NULL, &dwType, (BYTE*)workstation,
+ &dwSize);
+ if (status != ERROR_SUCCESS)
+ WLog_WARN(TAG, "Key ''WorkstationName' not found");
+ workstation[dwSize] = '\0';
+
+ if (ntlm_SetContextWorkstation(context, workstation) < 0)
+ {
+ free(workstation);
+ free(context);
+ return NULL;
+ }
+
+ free(workstation);
+ }
+
+ RegCloseKey(hKey);
+ }
+
+ /*
+ * Extended Protection is enabled by default in Windows 7,
+ * but enabling it in WinPR breaks TS Gateway at this point
+ */
+ context->SuppressExtendedProtection = FALSE;
+ status = RegOpenKeyEx(HKEY_LOCAL_MACHINE, _T("System\\CurrentControlSet\\Control\\LSA"), 0,
+ KEY_READ | KEY_WOW64_64KEY, &hKey);
+
+ if (status == ERROR_SUCCESS)
+ {
+ if (RegQueryValueEx(hKey, _T("SuppressExtendedProtection"), NULL, &dwType, (BYTE*)&dwValue,
+ &dwSize) == ERROR_SUCCESS)
+ context->SuppressExtendedProtection = dwValue ? 1 : 0;
+
+ RegCloseKey(hKey);
+ }
+
+ context->NegotiateFlags = 0;
+ context->LmCompatibilityLevel = 3;
+ ntlm_change_state(context, NTLM_STATE_INITIAL);
+ FillMemory(context->MachineID, sizeof(context->MachineID), 0xAA);
+
+ if (context->NTLMv2)
+ context->UseMIC = TRUE;
+
+ return context;
+}
+
+static void ntlm_ContextFree(NTLM_CONTEXT* context)
+{
+ if (!context)
+ return;
+
+ winpr_RC4_Free(context->SendRc4Seal);
+ winpr_RC4_Free(context->RecvRc4Seal);
+ sspi_SecBufferFree(&context->NegotiateMessage);
+ sspi_SecBufferFree(&context->ChallengeMessage);
+ sspi_SecBufferFree(&context->AuthenticateMessage);
+ sspi_SecBufferFree(&context->ChallengeTargetInfo);
+ sspi_SecBufferFree(&context->TargetName);
+ sspi_SecBufferFree(&context->NtChallengeResponse);
+ sspi_SecBufferFree(&context->LmChallengeResponse);
+ free(context->ServicePrincipalName.Buffer);
+ free(context->Workstation.Buffer);
+ free(context);
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleW(
+ SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
+ void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
+ PTimeStamp ptsExpiry)
+{
+ SEC_WINPR_NTLM_SETTINGS* settings = NULL;
+
+ if ((fCredentialUse != SECPKG_CRED_OUTBOUND) && (fCredentialUse != SECPKG_CRED_INBOUND) &&
+ (fCredentialUse != SECPKG_CRED_BOTH))
+ {
+ return SEC_E_INVALID_PARAMETER;
+ }
+
+ SSPI_CREDENTIALS* credentials = sspi_CredentialsNew();
+
+ if (!credentials)
+ return SEC_E_INTERNAL_ERROR;
+
+ credentials->fCredentialUse = fCredentialUse;
+ credentials->pGetKeyFn = pGetKeyFn;
+ credentials->pvGetKeyArgument = pvGetKeyArgument;
+
+ if (pAuthData)
+ {
+ UINT32 identityFlags = sspi_GetAuthIdentityFlags(pAuthData);
+
+ sspi_CopyAuthIdentity(&(credentials->identity),
+ (const SEC_WINNT_AUTH_IDENTITY_INFO*)pAuthData);
+
+ if (identityFlags & SEC_WINNT_AUTH_IDENTITY_EXTENDED)
+ settings = (((SEC_WINNT_AUTH_IDENTITY_WINPR*)pAuthData)->ntlmSettings);
+ }
+
+ if (settings)
+ {
+ if (settings->samFile)
+ {
+ credentials->ntlmSettings.samFile = _strdup(settings->samFile);
+ if (!credentials->ntlmSettings.samFile)
+ {
+ sspi_CredentialsFree(credentials);
+ return SEC_E_INSUFFICIENT_MEMORY;
+ }
+ }
+ credentials->ntlmSettings.hashCallback = settings->hashCallback;
+ credentials->ntlmSettings.hashCallbackArg = settings->hashCallbackArg;
+ }
+
+ sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials);
+ sspi_SecureHandleSetUpperPointer(phCredential, (void*)NTLM_PACKAGE_NAME);
+ return SEC_E_OK;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_AcquireCredentialsHandleA(
+ SEC_CHAR* pszPrincipal, SEC_CHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
+ void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
+ PTimeStamp ptsExpiry)
+{
+ SECURITY_STATUS status = SEC_E_INSUFFICIENT_MEMORY;
+ SEC_WCHAR* principal = NULL;
+ SEC_WCHAR* package = NULL;
+
+ if (pszPrincipal)
+ {
+ principal = ConvertUtf8ToWCharAlloc(pszPrincipal, NULL);
+ if (!principal)
+ goto fail;
+ }
+ if (pszPackage)
+ {
+ package = ConvertUtf8ToWCharAlloc(pszPackage, NULL);
+ if (!package)
+ goto fail;
+ }
+
+ status =
+ ntlm_AcquireCredentialsHandleW(principal, package, fCredentialUse, pvLogonID, pAuthData,
+ pGetKeyFn, pvGetKeyArgument, phCredential, ptsExpiry);
+
+fail:
+ free(principal);
+ free(package);
+
+ return status;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_FreeCredentialsHandle(PCredHandle phCredential)
+{
+ if (!phCredential)
+ return SEC_E_INVALID_HANDLE;
+
+ SSPI_CREDENTIALS* credentials =
+ (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
+
+ if (!credentials)
+ return SEC_E_INVALID_HANDLE;
+
+ sspi_CredentialsFree(credentials);
+ return SEC_E_OK;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesW(PCredHandle phCredential,
+ ULONG ulAttribute, void* pBuffer)
+{
+ if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
+ {
+ return SEC_E_OK;
+ }
+
+ WLog_ERR(TAG, "TODO: Implement");
+ return SEC_E_UNSUPPORTED_FUNCTION;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_QueryCredentialsAttributesA(PCredHandle phCredential,
+ ULONG ulAttribute, void* pBuffer)
+{
+ return ntlm_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
+}
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/windows/desktop/aa374707
+ */
+static SECURITY_STATUS SEC_ENTRY
+ntlm_AcceptSecurityContext(PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput,
+ ULONG fContextReq, ULONG TargetDataRep, PCtxtHandle phNewContext,
+ PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsTimeStamp)
+{
+ SECURITY_STATUS status = 0;
+ SSPI_CREDENTIALS* credentials = NULL;
+ PSecBuffer input_buffer = NULL;
+ PSecBuffer output_buffer = NULL;
+
+ /* behave like windows SSPIs that don't want empty context */
+ if (phContext && !phContext->dwLower && !phContext->dwUpper)
+ return SEC_E_INVALID_HANDLE;
+
+ NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
+
+ if (!context)
+ {
+ context = ntlm_ContextNew();
+
+ if (!context)
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ context->server = TRUE;
+
+ if (fContextReq & ASC_REQ_CONFIDENTIALITY)
+ context->confidentiality = TRUE;
+
+ credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
+ context->credentials = credentials;
+ context->SamFile = credentials->ntlmSettings.samFile;
+ context->HashCallback = credentials->ntlmSettings.hashCallback;
+ context->HashCallbackArg = credentials->ntlmSettings.hashCallbackArg;
+
+ ntlm_SetContextTargetName(context, NULL);
+ sspi_SecureHandleSetLowerPointer(phNewContext, context);
+ sspi_SecureHandleSetUpperPointer(phNewContext, (void*)NTLM_PACKAGE_NAME);
+ }
+
+ switch (ntlm_get_state(context))
+ {
+ case NTLM_STATE_INITIAL:
+ {
+ ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
+
+ if (!pInput)
+ return SEC_E_INVALID_TOKEN;
+
+ if (pInput->cBuffers < 1)
+ return SEC_E_INVALID_TOKEN;
+
+ input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
+
+ if (!input_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ if (input_buffer->cbBuffer < 1)
+ return SEC_E_INVALID_TOKEN;
+
+ status = ntlm_read_NegotiateMessage(context, input_buffer);
+ if (status != SEC_I_CONTINUE_NEEDED)
+ return status;
+
+ if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
+ {
+ if (!pOutput)
+ return SEC_E_INVALID_TOKEN;
+
+ if (pOutput->cBuffers < 1)
+ return SEC_E_INVALID_TOKEN;
+
+ output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
+
+ if (!output_buffer->BufferType)
+ return SEC_E_INVALID_TOKEN;
+
+ if (output_buffer->cbBuffer < 1)
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ return ntlm_write_ChallengeMessage(context, output_buffer);
+ }
+
+ return SEC_E_OUT_OF_SEQUENCE;
+ }
+
+ case NTLM_STATE_AUTHENTICATE:
+ {
+ if (!pInput)
+ return SEC_E_INVALID_TOKEN;
+
+ if (pInput->cBuffers < 1)
+ return SEC_E_INVALID_TOKEN;
+
+ input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
+
+ if (!input_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ if (input_buffer->cbBuffer < 1)
+ return SEC_E_INVALID_TOKEN;
+
+ status = ntlm_read_AuthenticateMessage(context, input_buffer);
+
+ if (pOutput)
+ {
+ for (ULONG i = 0; i < pOutput->cBuffers; i++)
+ {
+ pOutput->pBuffers[i].cbBuffer = 0;
+ pOutput->pBuffers[i].BufferType = SECBUFFER_TOKEN;
+ }
+ }
+
+ return status;
+ }
+
+ default:
+ return SEC_E_OUT_OF_SEQUENCE;
+ }
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_ImpersonateSecurityContext(PCtxtHandle phContext)
+{
+ return SEC_E_OK;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextW(
+ PCredHandle phCredential, PCtxtHandle phContext, SEC_WCHAR* pszTargetName, ULONG fContextReq,
+ ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
+ PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)
+{
+ SECURITY_STATUS status = 0;
+ SSPI_CREDENTIALS* credentials = NULL;
+ PSecBuffer input_buffer = NULL;
+ PSecBuffer output_buffer = NULL;
+ PSecBuffer channel_bindings = NULL;
+
+ /* behave like windows SSPIs that don't want empty context */
+ if (phContext && !phContext->dwLower && !phContext->dwUpper)
+ return SEC_E_INVALID_HANDLE;
+
+ NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
+
+ if (pInput)
+ {
+ input_buffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
+ channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
+ }
+
+ if (!context)
+ {
+ context = ntlm_ContextNew();
+
+ if (!context)
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ if (fContextReq & ISC_REQ_CONFIDENTIALITY)
+ context->confidentiality = TRUE;
+
+ credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
+ context->credentials = credentials;
+
+ if (context->Workstation.Length < 1)
+ {
+ if (ntlm_SetContextWorkstation(context, NULL) < 0)
+ {
+ ntlm_ContextFree(context);
+ return SEC_E_INTERNAL_ERROR;
+ }
+ }
+
+ if (ntlm_SetContextServicePrincipalNameW(context, pszTargetName) < 0)
+ {
+ ntlm_ContextFree(context);
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ sspi_SecureHandleSetLowerPointer(phNewContext, context);
+ sspi_SecureHandleSetUpperPointer(phNewContext, NTLM_SSP_NAME);
+ }
+
+ if ((!input_buffer) || (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE))
+ {
+ if (!pOutput)
+ return SEC_E_INVALID_TOKEN;
+
+ if (pOutput->cBuffers < 1)
+ return SEC_E_INVALID_TOKEN;
+
+ output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
+
+ if (!output_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ if (output_buffer->cbBuffer < 1)
+ return SEC_E_INVALID_TOKEN;
+
+ if (ntlm_get_state(context) == NTLM_STATE_INITIAL)
+ ntlm_change_state(context, NTLM_STATE_NEGOTIATE);
+
+ if (ntlm_get_state(context) == NTLM_STATE_NEGOTIATE)
+ return ntlm_write_NegotiateMessage(context, output_buffer);
+
+ return SEC_E_OUT_OF_SEQUENCE;
+ }
+ else
+ {
+ if (!input_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ if (input_buffer->cbBuffer < 1)
+ return SEC_E_INVALID_TOKEN;
+
+ channel_bindings = sspi_FindSecBuffer(pInput, SECBUFFER_CHANNEL_BINDINGS);
+
+ if (channel_bindings)
+ {
+ context->Bindings.BindingsLength = channel_bindings->cbBuffer;
+ context->Bindings.Bindings = (SEC_CHANNEL_BINDINGS*)channel_bindings->pvBuffer;
+ }
+
+ if (ntlm_get_state(context) == NTLM_STATE_CHALLENGE)
+ {
+ status = ntlm_read_ChallengeMessage(context, input_buffer);
+
+ if (status != SEC_I_CONTINUE_NEEDED)
+ return status;
+
+ if (!pOutput)
+ return SEC_E_INVALID_TOKEN;
+
+ if (pOutput->cBuffers < 1)
+ return SEC_E_INVALID_TOKEN;
+
+ output_buffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
+
+ if (!output_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ if (output_buffer->cbBuffer < 1)
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ if (ntlm_get_state(context) == NTLM_STATE_AUTHENTICATE)
+ return ntlm_write_AuthenticateMessage(context, output_buffer);
+ }
+
+ return SEC_E_OUT_OF_SEQUENCE;
+ }
+
+ return SEC_E_OUT_OF_SEQUENCE;
+}
+
+/**
+ * @see http://msdn.microsoft.com/en-us/library/windows/desktop/aa375512%28v=vs.85%29.aspx
+ */
+static SECURITY_STATUS SEC_ENTRY ntlm_InitializeSecurityContextA(
+ PCredHandle phCredential, PCtxtHandle phContext, SEC_CHAR* pszTargetName, ULONG fContextReq,
+ ULONG Reserved1, ULONG TargetDataRep, PSecBufferDesc pInput, ULONG Reserved2,
+ PCtxtHandle phNewContext, PSecBufferDesc pOutput, PULONG pfContextAttr, PTimeStamp ptsExpiry)
+{
+ SECURITY_STATUS status = 0;
+ SEC_WCHAR* pszTargetNameW = NULL;
+
+ if (pszTargetName)
+ {
+ pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, NULL);
+ if (!pszTargetNameW)
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ status = ntlm_InitializeSecurityContextW(phCredential, phContext, pszTargetNameW, fContextReq,
+ Reserved1, TargetDataRep, pInput, Reserved2,
+ phNewContext, pOutput, pfContextAttr, ptsExpiry);
+ free(pszTargetNameW);
+ return status;
+}
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa375354 */
+
+static SECURITY_STATUS SEC_ENTRY ntlm_DeleteSecurityContext(PCtxtHandle phContext)
+{
+ NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
+ ntlm_ContextFree(context);
+ return SEC_E_OK;
+}
+
+SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof)
+{
+ BYTE* blob = NULL;
+ SecBuffer* target = NULL;
+
+ WINPR_ASSERT(ntlm);
+ WINPR_ASSERT(ntproof);
+
+ target = &ntlm->ChallengeTargetInfo;
+
+ if (!sspi_SecBufferAlloc(ntproof, 36 + target->cbBuffer))
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ blob = (BYTE*)ntproof->pvBuffer;
+ CopyMemory(blob, ntlm->ServerChallenge, 8); /* Server challenge. */
+ blob[8] = 1; /* Response version. */
+ blob[9] = 1; /* Highest response version understood by the client. */
+ /* Reserved 6B. */
+ CopyMemory(&blob[16], ntlm->Timestamp, 8); /* Time. */
+ CopyMemory(&blob[24], ntlm->ClientChallenge, 8); /* Client challenge. */
+ /* Reserved 4B. */
+ /* Server name. */
+ CopyMemory(&blob[36], target->pvBuffer, target->cbBuffer);
+ return SEC_E_OK;
+}
+
+SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT* ntlm, SecBuffer* micvalue)
+{
+ BYTE* blob = NULL;
+ ULONG msgSize = 0;
+
+ WINPR_ASSERT(ntlm);
+ WINPR_ASSERT(micvalue);
+
+ msgSize = ntlm->NegotiateMessage.cbBuffer + ntlm->ChallengeMessage.cbBuffer +
+ ntlm->AuthenticateMessage.cbBuffer;
+
+ if (!sspi_SecBufferAlloc(micvalue, msgSize))
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ blob = (BYTE*)micvalue->pvBuffer;
+ CopyMemory(blob, ntlm->NegotiateMessage.pvBuffer, ntlm->NegotiateMessage.cbBuffer);
+ blob += ntlm->NegotiateMessage.cbBuffer;
+ CopyMemory(blob, ntlm->ChallengeMessage.pvBuffer, ntlm->ChallengeMessage.cbBuffer);
+ blob += ntlm->ChallengeMessage.cbBuffer;
+ CopyMemory(blob, ntlm->AuthenticateMessage.pvBuffer, ntlm->AuthenticateMessage.cbBuffer);
+ blob += ntlm->MessageIntegrityCheckOffset;
+ ZeroMemory(blob, 16);
+ return SEC_E_OK;
+}
+
+/* http://msdn.microsoft.com/en-us/library/windows/desktop/aa379337/ */
+
+static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesW(PCtxtHandle phContext,
+ ULONG ulAttribute, void* pBuffer)
+{
+ if (!phContext)
+ return SEC_E_INVALID_HANDLE;
+
+ if (!pBuffer)
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
+ if (!check_context(context))
+ return SEC_E_INVALID_HANDLE;
+
+ if (ulAttribute == SECPKG_ATTR_SIZES)
+ {
+ SecPkgContext_Sizes* ContextSizes = (SecPkgContext_Sizes*)pBuffer;
+ ContextSizes->cbMaxToken = 2010;
+ ContextSizes->cbMaxSignature = 16; /* the size of expected signature is 16 bytes */
+ ContextSizes->cbBlockSize = 0; /* no padding */
+ ContextSizes->cbSecurityTrailer = 16; /* no security trailer appended in NTLM
+ contrary to Kerberos */
+ return SEC_E_OK;
+ }
+ else if (ulAttribute == SECPKG_ATTR_AUTH_IDENTITY)
+ {
+ SSPI_CREDENTIALS* credentials = NULL;
+ const SecPkgContext_AuthIdentity empty = { 0 };
+ SecPkgContext_AuthIdentity* AuthIdentity = (SecPkgContext_AuthIdentity*)pBuffer;
+
+ WINPR_ASSERT(AuthIdentity);
+ *AuthIdentity = empty;
+
+ context->UseSamFileDatabase = FALSE;
+ credentials = context->credentials;
+
+ if (credentials->identity.UserLength > 0)
+ {
+ if (ConvertWCharNToUtf8(credentials->identity.User, credentials->identity.UserLength,
+ AuthIdentity->User, ARRAYSIZE(AuthIdentity->User)) <= 0)
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (credentials->identity.DomainLength > 0)
+ {
+ if (ConvertWCharNToUtf8(credentials->identity.Domain,
+ credentials->identity.DomainLength, AuthIdentity->Domain,
+ ARRAYSIZE(AuthIdentity->Domain)) <= 0)
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ return SEC_E_OK;
+ }
+ else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_NTPROOF_VALUE)
+ {
+ return ntlm_computeProofValue(context, (SecBuffer*)pBuffer);
+ }
+ else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_RANDKEY)
+ {
+ SecBuffer* randkey = NULL;
+ randkey = (SecBuffer*)pBuffer;
+
+ if (!sspi_SecBufferAlloc(randkey, 16))
+ return (SEC_E_INSUFFICIENT_MEMORY);
+
+ CopyMemory(randkey->pvBuffer, context->EncryptedRandomSessionKey, 16);
+ return (SEC_E_OK);
+ }
+ else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC)
+ {
+ SecBuffer* mic = (SecBuffer*)pBuffer;
+ NTLM_AUTHENTICATE_MESSAGE* message = &context->AUTHENTICATE_MESSAGE;
+
+ if (!sspi_SecBufferAlloc(mic, 16))
+ return (SEC_E_INSUFFICIENT_MEMORY);
+
+ CopyMemory(mic->pvBuffer, message->MessageIntegrityCheck, 16);
+ return (SEC_E_OK);
+ }
+ else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MIC_VALUE)
+ {
+ return ntlm_computeMicValue(context, (SecBuffer*)pBuffer);
+ }
+
+ WLog_ERR(TAG, "TODO: Implement ulAttribute=0x%08" PRIx32, ulAttribute);
+ return SEC_E_UNSUPPORTED_FUNCTION;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_QueryContextAttributesA(PCtxtHandle phContext,
+ ULONG ulAttribute, void* pBuffer)
+{
+ return ntlm_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesW(PCtxtHandle phContext,
+ ULONG ulAttribute, void* pBuffer,
+ ULONG cbBuffer)
+{
+ if (!phContext)
+ return SEC_E_INVALID_HANDLE;
+
+ if (!pBuffer)
+ return SEC_E_INVALID_PARAMETER;
+
+ NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
+ if (!context)
+ return SEC_E_INVALID_HANDLE;
+
+ if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_HASH)
+ {
+ SecPkgContext_AuthNtlmHash* AuthNtlmHash = (SecPkgContext_AuthNtlmHash*)pBuffer;
+
+ if (cbBuffer < sizeof(SecPkgContext_AuthNtlmHash))
+ return SEC_E_INVALID_PARAMETER;
+
+ if (AuthNtlmHash->Version == 1)
+ CopyMemory(context->NtlmHash, AuthNtlmHash->NtlmHash, 16);
+ else if (AuthNtlmHash->Version == 2)
+ CopyMemory(context->NtlmV2Hash, AuthNtlmHash->NtlmHash, 16);
+
+ return SEC_E_OK;
+ }
+ else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_MESSAGE)
+ {
+ SecPkgContext_AuthNtlmMessage* AuthNtlmMessage = (SecPkgContext_AuthNtlmMessage*)pBuffer;
+
+ if (cbBuffer < sizeof(SecPkgContext_AuthNtlmMessage))
+ return SEC_E_INVALID_PARAMETER;
+
+ if (AuthNtlmMessage->type == 1)
+ {
+ sspi_SecBufferFree(&context->NegotiateMessage);
+
+ if (!sspi_SecBufferAlloc(&context->NegotiateMessage, AuthNtlmMessage->length))
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ CopyMemory(context->NegotiateMessage.pvBuffer, AuthNtlmMessage->buffer,
+ AuthNtlmMessage->length);
+ }
+ else if (AuthNtlmMessage->type == 2)
+ {
+ sspi_SecBufferFree(&context->ChallengeMessage);
+
+ if (!sspi_SecBufferAlloc(&context->ChallengeMessage, AuthNtlmMessage->length))
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ CopyMemory(context->ChallengeMessage.pvBuffer, AuthNtlmMessage->buffer,
+ AuthNtlmMessage->length);
+ }
+ else if (AuthNtlmMessage->type == 3)
+ {
+ sspi_SecBufferFree(&context->AuthenticateMessage);
+
+ if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, AuthNtlmMessage->length))
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ CopyMemory(context->AuthenticateMessage.pvBuffer, AuthNtlmMessage->buffer,
+ AuthNtlmMessage->length);
+ }
+
+ return SEC_E_OK;
+ }
+ else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_TIMESTAMP)
+ {
+ SecPkgContext_AuthNtlmTimestamp* AuthNtlmTimestamp =
+ (SecPkgContext_AuthNtlmTimestamp*)pBuffer;
+
+ if (cbBuffer < sizeof(SecPkgContext_AuthNtlmTimestamp))
+ return SEC_E_INVALID_PARAMETER;
+
+ if (AuthNtlmTimestamp->ChallengeOrResponse)
+ CopyMemory(context->ChallengeTimestamp, AuthNtlmTimestamp->Timestamp, 8);
+ else
+ CopyMemory(context->Timestamp, AuthNtlmTimestamp->Timestamp, 8);
+
+ return SEC_E_OK;
+ }
+ else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE)
+ {
+ SecPkgContext_AuthNtlmClientChallenge* AuthNtlmClientChallenge =
+ (SecPkgContext_AuthNtlmClientChallenge*)pBuffer;
+
+ if (cbBuffer < sizeof(SecPkgContext_AuthNtlmClientChallenge))
+ return SEC_E_INVALID_PARAMETER;
+
+ CopyMemory(context->ClientChallenge, AuthNtlmClientChallenge->ClientChallenge, 8);
+ return SEC_E_OK;
+ }
+ else if (ulAttribute == SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE)
+ {
+ SecPkgContext_AuthNtlmServerChallenge* AuthNtlmServerChallenge =
+ (SecPkgContext_AuthNtlmServerChallenge*)pBuffer;
+
+ if (cbBuffer < sizeof(SecPkgContext_AuthNtlmServerChallenge))
+ return SEC_E_INVALID_PARAMETER;
+
+ CopyMemory(context->ServerChallenge, AuthNtlmServerChallenge->ServerChallenge, 8);
+ return SEC_E_OK;
+ }
+
+ WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
+ return SEC_E_UNSUPPORTED_FUNCTION;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_SetContextAttributesA(PCtxtHandle phContext,
+ ULONG ulAttribute, void* pBuffer,
+ ULONG cbBuffer)
+{
+ return ntlm_SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer);
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesW(PCredHandle phCredential,
+ ULONG ulAttribute, void* pBuffer,
+ ULONG cbBuffer)
+{
+ return SEC_E_UNSUPPORTED_FUNCTION;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_SetCredentialsAttributesA(PCredHandle phCredential,
+ ULONG ulAttribute, void* pBuffer,
+ ULONG cbBuffer)
+{
+ return SEC_E_UNSUPPORTED_FUNCTION;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_RevertSecurityContext(PCtxtHandle phContext)
+{
+ return SEC_E_OK;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
+ PSecBufferDesc pMessage, ULONG MessageSeqNo)
+{
+ const UINT32 SeqNo = MessageSeqNo;
+ UINT32 value = 0;
+ BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
+ BYTE checksum[8] = { 0 };
+ ULONG version = 1;
+ PSecBuffer data_buffer = NULL;
+ PSecBuffer signature_buffer = NULL;
+ NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
+ if (!check_context(context))
+ return SEC_E_INVALID_HANDLE;
+
+ for (ULONG index = 0; index < pMessage->cBuffers; index++)
+ {
+ SecBuffer* cur = &pMessage->pBuffers[index];
+
+ if (cur->BufferType & SECBUFFER_DATA)
+ data_buffer = cur;
+ else if (cur->BufferType & SECBUFFER_TOKEN)
+ signature_buffer = cur;
+ }
+
+ if (!data_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ if (!signature_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ /* Copy original data buffer */
+ ULONG length = data_buffer->cbBuffer;
+ void* data = malloc(length);
+
+ if (!data)
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ CopyMemory(data, data_buffer->pvBuffer, length);
+ /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
+ WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
+
+ if (hmac &&
+ winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
+ {
+ Data_Write_UINT32(&value, SeqNo);
+ winpr_HMAC_Update(hmac, (void*)&value, 4);
+ winpr_HMAC_Update(hmac, (void*)data, length);
+ winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
+ winpr_HMAC_Free(hmac);
+ }
+ else
+ {
+ winpr_HMAC_Free(hmac);
+ free(data);
+ return SEC_E_INSUFFICIENT_MEMORY;
+ }
+
+ /* Encrypt message using with RC4, result overwrites original buffer */
+ if ((data_buffer->BufferType & SECBUFFER_READONLY) == 0)
+ {
+ if (context->confidentiality)
+ winpr_RC4_Update(context->SendRc4Seal, length, (BYTE*)data,
+ (BYTE*)data_buffer->pvBuffer);
+ else
+ CopyMemory(data_buffer->pvBuffer, data, length);
+ }
+
+#ifdef WITH_DEBUG_NTLM
+ WLog_DBG(TAG, "Data Buffer (length = %" PRIuz ")", length);
+ winpr_HexDump(TAG, WLOG_DEBUG, data, length);
+ WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer);
+ winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
+#endif
+ free(data);
+ /* RC4-encrypt first 8 bytes of digest */
+ winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum);
+ if ((signature_buffer->BufferType & SECBUFFER_READONLY) == 0)
+ {
+ BYTE* signature = signature_buffer->pvBuffer;
+ /* Concatenate version, ciphertext and sequence number to build signature */
+ Data_Write_UINT32(signature, version);
+ CopyMemory(&signature[4], (void*)checksum, 8);
+ Data_Write_UINT32(&signature[12], SeqNo);
+ }
+ context->SendSeqNum++;
+#ifdef WITH_DEBUG_NTLM
+ WLog_DBG(TAG, "Signature (length = %" PRIu32 ")", signature_buffer->cbBuffer);
+ winpr_HexDump(TAG, WLOG_DEBUG, signature_buffer->pvBuffer, signature_buffer->cbBuffer);
+#endif
+ return SEC_E_OK;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_DecryptMessage(PCtxtHandle phContext, PSecBufferDesc pMessage,
+ ULONG MessageSeqNo, PULONG pfQOP)
+{
+ const UINT32 SeqNo = (UINT32)MessageSeqNo;
+ UINT32 value = 0;
+ BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
+ BYTE checksum[8] = { 0 };
+ UINT32 version = 1;
+ BYTE expected_signature[WINPR_MD5_DIGEST_LENGTH] = { 0 };
+ PSecBuffer data_buffer = NULL;
+ PSecBuffer signature_buffer = NULL;
+ NTLM_CONTEXT* context = (NTLM_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
+ if (!check_context(context))
+ return SEC_E_INVALID_HANDLE;
+
+ for (ULONG index = 0; index < pMessage->cBuffers; index++)
+ {
+ if (pMessage->pBuffers[index].BufferType == SECBUFFER_DATA)
+ data_buffer = &pMessage->pBuffers[index];
+ else if (pMessage->pBuffers[index].BufferType == SECBUFFER_TOKEN)
+ signature_buffer = &pMessage->pBuffers[index];
+ }
+
+ if (!data_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ if (!signature_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ /* Copy original data buffer */
+ const ULONG length = data_buffer->cbBuffer;
+ void* data = malloc(length);
+
+ if (!data)
+ return SEC_E_INSUFFICIENT_MEMORY;
+
+ CopyMemory(data, data_buffer->pvBuffer, length);
+
+ /* Decrypt message using with RC4, result overwrites original buffer */
+
+ if (context->confidentiality)
+ winpr_RC4_Update(context->RecvRc4Seal, length, (BYTE*)data, (BYTE*)data_buffer->pvBuffer);
+ else
+ CopyMemory(data_buffer->pvBuffer, data, length);
+
+ /* Compute the HMAC-MD5 hash of ConcatenationOf(seq_num,data) using the client signing key */
+ WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
+
+ if (hmac &&
+ winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
+ {
+ Data_Write_UINT32(&value, SeqNo);
+ winpr_HMAC_Update(hmac, (void*)&value, 4);
+ winpr_HMAC_Update(hmac, (void*)data_buffer->pvBuffer, data_buffer->cbBuffer);
+ winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
+ winpr_HMAC_Free(hmac);
+ }
+ else
+ {
+ winpr_HMAC_Free(hmac);
+ free(data);
+ return SEC_E_INSUFFICIENT_MEMORY;
+ }
+
+#ifdef WITH_DEBUG_NTLM
+ WLog_DBG(TAG, "Encrypted Data Buffer (length = %" PRIuz ")", length);
+ winpr_HexDump(TAG, WLOG_DEBUG, data, length);
+ WLog_DBG(TAG, "Data Buffer (length = %" PRIu32 ")", data_buffer->cbBuffer);
+ winpr_HexDump(TAG, WLOG_DEBUG, data_buffer->pvBuffer, data_buffer->cbBuffer);
+#endif
+ free(data);
+ /* RC4-encrypt first 8 bytes of digest */
+ winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum);
+ /* Concatenate version, ciphertext and sequence number to build signature */
+ Data_Write_UINT32(expected_signature, version);
+ CopyMemory(&expected_signature[4], (void*)checksum, 8);
+ Data_Write_UINT32(&expected_signature[12], SeqNo);
+ context->RecvSeqNum++;
+
+ if (memcmp(signature_buffer->pvBuffer, expected_signature, 16) != 0)
+ {
+ /* signature verification failed! */
+ WLog_ERR(TAG, "signature verification failed, something nasty is going on!");
+#ifdef WITH_DEBUG_NTLM
+ WLog_ERR(TAG, "Expected Signature:");
+ winpr_HexDump(TAG, WLOG_ERROR, expected_signature, 16);
+ WLog_ERR(TAG, "Actual Signature:");
+ winpr_HexDump(TAG, WLOG_ERROR, (BYTE*)signature_buffer->pvBuffer, 16);
+#endif
+ return SEC_E_MESSAGE_ALTERED;
+ }
+
+ return SEC_E_OK;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
+ PSecBufferDesc pMessage, ULONG MessageSeqNo)
+{
+ PSecBuffer data_buffer = NULL;
+ PSecBuffer sig_buffer = NULL;
+ UINT32 seq_no = 0;
+ BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
+ BYTE checksum[8] = { 0 };
+
+ NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
+ if (!check_context(context))
+ return SEC_E_INVALID_HANDLE;
+
+ for (ULONG i = 0; i < pMessage->cBuffers; i++)
+ {
+ if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
+ data_buffer = &pMessage->pBuffers[i];
+ else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
+ sig_buffer = &pMessage->pBuffers[i];
+ }
+
+ if (!data_buffer || !sig_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
+
+ if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->SendSigningKey, WINPR_MD5_DIGEST_LENGTH))
+ return SEC_E_INTERNAL_ERROR;
+
+ Data_Write_UINT32(&seq_no, MessageSeqNo);
+ winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4);
+ winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
+ winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
+ winpr_HMAC_Free(hmac);
+
+ winpr_RC4_Update(context->SendRc4Seal, 8, digest, checksum);
+
+ BYTE* signature = sig_buffer->pvBuffer;
+ Data_Write_UINT32(signature, 1L);
+ CopyMemory(&signature[4], checksum, 8);
+ Data_Write_UINT32(&signature[12], seq_no);
+ sig_buffer->cbBuffer = 16;
+
+ return SEC_E_OK;
+}
+
+static SECURITY_STATUS SEC_ENTRY ntlm_VerifySignature(PCtxtHandle phContext,
+ PSecBufferDesc pMessage, ULONG MessageSeqNo,
+ PULONG pfQOP)
+{
+ PSecBuffer data_buffer = NULL;
+ PSecBuffer sig_buffer = NULL;
+ UINT32 seq_no = 0;
+ BYTE digest[WINPR_MD5_DIGEST_LENGTH] = { 0 };
+ BYTE checksum[8] = { 0 };
+ BYTE signature[16] = { 0 };
+
+ NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
+ if (!check_context(context))
+ return SEC_E_INVALID_HANDLE;
+
+ for (ULONG i = 0; i < pMessage->cBuffers; i++)
+ {
+ if (pMessage->pBuffers[i].BufferType == SECBUFFER_DATA)
+ data_buffer = &pMessage->pBuffers[i];
+ else if (pMessage->pBuffers[i].BufferType == SECBUFFER_TOKEN)
+ sig_buffer = &pMessage->pBuffers[i];
+ }
+
+ if (!data_buffer || !sig_buffer)
+ return SEC_E_INVALID_TOKEN;
+
+ WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
+
+ if (!winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->RecvSigningKey, WINPR_MD5_DIGEST_LENGTH))
+ return SEC_E_INTERNAL_ERROR;
+
+ Data_Write_UINT32(&seq_no, MessageSeqNo);
+ winpr_HMAC_Update(hmac, (BYTE*)&seq_no, 4);
+ winpr_HMAC_Update(hmac, data_buffer->pvBuffer, data_buffer->cbBuffer);
+ winpr_HMAC_Final(hmac, digest, WINPR_MD5_DIGEST_LENGTH);
+ winpr_HMAC_Free(hmac);
+
+ winpr_RC4_Update(context->RecvRc4Seal, 8, digest, checksum);
+
+ Data_Write_UINT32(signature, 1L);
+ CopyMemory(&signature[4], checksum, 8);
+ Data_Write_UINT32(&signature[12], seq_no);
+
+ if (memcmp(sig_buffer->pvBuffer, signature, 16) != 0)
+ return SEC_E_MESSAGE_ALTERED;
+
+ return SEC_E_OK;
+}
+
+const SecurityFunctionTableA NTLM_SecurityFunctionTableA = {
+ 3, /* dwVersion */
+ NULL, /* EnumerateSecurityPackages */
+ ntlm_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
+ ntlm_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
+ ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */
+ NULL, /* Reserved2 */
+ ntlm_InitializeSecurityContextA, /* InitializeSecurityContext */
+ ntlm_AcceptSecurityContext, /* AcceptSecurityContext */
+ NULL, /* CompleteAuthToken */
+ ntlm_DeleteSecurityContext, /* DeleteSecurityContext */
+ NULL, /* ApplyControlToken */
+ ntlm_QueryContextAttributesA, /* QueryContextAttributes */
+ ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
+ ntlm_RevertSecurityContext, /* RevertSecurityContext */
+ ntlm_MakeSignature, /* MakeSignature */
+ ntlm_VerifySignature, /* VerifySignature */
+ NULL, /* FreeContextBuffer */
+ NULL, /* QuerySecurityPackageInfo */
+ NULL, /* Reserved3 */
+ NULL, /* Reserved4 */
+ NULL, /* ExportSecurityContext */
+ NULL, /* ImportSecurityContext */
+ NULL, /* AddCredentials */
+ NULL, /* Reserved8 */
+ NULL, /* QuerySecurityContextToken */
+ ntlm_EncryptMessage, /* EncryptMessage */
+ ntlm_DecryptMessage, /* DecryptMessage */
+ ntlm_SetContextAttributesA, /* SetContextAttributes */
+ ntlm_SetCredentialsAttributesA, /* SetCredentialsAttributes */
+};
+
+const SecurityFunctionTableW NTLM_SecurityFunctionTableW = {
+ 3, /* dwVersion */
+ NULL, /* EnumerateSecurityPackages */
+ ntlm_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
+ ntlm_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
+ ntlm_FreeCredentialsHandle, /* FreeCredentialsHandle */
+ NULL, /* Reserved2 */
+ ntlm_InitializeSecurityContextW, /* InitializeSecurityContext */
+ ntlm_AcceptSecurityContext, /* AcceptSecurityContext */
+ NULL, /* CompleteAuthToken */
+ ntlm_DeleteSecurityContext, /* DeleteSecurityContext */
+ NULL, /* ApplyControlToken */
+ ntlm_QueryContextAttributesW, /* QueryContextAttributes */
+ ntlm_ImpersonateSecurityContext, /* ImpersonateSecurityContext */
+ ntlm_RevertSecurityContext, /* RevertSecurityContext */
+ ntlm_MakeSignature, /* MakeSignature */
+ ntlm_VerifySignature, /* VerifySignature */
+ NULL, /* FreeContextBuffer */
+ NULL, /* QuerySecurityPackageInfo */
+ NULL, /* Reserved3 */
+ NULL, /* Reserved4 */
+ NULL, /* ExportSecurityContext */
+ NULL, /* ImportSecurityContext */
+ NULL, /* AddCredentials */
+ NULL, /* Reserved8 */
+ NULL, /* QuerySecurityContextToken */
+ ntlm_EncryptMessage, /* EncryptMessage */
+ ntlm_DecryptMessage, /* DecryptMessage */
+ ntlm_SetContextAttributesW, /* SetContextAttributes */
+ ntlm_SetCredentialsAttributesW, /* SetCredentialsAttributes */
+};
+
+const SecPkgInfoA NTLM_SecPkgInfoA = {
+ 0x00082B37, /* fCapabilities */
+ 1, /* wVersion */
+ 0x000A, /* wRPCID */
+ 0x00000B48, /* cbMaxToken */
+ "NTLM", /* Name */
+ "NTLM Security Package" /* Comment */
+};
+
+static WCHAR NTLM_SecPkgInfoW_NameBuffer[32] = { 0 };
+static WCHAR NTLM_SecPkgInfoW_CommentBuffer[32] = { 0 };
+
+const SecPkgInfoW NTLM_SecPkgInfoW = {
+ 0x00082B37, /* fCapabilities */
+ 1, /* wVersion */
+ 0x000A, /* wRPCID */
+ 0x00000B48, /* cbMaxToken */
+ NTLM_SecPkgInfoW_NameBuffer, /* Name */
+ NTLM_SecPkgInfoW_CommentBuffer /* Comment */
+};
+
+char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags)
+{
+ if (!buffer || (size == 0))
+ return buffer;
+
+ _snprintf(buffer, size, "[0x%08" PRIx32 "] ", flags);
+
+ for (int x = 0; x < 31; x++)
+ {
+ const UINT32 mask = 1 << x;
+ size_t len = strnlen(buffer, size);
+ if (flags & mask)
+ {
+ const char* str = ntlm_get_negotiate_string(mask);
+ const size_t flen = strlen(str);
+
+ if ((len > 0) && (buffer[len - 1] != ' '))
+ {
+ if (size - len < 1)
+ break;
+ winpr_str_append("|", buffer, size, NULL);
+ len++;
+ }
+
+ if (size - len < flen)
+ break;
+ winpr_str_append(str, buffer, size, NULL);
+ }
+ }
+
+ return buffer;
+}
+
+const char* ntlm_message_type_string(UINT32 messageType)
+{
+ switch (messageType)
+ {
+ case MESSAGE_TYPE_NEGOTIATE:
+ return "MESSAGE_TYPE_NEGOTIATE";
+ case MESSAGE_TYPE_CHALLENGE:
+ return "MESSAGE_TYPE_CHALLENGE";
+ case MESSAGE_TYPE_AUTHENTICATE:
+ return "MESSAGE_TYPE_AUTHENTICATE";
+ default:
+ return "MESSAGE_TYPE_UNKNOWN";
+ }
+}
+
+const char* ntlm_state_string(NTLM_STATE state)
+{
+ switch (state)
+ {
+ case NTLM_STATE_INITIAL:
+ return "NTLM_STATE_INITIAL";
+ case NTLM_STATE_NEGOTIATE:
+ return "NTLM_STATE_NEGOTIATE";
+ case NTLM_STATE_CHALLENGE:
+ return "NTLM_STATE_CHALLENGE";
+ case NTLM_STATE_AUTHENTICATE:
+ return "NTLM_STATE_AUTHENTICATE";
+ case NTLM_STATE_FINAL:
+ return "NTLM_STATE_FINAL";
+ default:
+ return "NTLM_STATE_UNKNOWN";
+ }
+}
+void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state)
+{
+ WINPR_ASSERT(ntlm);
+ WLog_DBG(TAG, "change state from %s to %s", ntlm_state_string(ntlm->state),
+ ntlm_state_string(state));
+ ntlm->state = state;
+}
+
+NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm)
+{
+ WINPR_ASSERT(ntlm);
+ return ntlm->state;
+}
+
+BOOL ntlm_reset_cipher_state(PSecHandle phContext)
+{
+ NTLM_CONTEXT* context = sspi_SecureHandleGetLowerPointer(phContext);
+
+ if (context)
+ {
+ check_context(context);
+ winpr_RC4_Free(context->SendRc4Seal);
+ winpr_RC4_Free(context->RecvRc4Seal);
+ context->SendRc4Seal = winpr_RC4_New(context->RecvSealingKey, 16);
+ context->RecvRc4Seal = winpr_RC4_New(context->SendSealingKey, 16);
+
+ if (!context->SendRc4Seal)
+ {
+ WLog_ERR(TAG, "Failed to allocate context->SendRc4Seal");
+ return FALSE;
+ }
+ if (!context->RecvRc4Seal)
+ {
+ WLog_ERR(TAG, "Failed to allocate context->RecvRc4Seal");
+ return FALSE;
+ }
+ }
+
+ return TRUE;
+}
+
+BOOL NTLM_init(void)
+{
+ InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Name, NTLM_SecPkgInfoW_NameBuffer,
+ ARRAYSIZE(NTLM_SecPkgInfoW_NameBuffer));
+ InitializeConstWCharFromUtf8(NTLM_SecPkgInfoA.Comment, NTLM_SecPkgInfoW_CommentBuffer,
+ ARRAYSIZE(NTLM_SecPkgInfoW_CommentBuffer));
+
+ return TRUE;
+}
diff --git a/winpr/libwinpr/sspi/NTLM/ntlm.h b/winpr/libwinpr/sspi/NTLM/ntlm.h
new file mode 100644
index 0000000..4eac436
--- /dev/null
+++ b/winpr/libwinpr/sspi/NTLM/ntlm.h
@@ -0,0 +1,301 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * NTLM Security Package
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WINPR_SSPI_NTLM_PRIVATE_H
+#define WINPR_SSPI_NTLM_PRIVATE_H
+
+#include <winpr/sspi.h>
+#include <winpr/windows.h>
+
+#include <winpr/nt.h>
+#include <winpr/crypto.h>
+
+#include "../sspi.h"
+
+#define MESSAGE_TYPE_NEGOTIATE 1
+#define MESSAGE_TYPE_CHALLENGE 2
+#define MESSAGE_TYPE_AUTHENTICATE 3
+
+#define NTLMSSP_NEGOTIATE_56 0x80000000 /* W (0) */
+#define NTLMSSP_NEGOTIATE_KEY_EXCH 0x40000000 /* V (1) */
+#define NTLMSSP_NEGOTIATE_128 0x20000000 /* U (2) */
+#define NTLMSSP_RESERVED1 0x10000000 /* r1 (3) */
+#define NTLMSSP_RESERVED2 0x08000000 /* r2 (4) */
+#define NTLMSSP_RESERVED3 0x04000000 /* r3 (5) */
+#define NTLMSSP_NEGOTIATE_VERSION 0x02000000 /* T (6) */
+#define NTLMSSP_RESERVED4 0x01000000 /* r4 (7) */
+#define NTLMSSP_NEGOTIATE_TARGET_INFO 0x00800000 /* S (8) */
+#define NTLMSSP_REQUEST_NON_NT_SESSION_KEY 0x00400000 /* R (9) */
+#define NTLMSSP_RESERVED5 0x00200000 /* r5 (10) */
+#define NTLMSSP_NEGOTIATE_IDENTIFY 0x00100000 /* Q (11) */
+#define NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY 0x00080000 /* P (12) */
+#define NTLMSSP_RESERVED6 0x00040000 /* r6 (13) */
+#define NTLMSSP_TARGET_TYPE_SERVER 0x00020000 /* O (14) */
+#define NTLMSSP_TARGET_TYPE_DOMAIN 0x00010000 /* N (15) */
+#define NTLMSSP_NEGOTIATE_ALWAYS_SIGN 0x00008000 /* M (16) */
+#define NTLMSSP_RESERVED7 0x00004000 /* r7 (17) */
+#define NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED 0x00002000 /* L (18) */
+#define NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED 0x00001000 /* K (19) */
+#define NTLMSSP_NEGOTIATE_ANONYMOUS 0x00000800 /* J (20) */
+#define NTLMSSP_RESERVED8 0x00000400 /* r8 (21) */
+#define NTLMSSP_NEGOTIATE_NTLM 0x00000200 /* H (22) */
+#define NTLMSSP_RESERVED9 0x00000100 /* r9 (23) */
+#define NTLMSSP_NEGOTIATE_LM_KEY 0x00000080 /* G (24) */
+#define NTLMSSP_NEGOTIATE_DATAGRAM 0x00000040 /* F (25) */
+#define NTLMSSP_NEGOTIATE_SEAL 0x00000020 /* E (26) */
+#define NTLMSSP_NEGOTIATE_SIGN 0x00000010 /* D (27) */
+#define NTLMSSP_RESERVED10 0x00000008 /* r10 (28) */
+#define NTLMSSP_REQUEST_TARGET 0x00000004 /* C (29) */
+#define NTLMSSP_NEGOTIATE_OEM 0x00000002 /* B (30) */
+#define NTLMSSP_NEGOTIATE_UNICODE 0x00000001 /* A (31) */
+
+typedef enum
+{
+ NTLM_STATE_INITIAL,
+ NTLM_STATE_NEGOTIATE,
+ NTLM_STATE_CHALLENGE,
+ NTLM_STATE_AUTHENTICATE,
+ NTLM_STATE_FINAL
+} NTLM_STATE;
+
+#ifdef __MINGW32__
+typedef MSV1_0_AVID NTLM_AV_ID;
+
+#if __MINGW64_VERSION_MAJOR < 9
+enum
+{
+ MsvAvTimestamp = MsvAvFlags + 1,
+ MsvAvRestrictions,
+ MsvAvTargetName,
+ MsvAvChannelBindings,
+ MsvAvSingleHost = MsvAvRestrictions
+};
+
+#else
+#ifndef MsvAvSingleHost
+#define MsvAvSingleHost MsvAvRestrictions
+#endif
+#endif
+#else
+typedef enum
+{
+ MsvAvEOL,
+ MsvAvNbComputerName,
+ MsvAvNbDomainName,
+ MsvAvDnsComputerName,
+ MsvAvDnsDomainName,
+ MsvAvDnsTreeName,
+ MsvAvFlags,
+ MsvAvTimestamp,
+ MsvAvSingleHost,
+ MsvAvTargetName,
+ MsvAvChannelBindings
+} NTLM_AV_ID;
+#endif /* __MINGW32__ */
+
+typedef struct
+{
+ UINT16 AvId;
+ UINT16 AvLen;
+} NTLM_AV_PAIR;
+
+#define MSV_AV_FLAGS_AUTHENTICATION_CONSTRAINED 0x00000001
+#define MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK 0x00000002
+#define MSV_AV_FLAGS_TARGET_SPN_UNTRUSTED_SOURCE 0x00000004
+
+#define WINDOWS_MAJOR_VERSION_5 0x05
+#define WINDOWS_MAJOR_VERSION_6 0x06
+#define WINDOWS_MINOR_VERSION_0 0x00
+#define WINDOWS_MINOR_VERSION_1 0x01
+#define WINDOWS_MINOR_VERSION_2 0x02
+#define NTLMSSP_REVISION_W2K3 0x0F
+
+typedef struct
+{
+ UINT8 ProductMajorVersion;
+ UINT8 ProductMinorVersion;
+ UINT16 ProductBuild;
+ BYTE Reserved[3];
+ UINT8 NTLMRevisionCurrent;
+} NTLM_VERSION_INFO;
+
+typedef struct
+{
+ UINT32 Size;
+ UINT32 Z4;
+ UINT32 DataPresent;
+ UINT32 CustomData;
+ BYTE MachineID[32];
+} NTLM_SINGLE_HOST_DATA;
+
+typedef struct
+{
+ BYTE Response[24];
+} NTLM_RESPONSE;
+
+typedef struct
+{
+ UINT8 RespType;
+ UINT8 HiRespType;
+ UINT16 Reserved1;
+ UINT32 Reserved2;
+ BYTE Timestamp[8];
+ BYTE ClientChallenge[8];
+ UINT32 Reserved3;
+ NTLM_AV_PAIR* AvPairs;
+ UINT32 cbAvPairs;
+} NTLMv2_CLIENT_CHALLENGE;
+
+typedef struct
+{
+ BYTE Response[16];
+ NTLMv2_CLIENT_CHALLENGE Challenge;
+} NTLMv2_RESPONSE;
+
+typedef struct
+{
+ UINT16 Len;
+ UINT16 MaxLen;
+ PBYTE Buffer;
+ UINT32 BufferOffset;
+} NTLM_MESSAGE_FIELDS;
+
+typedef struct
+{
+ BYTE Signature[8];
+ UINT32 MessageType;
+} NTLM_MESSAGE_HEADER;
+
+typedef struct
+{
+ NTLM_MESSAGE_HEADER header;
+ UINT32 NegotiateFlags;
+ NTLM_VERSION_INFO Version;
+ NTLM_MESSAGE_FIELDS DomainName;
+ NTLM_MESSAGE_FIELDS Workstation;
+} NTLM_NEGOTIATE_MESSAGE;
+
+typedef struct
+{
+ NTLM_MESSAGE_HEADER header;
+ UINT32 NegotiateFlags;
+ BYTE ServerChallenge[8];
+ BYTE Reserved[8];
+ NTLM_VERSION_INFO Version;
+ NTLM_MESSAGE_FIELDS TargetName;
+ NTLM_MESSAGE_FIELDS TargetInfo;
+} NTLM_CHALLENGE_MESSAGE;
+
+typedef struct
+{
+ NTLM_MESSAGE_HEADER header;
+ UINT32 NegotiateFlags;
+ NTLM_VERSION_INFO Version;
+ NTLM_MESSAGE_FIELDS DomainName;
+ NTLM_MESSAGE_FIELDS UserName;
+ NTLM_MESSAGE_FIELDS Workstation;
+ NTLM_MESSAGE_FIELDS LmChallengeResponse;
+ NTLM_MESSAGE_FIELDS NtChallengeResponse;
+ NTLM_MESSAGE_FIELDS EncryptedRandomSessionKey;
+ BYTE MessageIntegrityCheck[16];
+} NTLM_AUTHENTICATE_MESSAGE;
+
+typedef struct
+{
+ BOOL server;
+ BOOL NTLMv2;
+ BOOL UseMIC;
+ NTLM_STATE state;
+ int SendSeqNum;
+ int RecvSeqNum;
+ char* SamFile;
+ BYTE NtlmHash[16];
+ BYTE NtlmV2Hash[16];
+ BYTE MachineID[32];
+ BOOL SendVersionInfo;
+ BOOL confidentiality;
+ WINPR_RC4_CTX* SendRc4Seal;
+ WINPR_RC4_CTX* RecvRc4Seal;
+ BYTE* SendSigningKey;
+ BYTE* RecvSigningKey;
+ BYTE* SendSealingKey;
+ BYTE* RecvSealingKey;
+ UINT32 NegotiateFlags;
+ BOOL UseSamFileDatabase;
+ int LmCompatibilityLevel;
+ int SuppressExtendedProtection;
+ BOOL SendWorkstationName;
+ UNICODE_STRING Workstation;
+ UNICODE_STRING ServicePrincipalName;
+ SSPI_CREDENTIALS* credentials;
+ BYTE* ChannelBindingToken;
+ BYTE ChannelBindingsHash[16];
+ SecPkgContext_Bindings Bindings;
+ BOOL SendSingleHostData;
+ BOOL NegotiateKeyExchange;
+ NTLM_SINGLE_HOST_DATA SingleHostData;
+ NTLM_NEGOTIATE_MESSAGE NEGOTIATE_MESSAGE;
+ NTLM_CHALLENGE_MESSAGE CHALLENGE_MESSAGE;
+ NTLM_AUTHENTICATE_MESSAGE AUTHENTICATE_MESSAGE;
+ size_t MessageIntegrityCheckOffset;
+ SecBuffer NegotiateMessage;
+ SecBuffer ChallengeMessage;
+ SecBuffer AuthenticateMessage;
+ SecBuffer ChallengeTargetInfo;
+ SecBuffer AuthenticateTargetInfo;
+ SecBuffer TargetName;
+ SecBuffer NtChallengeResponse;
+ SecBuffer LmChallengeResponse;
+ NTLMv2_RESPONSE NTLMv2Response;
+ BYTE NtProofString[16];
+ BYTE Timestamp[8];
+ BYTE ChallengeTimestamp[8];
+ BYTE ServerChallenge[8];
+ BYTE ClientChallenge[8];
+ BYTE SessionBaseKey[16];
+ BYTE KeyExchangeKey[16];
+ BYTE RandomSessionKey[16];
+ BYTE ExportedSessionKey[16];
+ BYTE EncryptedRandomSessionKey[16];
+ BYTE ClientSigningKey[16];
+ BYTE ClientSealingKey[16];
+ BYTE ServerSigningKey[16];
+ BYTE ServerSealingKey[16];
+ psSspiNtlmHashCallback HashCallback;
+ void* HashCallbackArg;
+} NTLM_CONTEXT;
+
+char* ntlm_negotiate_flags_string(char* buffer, size_t size, UINT32 flags);
+const char* ntlm_message_type_string(UINT32 messageType);
+
+const char* ntlm_state_string(NTLM_STATE state);
+void ntlm_change_state(NTLM_CONTEXT* ntlm, NTLM_STATE state);
+NTLM_STATE ntlm_get_state(NTLM_CONTEXT* ntlm);
+BOOL ntlm_reset_cipher_state(PSecHandle phContext);
+
+SECURITY_STATUS ntlm_computeProofValue(NTLM_CONTEXT* ntlm, SecBuffer* ntproof);
+SECURITY_STATUS ntlm_computeMicValue(NTLM_CONTEXT* ntlm, SecBuffer* micvalue);
+
+#ifdef WITH_DEBUG_NLA
+#define WITH_DEBUG_NTLM
+#endif
+
+BOOL NTLM_init(void);
+
+#endif /* WINPR_SSPI_NTLM_PRIVATE_H */
diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c
new file mode 100644
index 0000000..881a743
--- /dev/null
+++ b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.c
@@ -0,0 +1,775 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * NTLM Security Package (AV_PAIRs)
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/assert.h>
+
+#include "ntlm.h"
+#include "../sspi.h"
+
+#include <winpr/crt.h>
+#include <winpr/print.h>
+#include <winpr/sysinfo.h>
+#include <winpr/tchar.h>
+#include <winpr/crypto.h>
+
+#include "ntlm_compute.h"
+
+#include "ntlm_av_pairs.h"
+
+#include "../../log.h"
+#define TAG WINPR_TAG("sspi.NTLM")
+
+static BOOL ntlm_av_pair_get_next_offset(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pOffset);
+
+static BOOL ntlm_av_pair_check_data(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair, size_t size)
+{
+ size_t offset = 0;
+ if (!pAvPair || cbAvPair < sizeof(NTLM_AV_PAIR) + size)
+ return FALSE;
+ if (!ntlm_av_pair_get_next_offset(pAvPair, cbAvPair, &offset))
+ return FALSE;
+ return cbAvPair >= offset;
+}
+
+static const char* get_av_pair_string(UINT16 pair)
+{
+ switch (pair)
+ {
+ case MsvAvEOL:
+ return "MsvAvEOL";
+ case MsvAvNbComputerName:
+ return "MsvAvNbComputerName";
+ case MsvAvNbDomainName:
+ return "MsvAvNbDomainName";
+ case MsvAvDnsComputerName:
+ return "MsvAvDnsComputerName";
+ case MsvAvDnsDomainName:
+ return "MsvAvDnsDomainName";
+ case MsvAvDnsTreeName:
+ return "MsvAvDnsTreeName";
+ case MsvAvFlags:
+ return "MsvAvFlags";
+ case MsvAvTimestamp:
+ return "MsvAvTimestamp";
+ case MsvAvSingleHost:
+ return "MsvAvSingleHost";
+ case MsvAvTargetName:
+ return "MsvAvTargetName";
+ case MsvAvChannelBindings:
+ return "MsvAvChannelBindings";
+ default:
+ return "UNKNOWN";
+ }
+}
+
+static BOOL ntlm_av_pair_check(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair);
+static NTLM_AV_PAIR* ntlm_av_pair_next(NTLM_AV_PAIR* pAvPairList, size_t* pcbAvPairList);
+
+static INLINE void ntlm_av_pair_set_id(NTLM_AV_PAIR* pAvPair, UINT16 id)
+{
+ WINPR_ASSERT(pAvPair);
+ Data_Write_UINT16(&pAvPair->AvId, id);
+}
+
+static INLINE void ntlm_av_pair_set_len(NTLM_AV_PAIR* pAvPair, UINT16 len)
+{
+ WINPR_ASSERT(pAvPair);
+ Data_Write_UINT16(&pAvPair->AvLen, len);
+}
+
+static BOOL ntlm_av_pair_list_init(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList)
+{
+ NTLM_AV_PAIR* pAvPair = pAvPairList;
+
+ if (!pAvPair || (cbAvPairList < sizeof(NTLM_AV_PAIR)))
+ return FALSE;
+
+ ntlm_av_pair_set_id(pAvPair, MsvAvEOL);
+ ntlm_av_pair_set_len(pAvPair, 0);
+ return TRUE;
+}
+
+static INLINE BOOL ntlm_av_pair_get_id(const NTLM_AV_PAIR* pAvPair, size_t size, UINT16* pair)
+{
+ UINT16 AvId = 0;
+ if (!pAvPair || !pair)
+ return FALSE;
+
+ if (size < sizeof(NTLM_AV_PAIR))
+ return FALSE;
+
+ Data_Read_UINT16(&pAvPair->AvId, AvId);
+
+ *pair = AvId;
+ return TRUE;
+}
+
+ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList)
+{
+ size_t size = 0;
+ size_t cbAvPair = 0;
+ NTLM_AV_PAIR* pAvPair = NULL;
+
+ pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair);
+ if (!pAvPair)
+ return 0;
+
+ size = ((PBYTE)pAvPair - (PBYTE)pAvPairList) + sizeof(NTLM_AV_PAIR);
+ WINPR_ASSERT(size <= ULONG_MAX);
+ return (ULONG)size;
+}
+
+static INLINE BOOL ntlm_av_pair_get_len(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pAvLen)
+{
+ UINT16 AvLen = 0;
+ if (!pAvPair)
+ return FALSE;
+
+ if (size < sizeof(NTLM_AV_PAIR))
+ return FALSE;
+
+ Data_Read_UINT16(&pAvPair->AvLen, AvLen);
+
+ *pAvLen = AvLen;
+ return TRUE;
+}
+
+#ifdef WITH_DEBUG_NTLM
+void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList)
+{
+ UINT16 pair = 0;
+ size_t cbAvPair = cbAvPairList;
+ NTLM_AV_PAIR* pAvPair = pAvPairList;
+
+ if (!ntlm_av_pair_check(pAvPair, cbAvPair))
+ return;
+
+ WLog_VRB(TAG, "AV_PAIRs =");
+
+ while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair) && (pair != MsvAvEOL))
+ {
+ size_t cbLen = 0;
+ ntlm_av_pair_get_len(pAvPair, cbAvPair, &cbLen);
+
+ WLog_VRB(TAG, "\t%s AvId: %" PRIu16 " AvLen: %" PRIu16 "", get_av_pair_string(pair), pair);
+ winpr_HexDump(TAG, WLOG_TRACE, ntlm_av_pair_get_value_pointer(pAvPair), cbLen);
+
+ pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
+ }
+}
+#endif
+
+static ULONG ntlm_av_pair_list_size(ULONG AvPairsCount, ULONG AvPairsValueLength)
+{
+ /* size of headers + value lengths + terminating MsvAvEOL AV_PAIR */
+ return ((AvPairsCount + 1) * 4) + AvPairsValueLength;
+}
+
+PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair)
+{
+ WINPR_ASSERT(pAvPair);
+ return (PBYTE)pAvPair + sizeof(NTLM_AV_PAIR);
+}
+
+static BOOL ntlm_av_pair_get_next_offset(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pOffset)
+{
+ size_t avLen = 0;
+ if (!pOffset)
+ return FALSE;
+
+ if (!ntlm_av_pair_get_len(pAvPair, size, &avLen))
+ return FALSE;
+ *pOffset = avLen + sizeof(NTLM_AV_PAIR);
+ return TRUE;
+}
+
+static BOOL ntlm_av_pair_check(const NTLM_AV_PAIR* pAvPair, size_t cbAvPair)
+{
+ return ntlm_av_pair_check_data(pAvPair, cbAvPair, 0);
+}
+
+static NTLM_AV_PAIR* ntlm_av_pair_next(NTLM_AV_PAIR* pAvPair, size_t* pcbAvPair)
+{
+ size_t offset = 0;
+
+ if (!pcbAvPair)
+ return NULL;
+ if (!ntlm_av_pair_check(pAvPair, *pcbAvPair))
+ return NULL;
+
+ if (!ntlm_av_pair_get_next_offset(pAvPair, *pcbAvPair, &offset))
+ return NULL;
+
+ *pcbAvPair -= offset;
+ return (NTLM_AV_PAIR*)((PBYTE)pAvPair + offset);
+}
+
+NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTLM_AV_ID AvId,
+ size_t* pcbAvPairListRemaining)
+{
+ UINT16 id = 0;
+ size_t cbAvPair = cbAvPairList;
+ NTLM_AV_PAIR* pAvPair = pAvPairList;
+
+ if (!ntlm_av_pair_check(pAvPair, cbAvPair))
+ pAvPair = NULL;
+
+ while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &id))
+ {
+ if (id == AvId)
+ break;
+ if (id == MsvAvEOL)
+ {
+ pAvPair = NULL;
+ break;
+ }
+
+ pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
+ }
+
+ if (!pAvPair)
+ cbAvPair = 0;
+ if (pcbAvPairListRemaining)
+ *pcbAvPairListRemaining = cbAvPair;
+
+ return pAvPair;
+}
+
+static BOOL ntlm_av_pair_add(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTLM_AV_ID AvId,
+ PBYTE Value, UINT16 AvLen)
+{
+ size_t cbAvPair = 0;
+ NTLM_AV_PAIR* pAvPair = NULL;
+
+ pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair);
+
+ /* size of header + value length + terminating MsvAvEOL AV_PAIR */
+ if (!pAvPair || cbAvPair < 2 * sizeof(NTLM_AV_PAIR) + AvLen)
+ return FALSE;
+
+ ntlm_av_pair_set_id(pAvPair, (UINT16)AvId);
+ ntlm_av_pair_set_len(pAvPair, AvLen);
+ if (AvLen)
+ {
+ WINPR_ASSERT(Value != NULL);
+ CopyMemory(ntlm_av_pair_get_value_pointer(pAvPair), Value, AvLen);
+ }
+
+ pAvPair = ntlm_av_pair_next(pAvPair, &cbAvPair);
+ return ntlm_av_pair_list_init(pAvPair, cbAvPair);
+}
+
+static BOOL ntlm_av_pair_add_copy(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList,
+ NTLM_AV_PAIR* pAvPair, size_t cbAvPair)
+{
+ UINT16 pair = 0;
+ size_t avLen = 0;
+
+ if (!ntlm_av_pair_check(pAvPair, cbAvPair))
+ return FALSE;
+
+ if (!ntlm_av_pair_get_id(pAvPair, cbAvPair, &pair))
+ return FALSE;
+
+ if (!ntlm_av_pair_get_len(pAvPair, cbAvPair, &avLen))
+ return FALSE;
+
+ WINPR_ASSERT(avLen <= UINT16_MAX);
+ return ntlm_av_pair_add(pAvPairList, cbAvPairList, pair,
+ ntlm_av_pair_get_value_pointer(pAvPair), (UINT16)avLen);
+}
+
+static int ntlm_get_target_computer_name(PUNICODE_STRING pName, COMPUTER_NAME_FORMAT type)
+{
+ char* name = NULL;
+ int status = -1;
+ DWORD nSize = 0;
+ CHAR* computerName = NULL;
+
+ WINPR_ASSERT(pName);
+
+ if (GetComputerNameExA(ComputerNameNetBIOS, NULL, &nSize) || GetLastError() != ERROR_MORE_DATA)
+ return -1;
+
+ computerName = calloc(nSize, sizeof(CHAR));
+
+ if (!computerName)
+ return -1;
+
+ if (!GetComputerNameExA(ComputerNameNetBIOS, computerName, &nSize))
+ {
+ free(computerName);
+ return -1;
+ }
+
+ if (nSize > MAX_COMPUTERNAME_LENGTH)
+ computerName[MAX_COMPUTERNAME_LENGTH] = '\0';
+
+ name = computerName;
+
+ if (!name)
+ return -1;
+
+ if (type == ComputerNameNetBIOS)
+ CharUpperA(name);
+
+ size_t len = 0;
+ pName->Buffer = ConvertUtf8ToWCharAlloc(name, &len);
+
+ if (!pName->Buffer || (len == 0) || (len > UINT16_MAX / sizeof(WCHAR)))
+ {
+ free(pName->Buffer);
+ pName->Buffer = NULL;
+ free(name);
+ return status;
+ }
+
+ pName->Length = (USHORT)((len) * sizeof(WCHAR));
+ pName->MaximumLength = pName->Length;
+ free(name);
+ return 1;
+}
+
+static void ntlm_free_unicode_string(PUNICODE_STRING string)
+{
+ if (string)
+ {
+ if (string->Length > 0)
+ {
+ free(string->Buffer);
+ string->Buffer = NULL;
+ string->Length = 0;
+ string->MaximumLength = 0;
+ }
+ }
+}
+
+/**
+ * From http://www.ietf.org/proceedings/72/slides/sasl-2.pdf:
+ *
+ * tls-server-end-point:
+ *
+ * The hash of the TLS server's end entity certificate as it appears, octet for octet,
+ * in the server's Certificate message (note that the Certificate message contains a
+ * certificate_list, the first element of which is the server's end entity certificate.)
+ * The hash function to be selected is as follows: if the certificate's signature hash
+ * algorithm is either MD5 or SHA-1, then use SHA-256, otherwise use the certificate's
+ * signature hash algorithm.
+ */
+
+/**
+ * Channel Bindings sample usage:
+ * https://raw.github.com/mozilla/mozilla-central/master/extensions/auth/nsAuthSSPI.cpp
+ */
+
+/*
+typedef struct gss_channel_bindings_struct {
+ OM_uint32 initiator_addrtype;
+ gss_buffer_desc initiator_address;
+ OM_uint32 acceptor_addrtype;
+ gss_buffer_desc acceptor_address;
+ gss_buffer_desc application_data;
+} *gss_channel_bindings_t;
+ */
+
+static BOOL ntlm_md5_update_uint32_be(WINPR_DIGEST_CTX* md5, UINT32 num)
+{
+ BYTE be32[4];
+ be32[0] = (num >> 0) & 0xFF;
+ be32[1] = (num >> 8) & 0xFF;
+ be32[2] = (num >> 16) & 0xFF;
+ be32[3] = (num >> 24) & 0xFF;
+ return winpr_Digest_Update(md5, be32, 4);
+}
+
+static void ntlm_compute_channel_bindings(NTLM_CONTEXT* context)
+{
+ WINPR_DIGEST_CTX* md5 = NULL;
+ BYTE* ChannelBindingToken = NULL;
+ UINT32 ChannelBindingTokenLength = 0;
+ SEC_CHANNEL_BINDINGS* ChannelBindings = NULL;
+
+ WINPR_ASSERT(context);
+
+ ZeroMemory(context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH);
+ ChannelBindings = context->Bindings.Bindings;
+
+ if (!ChannelBindings)
+ return;
+
+ if (!(md5 = winpr_Digest_New()))
+ return;
+
+ if (!winpr_Digest_Init(md5, WINPR_MD_MD5))
+ goto out;
+
+ ChannelBindingTokenLength = context->Bindings.BindingsLength - sizeof(SEC_CHANNEL_BINDINGS);
+ ChannelBindingToken = &((BYTE*)ChannelBindings)[ChannelBindings->dwApplicationDataOffset];
+
+ if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwInitiatorAddrType))
+ goto out;
+
+ if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbInitiatorLength))
+ goto out;
+
+ if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->dwAcceptorAddrType))
+ goto out;
+
+ if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbAcceptorLength))
+ goto out;
+
+ if (!ntlm_md5_update_uint32_be(md5, ChannelBindings->cbApplicationDataLength))
+ goto out;
+
+ if (!winpr_Digest_Update(md5, (void*)ChannelBindingToken, ChannelBindingTokenLength))
+ goto out;
+
+ if (!winpr_Digest_Final(md5, context->ChannelBindingsHash, WINPR_MD5_DIGEST_LENGTH))
+ goto out;
+
+out:
+ winpr_Digest_Free(md5);
+}
+
+static void ntlm_compute_single_host_data(NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+ /**
+ * The Single_Host_Data structure allows a client to send machine-specific information
+ * within an authentication exchange to services on the same machine. The client can
+ * produce additional information to be processed in an implementation-specific way when
+ * the client and server are on the same host. If the server and client platforms are
+ * different or if they are on different hosts, then the information MUST be ignored.
+ * Any fields after the MachineID field MUST be ignored on receipt.
+ */
+ Data_Write_UINT32(&context->SingleHostData.Size, 48);
+ Data_Write_UINT32(&context->SingleHostData.Z4, 0);
+ Data_Write_UINT32(&context->SingleHostData.DataPresent, 1);
+ Data_Write_UINT32(&context->SingleHostData.CustomData, SECURITY_MANDATORY_MEDIUM_RID);
+ FillMemory(context->SingleHostData.MachineID, 32, 0xAA);
+}
+
+BOOL ntlm_construct_challenge_target_info(NTLM_CONTEXT* context)
+{
+ BOOL rc = FALSE;
+ ULONG length = 0;
+ ULONG AvPairsCount = 0;
+ ULONG AvPairsLength = 0;
+ NTLM_AV_PAIR* pAvPairList = NULL;
+ size_t cbAvPairList = 0;
+ UNICODE_STRING NbDomainName = { 0 };
+ UNICODE_STRING NbComputerName = { 0 };
+ UNICODE_STRING DnsDomainName = { 0 };
+ UNICODE_STRING DnsComputerName = { 0 };
+
+ WINPR_ASSERT(context);
+
+ if (ntlm_get_target_computer_name(&NbDomainName, ComputerNameNetBIOS) < 0)
+ goto fail;
+
+ NbComputerName.Buffer = NULL;
+
+ if (ntlm_get_target_computer_name(&NbComputerName, ComputerNameNetBIOS) < 0)
+ goto fail;
+
+ DnsDomainName.Buffer = NULL;
+
+ if (ntlm_get_target_computer_name(&DnsDomainName, ComputerNameDnsDomain) < 0)
+ goto fail;
+
+ DnsComputerName.Buffer = NULL;
+
+ if (ntlm_get_target_computer_name(&DnsComputerName, ComputerNameDnsHostname) < 0)
+ goto fail;
+
+ AvPairsCount = 5;
+ AvPairsLength = NbDomainName.Length + NbComputerName.Length + DnsDomainName.Length +
+ DnsComputerName.Length + 8;
+ length = ntlm_av_pair_list_size(AvPairsCount, AvPairsLength);
+
+ if (!sspi_SecBufferAlloc(&context->ChallengeTargetInfo, length))
+ goto fail;
+
+ pAvPairList = (NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer;
+ cbAvPairList = context->ChallengeTargetInfo.cbBuffer;
+
+ if (!ntlm_av_pair_list_init(pAvPairList, cbAvPairList))
+ goto fail;
+
+ if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbDomainName, (PBYTE)NbDomainName.Buffer,
+ NbDomainName.Length))
+ goto fail;
+
+ if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvNbComputerName,
+ (PBYTE)NbComputerName.Buffer, NbComputerName.Length))
+ goto fail;
+
+ if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsDomainName,
+ (PBYTE)DnsDomainName.Buffer, DnsDomainName.Length))
+ goto fail;
+
+ if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvDnsComputerName,
+ (PBYTE)DnsComputerName.Buffer, DnsComputerName.Length))
+ goto fail;
+
+ if (!ntlm_av_pair_add(pAvPairList, cbAvPairList, MsvAvTimestamp, context->Timestamp,
+ sizeof(context->Timestamp)))
+ goto fail;
+
+ rc = TRUE;
+fail:
+ ntlm_free_unicode_string(&NbDomainName);
+ ntlm_free_unicode_string(&NbComputerName);
+ ntlm_free_unicode_string(&DnsDomainName);
+ ntlm_free_unicode_string(&DnsComputerName);
+ return rc;
+}
+
+BOOL ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context)
+{
+ ULONG size = 0;
+ ULONG AvPairsCount = 0;
+ ULONG AvPairsValueLength = 0;
+ NTLM_AV_PAIR* AvTimestamp = NULL;
+ NTLM_AV_PAIR* AvNbDomainName = NULL;
+ NTLM_AV_PAIR* AvNbComputerName = NULL;
+ NTLM_AV_PAIR* AvDnsDomainName = NULL;
+ NTLM_AV_PAIR* AvDnsComputerName = NULL;
+ NTLM_AV_PAIR* AvDnsTreeName = NULL;
+ NTLM_AV_PAIR* ChallengeTargetInfo = NULL;
+ NTLM_AV_PAIR* AuthenticateTargetInfo = NULL;
+ size_t cbAvTimestamp = 0;
+ size_t cbAvNbDomainName = 0;
+ size_t cbAvNbComputerName = 0;
+ size_t cbAvDnsDomainName = 0;
+ size_t cbAvDnsComputerName = 0;
+ size_t cbAvDnsTreeName = 0;
+ size_t cbChallengeTargetInfo = 0;
+ size_t cbAuthenticateTargetInfo = 0;
+
+ WINPR_ASSERT(context);
+
+ AvPairsCount = 1;
+ AvPairsValueLength = 0;
+ ChallengeTargetInfo = (NTLM_AV_PAIR*)context->ChallengeTargetInfo.pvBuffer;
+ cbChallengeTargetInfo = context->ChallengeTargetInfo.cbBuffer;
+ AvNbDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvNbDomainName,
+ &cbAvNbDomainName);
+ AvNbComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
+ MsvAvNbComputerName, &cbAvNbComputerName);
+ AvDnsDomainName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
+ MsvAvDnsDomainName, &cbAvDnsDomainName);
+ AvDnsComputerName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo,
+ MsvAvDnsComputerName, &cbAvDnsComputerName);
+ AvDnsTreeName = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvDnsTreeName,
+ &cbAvDnsTreeName);
+ AvTimestamp = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvTimestamp,
+ &cbAvTimestamp);
+
+ if (AvNbDomainName)
+ {
+ size_t avLen = 0;
+ if (!ntlm_av_pair_get_len(AvNbDomainName, cbAvNbDomainName, &avLen))
+ goto fail;
+ AvPairsCount++; /* MsvAvNbDomainName */
+ AvPairsValueLength += avLen;
+ }
+
+ if (AvNbComputerName)
+ {
+ size_t avLen = 0;
+ if (!ntlm_av_pair_get_len(AvNbComputerName, cbAvNbComputerName, &avLen))
+ goto fail;
+ AvPairsCount++; /* MsvAvNbComputerName */
+ AvPairsValueLength += avLen;
+ }
+
+ if (AvDnsDomainName)
+ {
+ size_t avLen = 0;
+ if (!ntlm_av_pair_get_len(AvDnsDomainName, cbAvDnsDomainName, &avLen))
+ goto fail;
+ AvPairsCount++; /* MsvAvDnsDomainName */
+ AvPairsValueLength += avLen;
+ }
+
+ if (AvDnsComputerName)
+ {
+ size_t avLen = 0;
+ if (!ntlm_av_pair_get_len(AvDnsComputerName, cbAvDnsComputerName, &avLen))
+ goto fail;
+ AvPairsCount++; /* MsvAvDnsComputerName */
+ AvPairsValueLength += avLen;
+ }
+
+ if (AvDnsTreeName)
+ {
+ size_t avLen = 0;
+ if (!ntlm_av_pair_get_len(AvDnsTreeName, cbAvDnsTreeName, &avLen))
+ goto fail;
+ AvPairsCount++; /* MsvAvDnsTreeName */
+ AvPairsValueLength += avLen;
+ }
+
+ AvPairsCount++; /* MsvAvTimestamp */
+ AvPairsValueLength += 8;
+
+ if (context->UseMIC)
+ {
+ AvPairsCount++; /* MsvAvFlags */
+ AvPairsValueLength += 4;
+ }
+
+ if (context->SendSingleHostData)
+ {
+ AvPairsCount++; /* MsvAvSingleHost */
+ ntlm_compute_single_host_data(context);
+ AvPairsValueLength += context->SingleHostData.Size;
+ }
+
+ /**
+ * Extended Protection for Authentication:
+ * http://blogs.technet.com/b/srd/archive/2009/12/08/extended-protection-for-authentication.aspx
+ */
+
+ if (!context->SuppressExtendedProtection)
+ {
+ /**
+ * SEC_CHANNEL_BINDINGS structure
+ * http://msdn.microsoft.com/en-us/library/windows/desktop/dd919963/
+ */
+ AvPairsCount++; /* MsvAvChannelBindings */
+ AvPairsValueLength += 16;
+ ntlm_compute_channel_bindings(context);
+
+ if (context->ServicePrincipalName.Length > 0)
+ {
+ AvPairsCount++; /* MsvAvTargetName */
+ AvPairsValueLength += context->ServicePrincipalName.Length;
+ }
+ }
+
+ size = ntlm_av_pair_list_size(AvPairsCount, AvPairsValueLength);
+
+ if (context->NTLMv2)
+ size += 8; /* unknown 8-byte padding */
+
+ if (!sspi_SecBufferAlloc(&context->AuthenticateTargetInfo, size))
+ goto fail;
+
+ AuthenticateTargetInfo = (NTLM_AV_PAIR*)context->AuthenticateTargetInfo.pvBuffer;
+ cbAuthenticateTargetInfo = context->AuthenticateTargetInfo.cbBuffer;
+
+ if (!ntlm_av_pair_list_init(AuthenticateTargetInfo, cbAuthenticateTargetInfo))
+ goto fail;
+
+ if (AvNbDomainName)
+ {
+ if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvNbDomainName,
+ cbAvNbDomainName))
+ goto fail;
+ }
+
+ if (AvNbComputerName)
+ {
+ if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
+ AvNbComputerName, cbAvNbComputerName))
+ goto fail;
+ }
+
+ if (AvDnsDomainName)
+ {
+ if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
+ AvDnsDomainName, cbAvDnsDomainName))
+ goto fail;
+ }
+
+ if (AvDnsComputerName)
+ {
+ if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
+ AvDnsComputerName, cbAvDnsComputerName))
+ goto fail;
+ }
+
+ if (AvDnsTreeName)
+ {
+ if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvDnsTreeName,
+ cbAvDnsTreeName))
+ goto fail;
+ }
+
+ if (AvTimestamp)
+ {
+ if (!ntlm_av_pair_add_copy(AuthenticateTargetInfo, cbAuthenticateTargetInfo, AvTimestamp,
+ cbAvTimestamp))
+ goto fail;
+ }
+
+ if (context->UseMIC)
+ {
+ UINT32 flags = 0;
+ Data_Write_UINT32(&flags, MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK);
+
+ if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvFlags,
+ (PBYTE)&flags, 4))
+ goto fail;
+ }
+
+ if (context->SendSingleHostData)
+ {
+ WINPR_ASSERT(context->SingleHostData.Size <= UINT16_MAX);
+ if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvSingleHost,
+ (PBYTE)&context->SingleHostData,
+ (UINT16)context->SingleHostData.Size))
+ goto fail;
+ }
+
+ if (!context->SuppressExtendedProtection)
+ {
+ if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo,
+ MsvAvChannelBindings, context->ChannelBindingsHash, 16))
+ goto fail;
+
+ if (context->ServicePrincipalName.Length > 0)
+ {
+ if (!ntlm_av_pair_add(AuthenticateTargetInfo, cbAuthenticateTargetInfo, MsvAvTargetName,
+ (PBYTE)context->ServicePrincipalName.Buffer,
+ context->ServicePrincipalName.Length))
+ goto fail;
+ }
+ }
+
+ if (context->NTLMv2)
+ {
+ NTLM_AV_PAIR* AvEOL = NULL;
+ AvEOL = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvEOL, NULL);
+
+ if (!AvEOL)
+ goto fail;
+
+ ZeroMemory(AvEOL, sizeof(NTLM_AV_PAIR));
+ }
+
+ return TRUE;
+fail:
+ sspi_SecBufferFree(&context->AuthenticateTargetInfo);
+ return FALSE;
+}
diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h
new file mode 100644
index 0000000..ab9da43
--- /dev/null
+++ b/winpr/libwinpr/sspi/NTLM/ntlm_av_pairs.h
@@ -0,0 +1,42 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * NTLM Security Package (AV_PAIRs)
+ *
+ * Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WINPR_SSPI_NTLM_AV_PAIRS_H
+#define WINPR_SSPI_NTLM_AV_PAIRS_H
+
+#include <winpr/config.h>
+
+#include "ntlm.h"
+
+#include <winpr/stream.h>
+
+ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList);
+
+#ifdef WITH_DEBUG_NTLM
+void ntlm_print_av_pair_list(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList);
+#endif
+
+PBYTE ntlm_av_pair_get_value_pointer(NTLM_AV_PAIR* pAvPair);
+NTLM_AV_PAIR* ntlm_av_pair_get(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList, NTLM_AV_ID AvId,
+ size_t* pcbAvPairListRemaining);
+
+BOOL ntlm_construct_challenge_target_info(NTLM_CONTEXT* context);
+BOOL ntlm_construct_authenticate_target_info(NTLM_CONTEXT* context);
+
+#endif /* WINPR_SSPI_NTLM_AV_PAIRS_H */
diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.c b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c
new file mode 100644
index 0000000..9c6e818
--- /dev/null
+++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.c
@@ -0,0 +1,887 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * NTLM Security Package (Compute)
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include <winpr/assert.h>
+
+#include "ntlm.h"
+#include "../sspi.h"
+
+#include <winpr/crt.h>
+#include <winpr/sam.h>
+#include <winpr/ntlm.h>
+#include <winpr/print.h>
+#include <winpr/crypto.h>
+#include <winpr/sysinfo.h>
+
+#include "ntlm_compute.h"
+
+#include "../../log.h"
+#define TAG WINPR_TAG("sspi.NTLM")
+
+#define NTLM_CheckAndLogRequiredCapacity(tag, s, nmemb, what) \
+ Stream_CheckAndLogRequiredCapacityEx(tag, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ") " what, \
+ __func__, __FILE__, (size_t)__LINE__)
+
+static char NTLM_CLIENT_SIGN_MAGIC[] = "session key to client-to-server signing key magic constant";
+static char NTLM_SERVER_SIGN_MAGIC[] = "session key to server-to-client signing key magic constant";
+static char NTLM_CLIENT_SEAL_MAGIC[] = "session key to client-to-server sealing key magic constant";
+static char NTLM_SERVER_SEAL_MAGIC[] = "session key to server-to-client sealing key magic constant";
+
+static const BYTE NTLM_NULL_BUFFER[16] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
+
+/**
+ * Populate VERSION structure msdn{cc236654}
+ * @param versionInfo A pointer to the version struct
+ *
+ * @return \b TRUE for success, \b FALSE for failure
+ */
+
+BOOL ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo)
+{
+ WINPR_ASSERT(versionInfo);
+
+#if defined(WITH_WINPR_DEPRECATED)
+ OSVERSIONINFOA osVersionInfo = { 0 };
+ osVersionInfo.dwOSVersionInfoSize = sizeof(OSVERSIONINFOA);
+ if (!GetVersionExA(&osVersionInfo))
+ return FALSE;
+ versionInfo->ProductMajorVersion = (UINT8)osVersionInfo.dwMajorVersion;
+ versionInfo->ProductMinorVersion = (UINT8)osVersionInfo.dwMinorVersion;
+ versionInfo->ProductBuild = (UINT16)osVersionInfo.dwBuildNumber;
+#else
+ /* Always return fixed version number.
+ *
+ * ProductVersion is fixed since windows 10 to Major 10, Minor 0
+ * ProductBuild taken from https://en.wikipedia.org/wiki/Windows_11_version_history
+ * with most recent (pre) release build number
+ */
+ versionInfo->ProductMajorVersion = 10;
+ versionInfo->ProductMinorVersion = 0;
+ versionInfo->ProductBuild = 22631;
+#endif
+ ZeroMemory(versionInfo->Reserved, sizeof(versionInfo->Reserved));
+ versionInfo->NTLMRevisionCurrent = NTLMSSP_REVISION_W2K3;
+ return TRUE;
+}
+
+/**
+ * Read VERSION structure. msdn{cc236654}
+ * @param s A pointer to a stream to read
+ * @param versionInfo A pointer to the struct to read data to
+ *
+ * @return \b TRUE for success, \b FALSE for failure
+ */
+
+BOOL ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(versionInfo);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ Stream_Read_UINT8(s, versionInfo->ProductMajorVersion); /* ProductMajorVersion (1 byte) */
+ Stream_Read_UINT8(s, versionInfo->ProductMinorVersion); /* ProductMinorVersion (1 byte) */
+ Stream_Read_UINT16(s, versionInfo->ProductBuild); /* ProductBuild (2 bytes) */
+ Stream_Read(s, versionInfo->Reserved, sizeof(versionInfo->Reserved)); /* Reserved (3 bytes) */
+ Stream_Read_UINT8(s, versionInfo->NTLMRevisionCurrent); /* NTLMRevisionCurrent (1 byte) */
+ return TRUE;
+}
+
+/**
+ * Write VERSION structure. msdn{cc236654}
+ * @param s A pointer to the stream to write to
+ * @param versionInfo A pointer to the buffer to read the data from
+ *
+ * @return \b TRUE for success, \b FALSE for failure
+ */
+
+BOOL ntlm_write_version_info(wStream* s, const NTLM_VERSION_INFO* versionInfo)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(versionInfo);
+
+ if (!Stream_CheckAndLogRequiredCapacityEx(
+ TAG, WLOG_WARN, s, 5ull + sizeof(versionInfo->Reserved), 1ull,
+ "%s(%s:%" PRIuz ") NTLM_VERSION_INFO", __func__, __FILE__, (size_t)__LINE__))
+ return FALSE;
+
+ Stream_Write_UINT8(s, versionInfo->ProductMajorVersion); /* ProductMajorVersion (1 byte) */
+ Stream_Write_UINT8(s, versionInfo->ProductMinorVersion); /* ProductMinorVersion (1 byte) */
+ Stream_Write_UINT16(s, versionInfo->ProductBuild); /* ProductBuild (2 bytes) */
+ Stream_Write(s, versionInfo->Reserved, sizeof(versionInfo->Reserved)); /* Reserved (3 bytes) */
+ Stream_Write_UINT8(s, versionInfo->NTLMRevisionCurrent); /* NTLMRevisionCurrent (1 byte) */
+ return TRUE;
+}
+
+/**
+ * Print VERSION structure. msdn{cc236654}
+ * @param versionInfo A pointer to the struct containing the data to print
+ */
+#ifdef WITH_DEBUG_NTLM
+void ntlm_print_version_info(const NTLM_VERSION_INFO* versionInfo)
+{
+ WINPR_ASSERT(versionInfo);
+
+ WLog_VRB(TAG, "VERSION ={");
+ WLog_VRB(TAG, "\tProductMajorVersion: %" PRIu8 "", versionInfo->ProductMajorVersion);
+ WLog_VRB(TAG, "\tProductMinorVersion: %" PRIu8 "", versionInfo->ProductMinorVersion);
+ WLog_VRB(TAG, "\tProductBuild: %" PRIu16 "", versionInfo->ProductBuild);
+ WLog_VRB(TAG, "\tReserved: 0x%02" PRIX8 "%02" PRIX8 "%02" PRIX8 "", versionInfo->Reserved[0],
+ versionInfo->Reserved[1], versionInfo->Reserved[2]);
+ WLog_VRB(TAG, "\tNTLMRevisionCurrent: 0x%02" PRIX8 "", versionInfo->NTLMRevisionCurrent);
+}
+#endif
+
+static BOOL ntlm_read_ntlm_v2_client_challenge(wStream* s, NTLMv2_CLIENT_CHALLENGE* challenge)
+{
+ size_t size = 0;
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(challenge);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 28))
+ return FALSE;
+
+ Stream_Read_UINT8(s, challenge->RespType);
+ Stream_Read_UINT8(s, challenge->HiRespType);
+ Stream_Read_UINT16(s, challenge->Reserved1);
+ Stream_Read_UINT32(s, challenge->Reserved2);
+ Stream_Read(s, challenge->Timestamp, 8);
+ Stream_Read(s, challenge->ClientChallenge, 8);
+ Stream_Read_UINT32(s, challenge->Reserved3);
+ size = Stream_Length(s) - Stream_GetPosition(s);
+
+ if (size > UINT32_MAX)
+ {
+ WLog_ERR(TAG, "NTLMv2_CLIENT_CHALLENGE::cbAvPairs too large, got %" PRIuz "bytes", size);
+ return FALSE;
+ }
+
+ challenge->cbAvPairs = (UINT32)size;
+ challenge->AvPairs = (NTLM_AV_PAIR*)malloc(challenge->cbAvPairs);
+
+ if (!challenge->AvPairs)
+ {
+ WLog_ERR(TAG, "NTLMv2_CLIENT_CHALLENGE::AvPairs failed to allocate %" PRIu32 "bytes",
+ challenge->cbAvPairs);
+ return FALSE;
+ }
+
+ Stream_Read(s, challenge->AvPairs, size);
+ return TRUE;
+}
+
+static BOOL ntlm_write_ntlm_v2_client_challenge(wStream* s,
+ const NTLMv2_CLIENT_CHALLENGE* challenge)
+{
+ ULONG length = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(challenge);
+
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 28, "NTLMv2_CLIENT_CHALLENGE"))
+ return FALSE;
+
+ Stream_Write_UINT8(s, challenge->RespType);
+ Stream_Write_UINT8(s, challenge->HiRespType);
+ Stream_Write_UINT16(s, challenge->Reserved1);
+ Stream_Write_UINT32(s, challenge->Reserved2);
+ Stream_Write(s, challenge->Timestamp, 8);
+ Stream_Write(s, challenge->ClientChallenge, 8);
+ Stream_Write_UINT32(s, challenge->Reserved3);
+ length = ntlm_av_pair_list_length(challenge->AvPairs, challenge->cbAvPairs);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, length))
+ return FALSE;
+
+ Stream_Write(s, challenge->AvPairs, length);
+ return TRUE;
+}
+
+BOOL ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(response);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
+ return FALSE;
+
+ Stream_Read(s, response->Response, 16);
+ return ntlm_read_ntlm_v2_client_challenge(s, &(response->Challenge));
+}
+
+BOOL ntlm_write_ntlm_v2_response(wStream* s, const NTLMv2_RESPONSE* response)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(response);
+
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 16ull, "NTLMv2_RESPONSE"))
+ return FALSE;
+
+ Stream_Write(s, response->Response, 16);
+ return ntlm_write_ntlm_v2_client_challenge(s, &(response->Challenge));
+}
+
+/**
+ * Get current time, in tenths of microseconds since midnight of January 1, 1601.
+ * @param[out] timestamp 64-bit little-endian timestamp
+ */
+
+void ntlm_current_time(BYTE* timestamp)
+{
+ FILETIME filetime = { 0 };
+ ULARGE_INTEGER time64 = { 0 };
+
+ WINPR_ASSERT(timestamp);
+
+ GetSystemTimeAsFileTime(&filetime);
+ time64.u.LowPart = filetime.dwLowDateTime;
+ time64.u.HighPart = filetime.dwHighDateTime;
+ CopyMemory(timestamp, &(time64.QuadPart), 8);
+}
+
+/**
+ * Generate timestamp for AUTHENTICATE_MESSAGE.
+ *
+ * @param context A pointer to the NTLM context
+ */
+
+void ntlm_generate_timestamp(NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+
+ if (memcmp(context->ChallengeTimestamp, NTLM_NULL_BUFFER, 8) != 0)
+ CopyMemory(context->Timestamp, context->ChallengeTimestamp, 8);
+ else
+ ntlm_current_time(context->Timestamp);
+}
+
+static BOOL ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash)
+{
+ BOOL rc = FALSE;
+ WINPR_SAM* sam = NULL;
+ WINPR_SAM_ENTRY* entry = NULL;
+ SSPI_CREDENTIALS* credentials = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(hash);
+
+ credentials = context->credentials;
+ sam = SamOpen(context->SamFile, TRUE);
+
+ if (!sam)
+ goto fail;
+
+ entry = SamLookupUserW(
+ sam, (LPWSTR)credentials->identity.User, credentials->identity.UserLength * sizeof(WCHAR),
+ (LPWSTR)credentials->identity.Domain, credentials->identity.DomainLength * sizeof(WCHAR));
+
+ if (!entry)
+ {
+ entry = SamLookupUserW(sam, (LPWSTR)credentials->identity.User,
+ credentials->identity.UserLength * sizeof(WCHAR), NULL, 0);
+ }
+
+ if (!entry)
+ goto fail;
+
+#ifdef WITH_DEBUG_NTLM
+ WLog_VRB(TAG, "NTLM Hash:");
+ winpr_HexDump(TAG, WLOG_DEBUG, entry->NtHash, 16);
+#endif
+ NTOWFv2FromHashW(entry->NtHash, (LPWSTR)credentials->identity.User,
+ credentials->identity.UserLength * sizeof(WCHAR),
+ (LPWSTR)credentials->identity.Domain,
+ credentials->identity.DomainLength * sizeof(WCHAR), (BYTE*)hash);
+
+ rc = TRUE;
+
+fail:
+ SamFreeEntry(sam, entry);
+ SamClose(sam);
+ if (!rc)
+ WLog_ERR(TAG, "Error: Could not find user in SAM database");
+
+ return rc;
+}
+
+static int ntlm_convert_password_hash(NTLM_CONTEXT* context, BYTE* hash)
+{
+ char PasswordHash[32] = { 0 };
+ INT64 PasswordHashLength = 0;
+ SSPI_CREDENTIALS* credentials = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(hash);
+
+ credentials = context->credentials;
+ /* Password contains a password hash of length (PasswordLength -
+ * SSPI_CREDENTIALS_HASH_LENGTH_OFFSET) */
+ PasswordHashLength = credentials->identity.PasswordLength - SSPI_CREDENTIALS_HASH_LENGTH_OFFSET;
+
+ WINPR_ASSERT(PasswordHashLength >= 0);
+ WINPR_ASSERT((size_t)PasswordHashLength < ARRAYSIZE(PasswordHash));
+ if (ConvertWCharNToUtf8(credentials->identity.Password, PasswordHashLength, PasswordHash,
+ ARRAYSIZE(PasswordHash)) <= 0)
+ return -1;
+
+ CharUpperBuffA(PasswordHash, (DWORD)PasswordHashLength);
+
+ for (size_t i = 0; i < ARRAYSIZE(PasswordHash); i += 2)
+ {
+ BYTE hn =
+ (BYTE)(PasswordHash[i] > '9' ? PasswordHash[i] - 'A' + 10 : PasswordHash[i] - '0');
+ BYTE ln = (BYTE)(PasswordHash[i + 1] > '9' ? PasswordHash[i + 1] - 'A' + 10
+ : PasswordHash[i + 1] - '0');
+ hash[i / 2] = (BYTE)((hn << 4) | ln);
+ }
+
+ return 1;
+}
+
+static BOOL ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash)
+{
+ SSPI_CREDENTIALS* credentials = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(hash);
+
+ credentials = context->credentials;
+#ifdef WITH_DEBUG_NTLM
+
+ if (credentials)
+ {
+ WLog_VRB(TAG, "Password (length = %" PRIu32 ")", credentials->identity.PasswordLength * 2);
+ winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.Password,
+ credentials->identity.PasswordLength * 2);
+ WLog_VRB(TAG, "Username (length = %" PRIu32 ")", credentials->identity.UserLength * 2);
+ winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.User,
+ credentials->identity.UserLength * 2);
+ WLog_VRB(TAG, "Domain (length = %" PRIu32 ")", credentials->identity.DomainLength * 2);
+ winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)credentials->identity.Domain,
+ credentials->identity.DomainLength * 2);
+ }
+ else
+ WLog_VRB(TAG, "Strange, NTLM_CONTEXT is missing valid credentials...");
+
+ WLog_VRB(TAG, "Workstation (length = %" PRIu16 ")", context->Workstation.Length);
+ winpr_HexDump(TAG, WLOG_TRACE, (BYTE*)context->Workstation.Buffer, context->Workstation.Length);
+ WLog_VRB(TAG, "NTOWFv2, NTLMv2 Hash");
+ winpr_HexDump(TAG, WLOG_TRACE, context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH);
+#endif
+
+ if (memcmp(context->NtlmV2Hash, NTLM_NULL_BUFFER, 16) != 0)
+ return TRUE;
+
+ if (!credentials)
+ return FALSE;
+ else if (memcmp(context->NtlmHash, NTLM_NULL_BUFFER, 16) != 0)
+ {
+ NTOWFv2FromHashW(context->NtlmHash, (LPWSTR)credentials->identity.User,
+ credentials->identity.UserLength * 2, (LPWSTR)credentials->identity.Domain,
+ credentials->identity.DomainLength * 2, (BYTE*)hash);
+ }
+ else if (credentials->identity.PasswordLength > SSPI_CREDENTIALS_HASH_LENGTH_OFFSET)
+ {
+ /* Special case for WinPR: password hash */
+ if (ntlm_convert_password_hash(context, context->NtlmHash) < 0)
+ return FALSE;
+
+ NTOWFv2FromHashW(context->NtlmHash, (LPWSTR)credentials->identity.User,
+ credentials->identity.UserLength * 2, (LPWSTR)credentials->identity.Domain,
+ credentials->identity.DomainLength * 2, (BYTE*)hash);
+ }
+ else if (credentials->identity.Password)
+ {
+ NTOWFv2W((LPWSTR)credentials->identity.Password, credentials->identity.PasswordLength * 2,
+ (LPWSTR)credentials->identity.User, credentials->identity.UserLength * 2,
+ (LPWSTR)credentials->identity.Domain, credentials->identity.DomainLength * 2,
+ (BYTE*)hash);
+ }
+ else if (context->HashCallback)
+ {
+ int ret = 0;
+ SecBuffer proofValue;
+ SecBuffer micValue;
+
+ if (ntlm_computeProofValue(context, &proofValue) != SEC_E_OK)
+ return FALSE;
+
+ if (ntlm_computeMicValue(context, &micValue) != SEC_E_OK)
+ {
+ sspi_SecBufferFree(&proofValue);
+ return FALSE;
+ }
+
+ ret = context->HashCallback(context->HashCallbackArg, &credentials->identity, &proofValue,
+ context->EncryptedRandomSessionKey,
+ context->AUTHENTICATE_MESSAGE.MessageIntegrityCheck, &micValue,
+ hash);
+ sspi_SecBufferFree(&proofValue);
+ sspi_SecBufferFree(&micValue);
+ return ret ? TRUE : FALSE;
+ }
+ else if (context->UseSamFileDatabase)
+ {
+ return ntlm_fetch_ntlm_v2_hash(context, hash);
+ }
+
+ return TRUE;
+}
+
+BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context)
+{
+ BYTE* response = NULL;
+ BYTE value[WINPR_MD5_DIGEST_LENGTH] = { 0 };
+
+ WINPR_ASSERT(context);
+
+ if (context->LmCompatibilityLevel < 2)
+ {
+ if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24))
+ return FALSE;
+
+ ZeroMemory(context->LmChallengeResponse.pvBuffer, 24);
+ return TRUE;
+ }
+
+ /* Compute the NTLMv2 hash */
+
+ if (!ntlm_compute_ntlm_v2_hash(context, context->NtlmV2Hash))
+ return FALSE;
+
+ /* Concatenate the server and client challenges */
+ CopyMemory(value, context->ServerChallenge, 8);
+ CopyMemory(&value[8], context->ClientChallenge, 8);
+
+ if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24))
+ return FALSE;
+
+ response = (BYTE*)context->LmChallengeResponse.pvBuffer;
+ /* Compute the HMAC-MD5 hash of the resulting value using the NTLMv2 hash as the key */
+ winpr_HMAC(WINPR_MD_MD5, (void*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH, (BYTE*)value,
+ WINPR_MD5_DIGEST_LENGTH, (BYTE*)response, WINPR_MD5_DIGEST_LENGTH);
+ /* Concatenate the resulting HMAC-MD5 hash and the client challenge, giving us the LMv2 response
+ * (24 bytes) */
+ CopyMemory(&response[16], context->ClientChallenge, 8);
+ return TRUE;
+}
+
+/**
+ * Compute NTLMv2 Response.
+ *
+ * NTLMv2_RESPONSE msdn{cc236653}
+ * NTLMv2 Authentication msdn{cc236700}
+ *
+ * @param context A pointer to the NTLM context
+ * @return \b TRUE for success, \b FALSE for failure
+ */
+
+BOOL ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context)
+{
+ BYTE* blob = NULL;
+ SecBuffer ntlm_v2_temp = { 0 };
+ SecBuffer ntlm_v2_temp_chal = { 0 };
+ PSecBuffer TargetInfo = NULL;
+
+ WINPR_ASSERT(context);
+
+ TargetInfo = &context->ChallengeTargetInfo;
+ BOOL ret = FALSE;
+
+ if (!sspi_SecBufferAlloc(&ntlm_v2_temp, TargetInfo->cbBuffer + 28))
+ goto exit;
+
+ ZeroMemory(ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
+ blob = (BYTE*)ntlm_v2_temp.pvBuffer;
+
+ /* Compute the NTLMv2 hash */
+ if (!ntlm_compute_ntlm_v2_hash(context, (BYTE*)context->NtlmV2Hash))
+ goto exit;
+
+ /* Construct temp */
+ blob[0] = 1; /* RespType (1 byte) */
+ blob[1] = 1; /* HighRespType (1 byte) */
+ /* Reserved1 (2 bytes) */
+ /* Reserved2 (4 bytes) */
+ CopyMemory(&blob[8], context->Timestamp, 8); /* Timestamp (8 bytes) */
+ CopyMemory(&blob[16], context->ClientChallenge, 8); /* ClientChallenge (8 bytes) */
+ /* Reserved3 (4 bytes) */
+ CopyMemory(&blob[28], TargetInfo->pvBuffer, TargetInfo->cbBuffer);
+#ifdef WITH_DEBUG_NTLM
+ WLog_VRB(TAG, "NTLMv2 Response Temp Blob");
+ winpr_HexDump(TAG, WLOG_TRACE, ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
+#endif
+
+ /* Concatenate server challenge with temp */
+
+ if (!sspi_SecBufferAlloc(&ntlm_v2_temp_chal, ntlm_v2_temp.cbBuffer + 8))
+ goto exit;
+
+ blob = (BYTE*)ntlm_v2_temp_chal.pvBuffer;
+ CopyMemory(blob, context->ServerChallenge, 8);
+ CopyMemory(&blob[8], ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
+ winpr_HMAC(WINPR_MD_MD5, (BYTE*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH,
+ (BYTE*)ntlm_v2_temp_chal.pvBuffer, ntlm_v2_temp_chal.cbBuffer,
+ context->NtProofString, WINPR_MD5_DIGEST_LENGTH);
+
+ /* NtChallengeResponse, Concatenate NTProofStr with temp */
+
+ if (!sspi_SecBufferAlloc(&context->NtChallengeResponse, ntlm_v2_temp.cbBuffer + 16))
+ goto exit;
+
+ blob = (BYTE*)context->NtChallengeResponse.pvBuffer;
+ CopyMemory(blob, context->NtProofString, WINPR_MD5_DIGEST_LENGTH);
+ CopyMemory(&blob[16], ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
+ /* Compute SessionBaseKey, the HMAC-MD5 hash of NTProofStr using the NTLMv2 hash as the key */
+ winpr_HMAC(WINPR_MD_MD5, (BYTE*)context->NtlmV2Hash, WINPR_MD5_DIGEST_LENGTH,
+ context->NtProofString, WINPR_MD5_DIGEST_LENGTH, context->SessionBaseKey,
+ WINPR_MD5_DIGEST_LENGTH);
+ ret = TRUE;
+exit:
+ sspi_SecBufferFree(&ntlm_v2_temp);
+ sspi_SecBufferFree(&ntlm_v2_temp_chal);
+ return ret;
+}
+
+/**
+ * Encrypt the given plain text using RC4 and the given key.
+ * @param key RC4 key
+ * @param length text length
+ * @param plaintext plain text
+ * @param ciphertext cipher text
+ */
+
+void ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext)
+{
+ WINPR_RC4_CTX* rc4 = winpr_RC4_New(key, 16);
+
+ if (rc4)
+ {
+ winpr_RC4_Update(rc4, length, plaintext, ciphertext);
+ winpr_RC4_Free(rc4);
+ }
+}
+
+/**
+ * Generate client challenge (8-byte nonce).
+ * @param context A pointer to the NTLM context
+ */
+
+void ntlm_generate_client_challenge(NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+
+ /* ClientChallenge is used in computation of LMv2 and NTLMv2 responses */
+ if (memcmp(context->ClientChallenge, NTLM_NULL_BUFFER, sizeof(context->ClientChallenge)) == 0)
+ winpr_RAND(context->ClientChallenge, sizeof(context->ClientChallenge));
+}
+
+/**
+ * Generate server challenge (8-byte nonce).
+ * @param context A pointer to the NTLM context
+ */
+
+void ntlm_generate_server_challenge(NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+
+ if (memcmp(context->ServerChallenge, NTLM_NULL_BUFFER, sizeof(context->ServerChallenge)) == 0)
+ winpr_RAND(context->ServerChallenge, sizeof(context->ServerChallenge));
+}
+
+/**
+ * Generate KeyExchangeKey (the 128-bit SessionBaseKey). msdn{cc236710}
+ * @param context A pointer to the NTLM context
+ */
+
+void ntlm_generate_key_exchange_key(NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(sizeof(context->KeyExchangeKey) == sizeof(context->SessionBaseKey));
+
+ /* In NTLMv2, KeyExchangeKey is the 128-bit SessionBaseKey */
+ CopyMemory(context->KeyExchangeKey, context->SessionBaseKey, sizeof(context->KeyExchangeKey));
+}
+
+/**
+ * Generate RandomSessionKey (16-byte nonce).
+ * @param context A pointer to the NTLM context
+ */
+
+void ntlm_generate_random_session_key(NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+ winpr_RAND(context->RandomSessionKey, sizeof(context->RandomSessionKey));
+}
+
+/**
+ * Generate ExportedSessionKey (the RandomSessionKey, exported)
+ * @param context A pointer to the NTLM context
+ */
+
+void ntlm_generate_exported_session_key(NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+
+ CopyMemory(context->ExportedSessionKey, context->RandomSessionKey,
+ sizeof(context->ExportedSessionKey));
+}
+
+/**
+ * Encrypt RandomSessionKey (RC4-encrypted RandomSessionKey, using KeyExchangeKey as the key).
+ * @param context A pointer to the NTLM context
+ */
+
+void ntlm_encrypt_random_session_key(NTLM_CONTEXT* context)
+{
+ /* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the
+ * KeyExchangeKey */
+ WINPR_ASSERT(context);
+ ntlm_rc4k(context->KeyExchangeKey, 16, context->RandomSessionKey,
+ context->EncryptedRandomSessionKey);
+}
+
+/**
+ * Decrypt RandomSessionKey (RC4-encrypted RandomSessionKey, using KeyExchangeKey as the key).
+ * @param context A pointer to the NTLM context
+ */
+
+void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+
+ /* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the
+ * KeyExchangeKey */
+
+ /**
+ * if (NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ * Set RandomSessionKey to RC4K(KeyExchangeKey,
+ * AUTHENTICATE_MESSAGE.EncryptedRandomSessionKey) else Set RandomSessionKey to KeyExchangeKey
+ */
+ if (context->NegotiateKeyExchange)
+ {
+ WINPR_ASSERT(sizeof(context->EncryptedRandomSessionKey) ==
+ sizeof(context->RandomSessionKey));
+ ntlm_rc4k(context->KeyExchangeKey, sizeof(context->EncryptedRandomSessionKey),
+ context->EncryptedRandomSessionKey, context->RandomSessionKey);
+ }
+ else
+ {
+ WINPR_ASSERT(sizeof(context->RandomSessionKey) == sizeof(context->KeyExchangeKey));
+ CopyMemory(context->RandomSessionKey, context->KeyExchangeKey,
+ sizeof(context->RandomSessionKey));
+ }
+}
+
+/**
+ * Generate signing key msdn{cc236711}
+ *
+ * @param exported_session_key ExportedSessionKey
+ * @param sign_magic Sign magic string
+ * @param signing_key Destination signing key
+ *
+ * @return \b TRUE for success, \b FALSE for failure
+ */
+
+static BOOL ntlm_generate_signing_key(BYTE* exported_session_key, const SecBuffer* sign_magic,
+ BYTE* signing_key)
+{
+ BOOL rc = FALSE;
+ size_t length = 0;
+ BYTE* value = NULL;
+
+ WINPR_ASSERT(exported_session_key);
+ WINPR_ASSERT(sign_magic);
+ WINPR_ASSERT(signing_key);
+
+ length = WINPR_MD5_DIGEST_LENGTH + sign_magic->cbBuffer;
+ value = (BYTE*)malloc(length);
+
+ if (!value)
+ goto out;
+
+ /* Concatenate ExportedSessionKey with sign magic */
+ CopyMemory(value, exported_session_key, WINPR_MD5_DIGEST_LENGTH);
+ CopyMemory(&value[WINPR_MD5_DIGEST_LENGTH], sign_magic->pvBuffer, sign_magic->cbBuffer);
+
+ rc = winpr_Digest(WINPR_MD_MD5, value, length, signing_key, WINPR_MD5_DIGEST_LENGTH);
+
+out:
+ free(value);
+ return rc;
+}
+
+/**
+ * Generate client signing key (ClientSigningKey). msdn{cc236711}
+ * @param context A pointer to the NTLM context
+ *
+ * @return \b TRUE for success, \b FALSE for failure
+ */
+
+BOOL ntlm_generate_client_signing_key(NTLM_CONTEXT* context)
+{
+ const SecBuffer signMagic = { sizeof(NTLM_CLIENT_SIGN_MAGIC), 0, NTLM_CLIENT_SIGN_MAGIC };
+
+ WINPR_ASSERT(context);
+ return ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic,
+ context->ClientSigningKey);
+}
+
+/**
+ * Generate server signing key (ServerSigningKey). msdn{cc236711}
+ * @param context A pointer to the NTLM context
+ *
+ * @return \b TRUE for success, \b FALSE for failure
+ */
+
+BOOL ntlm_generate_server_signing_key(NTLM_CONTEXT* context)
+{
+ const SecBuffer signMagic = { sizeof(NTLM_SERVER_SIGN_MAGIC), 0, NTLM_SERVER_SIGN_MAGIC };
+
+ WINPR_ASSERT(context);
+ return ntlm_generate_signing_key(context->ExportedSessionKey, &signMagic,
+ context->ServerSigningKey);
+}
+
+/**
+ * Generate client sealing key (ClientSealingKey). msdn{cc236712}
+ * @param context A pointer to the NTLM context
+ *
+ * @return \b TRUE for success, \b FALSE for failure
+ */
+
+BOOL ntlm_generate_client_sealing_key(NTLM_CONTEXT* context)
+{
+ const SecBuffer sealMagic = { sizeof(NTLM_CLIENT_SEAL_MAGIC), 0, NTLM_CLIENT_SEAL_MAGIC };
+
+ WINPR_ASSERT(context);
+ return ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic,
+ context->ClientSealingKey);
+}
+
+/**
+ * Generate server sealing key (ServerSealingKey). msdn{cc236712}
+ * @param context A pointer to the NTLM context
+ *
+ * @return \b TRUE for success, \b FALSE for failure
+ */
+
+BOOL ntlm_generate_server_sealing_key(NTLM_CONTEXT* context)
+{
+ const SecBuffer sealMagic = { sizeof(NTLM_SERVER_SEAL_MAGIC), 0, NTLM_SERVER_SEAL_MAGIC };
+
+ WINPR_ASSERT(context);
+ return ntlm_generate_signing_key(context->ExportedSessionKey, &sealMagic,
+ context->ServerSealingKey);
+}
+
+/**
+ * Initialize RC4 stream cipher states for sealing.
+ * @param context A pointer to the NTLM context
+ */
+
+BOOL ntlm_init_rc4_seal_states(NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+ if (context->server)
+ {
+ context->SendSigningKey = context->ServerSigningKey;
+ context->RecvSigningKey = context->ClientSigningKey;
+ context->SendSealingKey = context->ClientSealingKey;
+ context->RecvSealingKey = context->ServerSealingKey;
+ context->SendRc4Seal =
+ winpr_RC4_New(context->ServerSealingKey, sizeof(context->ServerSealingKey));
+ context->RecvRc4Seal =
+ winpr_RC4_New(context->ClientSealingKey, sizeof(context->ClientSealingKey));
+ }
+ else
+ {
+ context->SendSigningKey = context->ClientSigningKey;
+ context->RecvSigningKey = context->ServerSigningKey;
+ context->SendSealingKey = context->ServerSealingKey;
+ context->RecvSealingKey = context->ClientSealingKey;
+ context->SendRc4Seal =
+ winpr_RC4_New(context->ClientSealingKey, sizeof(context->ClientSealingKey));
+ context->RecvRc4Seal =
+ winpr_RC4_New(context->ServerSealingKey, sizeof(context->ServerSealingKey));
+ }
+ if (!context->SendRc4Seal)
+ {
+ WLog_ERR(TAG, "Failed to allocate context->SendRc4Seal");
+ return FALSE;
+ }
+ if (!context->RecvRc4Seal)
+ {
+ WLog_ERR(TAG, "Failed to allocate context->RecvRc4Seal");
+ return FALSE;
+ }
+ return TRUE;
+}
+
+BOOL ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT32 size)
+{
+ BOOL rc = FALSE;
+ /*
+ * Compute the HMAC-MD5 hash of ConcatenationOf(NEGOTIATE_MESSAGE,
+ * CHALLENGE_MESSAGE, AUTHENTICATE_MESSAGE) using the ExportedSessionKey
+ */
+ WINPR_HMAC_CTX* hmac = winpr_HMAC_New();
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(mic);
+ WINPR_ASSERT(size >= WINPR_MD5_DIGEST_LENGTH);
+
+ memset(mic, 0, size);
+ if (!hmac)
+ return FALSE;
+
+ if (winpr_HMAC_Init(hmac, WINPR_MD_MD5, context->ExportedSessionKey, WINPR_MD5_DIGEST_LENGTH))
+ {
+ winpr_HMAC_Update(hmac, (BYTE*)context->NegotiateMessage.pvBuffer,
+ context->NegotiateMessage.cbBuffer);
+ winpr_HMAC_Update(hmac, (BYTE*)context->ChallengeMessage.pvBuffer,
+ context->ChallengeMessage.cbBuffer);
+
+ if (context->MessageIntegrityCheckOffset > 0)
+ {
+ const BYTE* auth = (BYTE*)context->AuthenticateMessage.pvBuffer;
+ const BYTE data[WINPR_MD5_DIGEST_LENGTH] = { 0 };
+ const size_t rest = context->MessageIntegrityCheckOffset + sizeof(data);
+
+ WINPR_ASSERT(rest <= context->AuthenticateMessage.cbBuffer);
+ winpr_HMAC_Update(hmac, &auth[0], context->MessageIntegrityCheckOffset);
+ winpr_HMAC_Update(hmac, data, sizeof(data));
+ winpr_HMAC_Update(hmac, &auth[rest], context->AuthenticateMessage.cbBuffer - rest);
+ }
+ else
+ {
+ winpr_HMAC_Update(hmac, (BYTE*)context->AuthenticateMessage.pvBuffer,
+ context->AuthenticateMessage.cbBuffer);
+ }
+ winpr_HMAC_Final(hmac, mic, WINPR_MD5_DIGEST_LENGTH);
+ rc = TRUE;
+ }
+
+ winpr_HMAC_Free(hmac);
+ return rc;
+}
diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_compute.h b/winpr/libwinpr/sspi/NTLM/ntlm_compute.h
new file mode 100644
index 0000000..006a449
--- /dev/null
+++ b/winpr/libwinpr/sspi/NTLM/ntlm_compute.h
@@ -0,0 +1,64 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * NTLM Security Package (Compute)
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WINPR_SSPI_NTLM_COMPUTE_H
+#define WINPR_SSPI_NTLM_COMPUTE_H
+
+#include "ntlm.h"
+
+#include "ntlm_av_pairs.h"
+
+BOOL ntlm_get_version_info(NTLM_VERSION_INFO* versionInfo);
+BOOL ntlm_read_version_info(wStream* s, NTLM_VERSION_INFO* versionInfo);
+BOOL ntlm_write_version_info(wStream* s, const NTLM_VERSION_INFO* versionInfo);
+
+#ifdef WITH_DEBUG_NTLM
+void ntlm_print_version_info(const NTLM_VERSION_INFO* versionInfo);
+#endif
+
+BOOL ntlm_read_ntlm_v2_response(wStream* s, NTLMv2_RESPONSE* response);
+BOOL ntlm_write_ntlm_v2_response(wStream* s, const NTLMv2_RESPONSE* response);
+
+void ntlm_output_target_name(NTLM_CONTEXT* context);
+void ntlm_output_channel_bindings(NTLM_CONTEXT* context);
+
+void ntlm_current_time(BYTE* timestamp);
+void ntlm_generate_timestamp(NTLM_CONTEXT* context);
+
+BOOL ntlm_compute_lm_v2_response(NTLM_CONTEXT* context);
+BOOL ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context);
+
+void ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext);
+void ntlm_generate_client_challenge(NTLM_CONTEXT* context);
+void ntlm_generate_server_challenge(NTLM_CONTEXT* context);
+void ntlm_generate_key_exchange_key(NTLM_CONTEXT* context);
+void ntlm_generate_random_session_key(NTLM_CONTEXT* context);
+void ntlm_generate_exported_session_key(NTLM_CONTEXT* context);
+void ntlm_encrypt_random_session_key(NTLM_CONTEXT* context);
+void ntlm_decrypt_random_session_key(NTLM_CONTEXT* context);
+
+BOOL ntlm_generate_client_signing_key(NTLM_CONTEXT* context);
+BOOL ntlm_generate_server_signing_key(NTLM_CONTEXT* context);
+BOOL ntlm_generate_client_sealing_key(NTLM_CONTEXT* context);
+BOOL ntlm_generate_server_sealing_key(NTLM_CONTEXT* context);
+BOOL ntlm_init_rc4_seal_states(NTLM_CONTEXT* context);
+
+BOOL ntlm_compute_message_integrity_check(NTLM_CONTEXT* context, BYTE* mic, UINT32 size);
+
+#endif /* WINPR_AUTH_NTLM_COMPUTE_H */
diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_export.h b/winpr/libwinpr/sspi/NTLM/ntlm_export.h
new file mode 100644
index 0000000..5249be2
--- /dev/null
+++ b/winpr/libwinpr/sspi/NTLM/ntlm_export.h
@@ -0,0 +1,40 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * NTLM Security Package
+ *
+ * Copyright 2021 Armin Novak <armin.novak@thincast.com>
+ * Copyright 2021 Thincast Technologies GmbH
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WINPR_SSPI_NTLM_EXPORT_H
+#define WINPR_SSPI_NTLM_EXPORT_H
+
+#include <winpr/sspi.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+ extern const SecPkgInfoA NTLM_SecPkgInfoA;
+ extern const SecPkgInfoW NTLM_SecPkgInfoW;
+ extern const SecurityFunctionTableA NTLM_SecurityFunctionTableA;
+ extern const SecurityFunctionTableW NTLM_SecurityFunctionTableW;
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_message.c b/winpr/libwinpr/sspi/NTLM/ntlm_message.c
new file mode 100644
index 0000000..511ca47
--- /dev/null
+++ b/winpr/libwinpr/sspi/NTLM/ntlm_message.c
@@ -0,0 +1,1397 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * NTLM Security Package (Message)
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include <winpr/config.h>
+
+#include "ntlm.h"
+#include "../sspi.h"
+
+#include <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/print.h>
+#include <winpr/stream.h>
+#include <winpr/sysinfo.h>
+
+#include "ntlm_compute.h"
+
+#include "ntlm_message.h"
+
+#include "../../log.h"
+#define TAG WINPR_TAG("sspi.NTLM")
+
+#define NTLM_CheckAndLogRequiredCapacity(tag, s, nmemb, what) \
+ Stream_CheckAndLogRequiredCapacityEx(tag, WLOG_WARN, s, nmemb, 1, "%s(%s:%" PRIuz ") " what, \
+ __func__, __FILE__, (size_t)__LINE__)
+
+static const char NTLM_SIGNATURE[8] = { 'N', 'T', 'L', 'M', 'S', 'S', 'P', '\0' };
+
+static void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields);
+
+const char* ntlm_get_negotiate_string(UINT32 flag)
+{
+ if (flag & NTLMSSP_NEGOTIATE_56)
+ return "NTLMSSP_NEGOTIATE_56";
+ if (flag & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ return "NTLMSSP_NEGOTIATE_KEY_EXCH";
+ if (flag & NTLMSSP_NEGOTIATE_128)
+ return "NTLMSSP_NEGOTIATE_128";
+ if (flag & NTLMSSP_RESERVED1)
+ return "NTLMSSP_RESERVED1";
+ if (flag & NTLMSSP_RESERVED2)
+ return "NTLMSSP_RESERVED2";
+ if (flag & NTLMSSP_RESERVED3)
+ return "NTLMSSP_RESERVED3";
+ if (flag & NTLMSSP_NEGOTIATE_VERSION)
+ return "NTLMSSP_NEGOTIATE_VERSION";
+ if (flag & NTLMSSP_RESERVED4)
+ return "NTLMSSP_RESERVED4";
+ if (flag & NTLMSSP_NEGOTIATE_TARGET_INFO)
+ return "NTLMSSP_NEGOTIATE_TARGET_INFO";
+ if (flag & NTLMSSP_REQUEST_NON_NT_SESSION_KEY)
+ return "NTLMSSP_REQUEST_NON_NT_SESSION_KEY";
+ if (flag & NTLMSSP_RESERVED5)
+ return "NTLMSSP_RESERVED5";
+ if (flag & NTLMSSP_NEGOTIATE_IDENTIFY)
+ return "NTLMSSP_NEGOTIATE_IDENTIFY";
+ if (flag & NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY)
+ return "NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY";
+ if (flag & NTLMSSP_RESERVED6)
+ return "NTLMSSP_RESERVED6";
+ if (flag & NTLMSSP_TARGET_TYPE_SERVER)
+ return "NTLMSSP_TARGET_TYPE_SERVER";
+ if (flag & NTLMSSP_TARGET_TYPE_DOMAIN)
+ return "NTLMSSP_TARGET_TYPE_DOMAIN";
+ if (flag & NTLMSSP_NEGOTIATE_ALWAYS_SIGN)
+ return "NTLMSSP_NEGOTIATE_ALWAYS_SIGN";
+ if (flag & NTLMSSP_RESERVED7)
+ return "NTLMSSP_RESERVED7";
+ if (flag & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ return "NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED";
+ if (flag & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
+ return "NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED";
+ if (flag & NTLMSSP_NEGOTIATE_ANONYMOUS)
+ return "NTLMSSP_NEGOTIATE_ANONYMOUS";
+ if (flag & NTLMSSP_RESERVED8)
+ return "NTLMSSP_RESERVED8";
+ if (flag & NTLMSSP_NEGOTIATE_NTLM)
+ return "NTLMSSP_NEGOTIATE_NTLM";
+ if (flag & NTLMSSP_RESERVED9)
+ return "NTLMSSP_RESERVED9";
+ if (flag & NTLMSSP_NEGOTIATE_LM_KEY)
+ return "NTLMSSP_NEGOTIATE_LM_KEY";
+ if (flag & NTLMSSP_NEGOTIATE_DATAGRAM)
+ return "NTLMSSP_NEGOTIATE_DATAGRAM";
+ if (flag & NTLMSSP_NEGOTIATE_SEAL)
+ return "NTLMSSP_NEGOTIATE_SEAL";
+ if (flag & NTLMSSP_NEGOTIATE_SIGN)
+ return "NTLMSSP_NEGOTIATE_SIGN";
+ if (flag & NTLMSSP_RESERVED10)
+ return "NTLMSSP_RESERVED10";
+ if (flag & NTLMSSP_REQUEST_TARGET)
+ return "NTLMSSP_REQUEST_TARGET";
+ if (flag & NTLMSSP_NEGOTIATE_OEM)
+ return "NTLMSSP_NEGOTIATE_OEM";
+ if (flag & NTLMSSP_NEGOTIATE_UNICODE)
+ return "NTLMSSP_NEGOTIATE_UNICODE";
+ return "NTLMSSP_NEGOTIATE_UNKNOWN";
+}
+
+#if defined(WITH_DEBUG_NTLM)
+static void ntlm_print_message_fields(const NTLM_MESSAGE_FIELDS* fields, const char* name)
+{
+ WINPR_ASSERT(fields);
+ WINPR_ASSERT(name);
+
+ WLog_VRB(TAG, "%s (Len: %" PRIu16 " MaxLen: %" PRIu16 " BufferOffset: %" PRIu32 ")", name,
+ fields->Len, fields->MaxLen, fields->BufferOffset);
+
+ if (fields->Len > 0)
+ winpr_HexDump(TAG, WLOG_TRACE, fields->Buffer, fields->Len);
+}
+
+static void ntlm_print_negotiate_flags(UINT32 flags)
+{
+ WLog_VRB(TAG, "negotiateFlags \"0x%08" PRIX32 "\"", flags);
+
+ for (int i = 31; i >= 0; i--)
+ {
+ if ((flags >> i) & 1)
+ {
+ const char* str = ntlm_get_negotiate_string(1 << i);
+ WLog_VRB(TAG, "\t%s (%d),", str, (31 - i));
+ }
+ }
+}
+
+static void ntlm_print_negotiate_message(const SecBuffer* NegotiateMessage,
+ const NTLM_NEGOTIATE_MESSAGE* message)
+{
+ WINPR_ASSERT(NegotiateMessage);
+ WINPR_ASSERT(message);
+
+ WLog_VRB(TAG, "NEGOTIATE_MESSAGE (length = %" PRIu32 ")", NegotiateMessage->cbBuffer);
+ winpr_HexDump(TAG, WLOG_TRACE, NegotiateMessage->pvBuffer, NegotiateMessage->cbBuffer);
+ ntlm_print_negotiate_flags(message->NegotiateFlags);
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ ntlm_print_version_info(&(message->Version));
+}
+
+static void ntlm_print_challenge_message(const SecBuffer* ChallengeMessage,
+ const NTLM_CHALLENGE_MESSAGE* message,
+ const SecBuffer* ChallengeTargetInfo)
+{
+ WINPR_ASSERT(ChallengeMessage);
+ WINPR_ASSERT(message);
+
+ WLog_VRB(TAG, "CHALLENGE_MESSAGE (length = %" PRIu32 ")", ChallengeMessage->cbBuffer);
+ winpr_HexDump(TAG, WLOG_TRACE, ChallengeMessage->pvBuffer, ChallengeMessage->cbBuffer);
+ ntlm_print_negotiate_flags(message->NegotiateFlags);
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ ntlm_print_version_info(&(message->Version));
+
+ ntlm_print_message_fields(&(message->TargetName), "TargetName");
+ ntlm_print_message_fields(&(message->TargetInfo), "TargetInfo");
+
+ if (ChallengeTargetInfo && (ChallengeTargetInfo->cbBuffer > 0))
+ {
+ WLog_VRB(TAG, "ChallengeTargetInfo (%" PRIu32 "):", ChallengeTargetInfo->cbBuffer);
+ ntlm_print_av_pair_list(ChallengeTargetInfo->pvBuffer, ChallengeTargetInfo->cbBuffer);
+ }
+}
+
+static void ntlm_print_authenticate_message(const SecBuffer* AuthenticateMessage,
+ const NTLM_AUTHENTICATE_MESSAGE* message, UINT32 flags,
+ const SecBuffer* AuthenticateTargetInfo)
+{
+ WINPR_ASSERT(AuthenticateMessage);
+ WINPR_ASSERT(message);
+
+ WLog_VRB(TAG, "AUTHENTICATE_MESSAGE (length = %" PRIu32 ")", AuthenticateMessage->cbBuffer);
+ winpr_HexDump(TAG, WLOG_TRACE, AuthenticateMessage->pvBuffer, AuthenticateMessage->cbBuffer);
+ ntlm_print_negotiate_flags(message->NegotiateFlags);
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ ntlm_print_version_info(&(message->Version));
+
+ if (AuthenticateTargetInfo && (AuthenticateTargetInfo->cbBuffer > 0))
+ {
+ WLog_VRB(TAG, "AuthenticateTargetInfo (%" PRIu32 "):", AuthenticateTargetInfo->cbBuffer);
+ ntlm_print_av_pair_list(AuthenticateTargetInfo->pvBuffer, AuthenticateTargetInfo->cbBuffer);
+ }
+
+ ntlm_print_message_fields(&(message->DomainName), "DomainName");
+ ntlm_print_message_fields(&(message->UserName), "UserName");
+ ntlm_print_message_fields(&(message->Workstation), "Workstation");
+ ntlm_print_message_fields(&(message->LmChallengeResponse), "LmChallengeResponse");
+ ntlm_print_message_fields(&(message->NtChallengeResponse), "NtChallengeResponse");
+ ntlm_print_message_fields(&(message->EncryptedRandomSessionKey), "EncryptedRandomSessionKey");
+
+ if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
+ {
+ WLog_VRB(TAG, "MessageIntegrityCheck (length = 16)");
+ winpr_HexDump(TAG, WLOG_TRACE, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck));
+ }
+}
+
+static void ntlm_print_authentication_complete(const NTLM_CONTEXT* context)
+{
+ WINPR_ASSERT(context);
+
+ WLog_VRB(TAG, "ClientChallenge");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ClientChallenge, 8);
+ WLog_VRB(TAG, "ServerChallenge");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ServerChallenge, 8);
+ WLog_VRB(TAG, "SessionBaseKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->SessionBaseKey, 16);
+ WLog_VRB(TAG, "KeyExchangeKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->KeyExchangeKey, 16);
+ WLog_VRB(TAG, "ExportedSessionKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ExportedSessionKey, 16);
+ WLog_VRB(TAG, "RandomSessionKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->RandomSessionKey, 16);
+ WLog_VRB(TAG, "ClientSigningKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ClientSigningKey, 16);
+ WLog_VRB(TAG, "ClientSealingKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ClientSealingKey, 16);
+ WLog_VRB(TAG, "ServerSigningKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ServerSigningKey, 16);
+ WLog_VRB(TAG, "ServerSealingKey");
+ winpr_HexDump(TAG, WLOG_TRACE, context->ServerSealingKey, 16);
+ WLog_VRB(TAG, "Timestamp");
+ winpr_HexDump(TAG, WLOG_TRACE, context->Timestamp, 8);
+}
+#endif
+
+static BOOL ntlm_read_message_header(wStream* s, NTLM_MESSAGE_HEADER* header, UINT32 expected)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(header);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 12))
+ return FALSE;
+
+ Stream_Read(s, header->Signature, 8);
+ Stream_Read_UINT32(s, header->MessageType);
+
+ if (strncmp((char*)header->Signature, NTLM_SIGNATURE, 8) != 0)
+ {
+ WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid signature, got %s, expected %s",
+ header->Signature, NTLM_SIGNATURE);
+ return FALSE;
+ }
+
+ if (header->MessageType != expected)
+ {
+ WLog_ERR(TAG, "NTLM_MESSAGE_HEADER Invalid message tyep, got %s, expected %s",
+ ntlm_message_type_string(header->MessageType), ntlm_message_type_string(expected));
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+static BOOL ntlm_write_message_header(wStream* s, const NTLM_MESSAGE_HEADER* header)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(header);
+
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, sizeof(NTLM_SIGNATURE) + 4ull,
+ "NTLM_MESSAGE_HEADER::header"))
+ return FALSE;
+
+ Stream_Write(s, header->Signature, sizeof(NTLM_SIGNATURE));
+ Stream_Write_UINT32(s, header->MessageType);
+
+ return TRUE;
+}
+
+static BOOL ntlm_populate_message_header(NTLM_MESSAGE_HEADER* header, UINT32 MessageType)
+{
+ WINPR_ASSERT(header);
+
+ CopyMemory(header->Signature, NTLM_SIGNATURE, sizeof(NTLM_SIGNATURE));
+ header->MessageType = MessageType;
+ return TRUE;
+}
+
+static BOOL ntlm_read_message_fields(wStream* s, NTLM_MESSAGE_FIELDS* fields)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(fields);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 8))
+ return FALSE;
+
+ ntlm_free_message_fields_buffer(fields);
+
+ Stream_Read_UINT16(s, fields->Len); /* Len (2 bytes) */
+ Stream_Read_UINT16(s, fields->MaxLen); /* MaxLen (2 bytes) */
+ Stream_Read_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
+ return TRUE;
+}
+
+static BOOL ntlm_write_message_fields(wStream* s, const NTLM_MESSAGE_FIELDS* fields)
+{
+ UINT16 MaxLen = 0;
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(fields);
+
+ MaxLen = fields->MaxLen;
+ if (fields->MaxLen < 1)
+ MaxLen = fields->Len;
+
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), 8, "NTLM_MESSAGE_FIELDS::header"))
+ return FALSE;
+
+ Stream_Write_UINT16(s, fields->Len); /* Len (2 bytes) */
+ Stream_Write_UINT16(s, MaxLen); /* MaxLen (2 bytes) */
+ Stream_Write_UINT32(s, fields->BufferOffset); /* BufferOffset (4 bytes) */
+ return TRUE;
+}
+
+static BOOL ntlm_read_message_fields_buffer(wStream* s, NTLM_MESSAGE_FIELDS* fields)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(fields);
+
+ if (fields->Len > 0)
+ {
+ const UINT32 offset = fields->BufferOffset + fields->Len;
+
+ if (fields->BufferOffset > UINT32_MAX - fields->Len)
+ {
+ WLog_ERR(TAG,
+ "NTLM_MESSAGE_FIELDS::BufferOffset %" PRIu32
+ " too large, maximum allowed is %" PRIu32,
+ fields->BufferOffset, UINT32_MAX - fields->Len);
+ return FALSE;
+ }
+
+ if (offset > Stream_Length(s))
+ {
+ WLog_ERR(TAG,
+ "NTLM_MESSAGE_FIELDS::Buffer offset %" PRIu32 " beyond received data %" PRIuz,
+ offset, Stream_Length(s));
+ return FALSE;
+ }
+
+ fields->Buffer = (PBYTE)malloc(fields->Len);
+
+ if (!fields->Buffer)
+ {
+ WLog_ERR(TAG, "NTLM_MESSAGE_FIELDS::Buffer allocation of %" PRIu16 "bytes failed",
+ fields->Len);
+ return FALSE;
+ }
+
+ Stream_SetPosition(s, fields->BufferOffset);
+ Stream_Read(s, fields->Buffer, fields->Len);
+ }
+
+ return TRUE;
+}
+
+static BOOL ntlm_write_message_fields_buffer(wStream* s, const NTLM_MESSAGE_FIELDS* fields)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(fields);
+
+ if (fields->Len > 0)
+ {
+ Stream_SetPosition(s, fields->BufferOffset);
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, (s), fields->Len, "NTLM_MESSAGE_FIELDS::Len"))
+ return FALSE;
+
+ Stream_Write(s, fields->Buffer, fields->Len);
+ }
+ return TRUE;
+}
+
+void ntlm_free_message_fields_buffer(NTLM_MESSAGE_FIELDS* fields)
+{
+ if (fields)
+ {
+ if (fields->Buffer)
+ {
+ free(fields->Buffer);
+ fields->Len = 0;
+ fields->MaxLen = 0;
+ fields->Buffer = NULL;
+ fields->BufferOffset = 0;
+ }
+ }
+}
+
+static BOOL ntlm_read_negotiate_flags(wStream* s, UINT32* flags, UINT32 required, const char* name)
+{
+ UINT32 NegotiateFlags = 0;
+ char buffer[1024] = { 0 };
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(flags);
+ WINPR_ASSERT(name);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 4))
+ return FALSE;
+
+ Stream_Read_UINT32(s, NegotiateFlags); /* NegotiateFlags (4 bytes) */
+
+ if ((NegotiateFlags & required) != required)
+ {
+ WLog_ERR(TAG, "%s::NegotiateFlags invalid flags 0x08%" PRIx32 ", 0x%08" PRIx32 " required",
+ name, NegotiateFlags, required);
+ return FALSE;
+ }
+
+ WLog_DBG(TAG, "Read flags %s",
+ ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), NegotiateFlags));
+ *flags = NegotiateFlags;
+ return TRUE;
+}
+
+static BOOL ntlm_write_negotiate_flags(wStream* s, UINT32 flags, const char* name)
+{
+ char buffer[1024] = { 0 };
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(name);
+
+ if (!Stream_CheckAndLogRequiredCapacityEx(TAG, WLOG_WARN, s, 4ull, 1ull,
+ "%s(%s:%" PRIuz ") %s::NegotiateFlags", __func__,
+ __FILE__, (size_t)__LINE__, name))
+ return FALSE;
+
+ WLog_DBG(TAG, "Write flags %s", ntlm_negotiate_flags_string(buffer, ARRAYSIZE(buffer), flags));
+ Stream_Write_UINT32(s, flags); /* NegotiateFlags (4 bytes) */
+ return TRUE;
+}
+
+static BOOL ntlm_read_message_integrity_check(wStream* s, size_t* offset, BYTE* data, size_t size,
+ const char* name)
+{
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(offset);
+ WINPR_ASSERT(data);
+ WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH);
+ WINPR_ASSERT(name);
+
+ *offset = Stream_GetPosition(s);
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, size))
+ return FALSE;
+
+ Stream_Read(s, data, size);
+ return TRUE;
+}
+
+static BOOL ntlm_write_message_integrity_check(wStream* s, size_t offset, const BYTE* data,
+ size_t size, const char* name)
+{
+ size_t pos = 0;
+
+ WINPR_ASSERT(s);
+ WINPR_ASSERT(data);
+ WINPR_ASSERT(size == WINPR_MD5_DIGEST_LENGTH);
+ WINPR_ASSERT(name);
+
+ pos = Stream_GetPosition(s);
+
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, offset, "MessageIntegrityCheck::offset"))
+ return FALSE;
+
+ Stream_SetPosition(s, offset);
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, size, "MessageIntegrityCheck::size"))
+ return FALSE;
+
+ Stream_Write(s, data, size);
+ Stream_SetPosition(s, pos);
+ return TRUE;
+}
+
+SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
+{
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ const NTLM_NEGOTIATE_MESSAGE empty = { 0 };
+ NTLM_NEGOTIATE_MESSAGE* message = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(buffer);
+
+ message = &context->NEGOTIATE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_NEGOTIATE))
+ return SEC_E_INVALID_TOKEN;
+
+ if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags,
+ NTLMSSP_REQUEST_TARGET | NTLMSSP_NEGOTIATE_NTLM |
+ NTLMSSP_NEGOTIATE_UNICODE,
+ "NTLM_NEGOTIATE_MESSAGE"))
+ return SEC_E_INVALID_TOKEN;
+
+ context->NegotiateFlags = message->NegotiateFlags;
+
+ /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
+ // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
+ {
+ if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
+ return SEC_E_INVALID_TOKEN;
+ }
+
+ /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */
+ // if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ {
+ if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
+ return SEC_E_INVALID_TOKEN;
+ }
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
+ return SEC_E_INVALID_TOKEN;
+ }
+
+ if (!ntlm_read_message_fields_buffer(s, &message->DomainName))
+ return SEC_E_INVALID_TOKEN;
+
+ if (!ntlm_read_message_fields_buffer(s, &message->Workstation))
+ return SEC_E_INVALID_TOKEN;
+
+ length = Stream_GetPosition(s);
+ WINPR_ASSERT(length <= ULONG_MAX);
+ buffer->cbBuffer = (ULONG)length;
+
+ if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length))
+ return SEC_E_INTERNAL_ERROR;
+
+ CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer);
+ context->NegotiateMessage.BufferType = buffer->BufferType;
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_negotiate_message(&context->NegotiateMessage, message);
+#endif
+ ntlm_change_state(context, NTLM_STATE_CHALLENGE);
+ return SEC_I_CONTINUE_NEEDED;
+}
+
+SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer)
+{
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ const NTLM_NEGOTIATE_MESSAGE empty = { 0 };
+ NTLM_NEGOTIATE_MESSAGE* message = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(buffer);
+
+ message = &context->NEGOTIATE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_NEGOTIATE))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (context->NTLMv2)
+ {
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_LM_KEY;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_OEM;
+ }
+
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
+ message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
+
+ if (context->confidentiality)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
+
+ if (context->SendVersionInfo)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ ntlm_get_version_info(&(message->Version));
+
+ context->NegotiateFlags = message->NegotiateFlags;
+ /* Message Header (12 bytes) */
+ if (!ntlm_write_message_header(s, &message->header))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_NEGOTIATE_MESSAGE"))
+ return SEC_E_INTERNAL_ERROR;
+
+ /* only set if NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED is set */
+ /* DomainNameFields (8 bytes) */
+ if (!ntlm_write_message_fields(s, &(message->DomainName)))
+ return SEC_E_INTERNAL_ERROR;
+
+ /* only set if NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED is set */
+ /* WorkstationFields (8 bytes) */
+ if (!ntlm_write_message_fields(s, &(message->Workstation)))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_write_version_info(s, &(message->Version)))
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ length = Stream_GetPosition(s);
+ WINPR_ASSERT(length <= ULONG_MAX);
+ buffer->cbBuffer = (ULONG)length;
+
+ if (!sspi_SecBufferAlloc(&context->NegotiateMessage, (ULONG)length))
+ return SEC_E_INTERNAL_ERROR;
+
+ CopyMemory(context->NegotiateMessage.pvBuffer, buffer->pvBuffer, buffer->cbBuffer);
+ context->NegotiateMessage.BufferType = buffer->BufferType;
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_negotiate_message(&context->NegotiateMessage, message);
+#endif
+ ntlm_change_state(context, NTLM_STATE_CHALLENGE);
+ return SEC_I_CONTINUE_NEEDED;
+}
+
+SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
+{
+ SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ size_t StartOffset = 0;
+ size_t PayloadOffset = 0;
+ NTLM_AV_PAIR* AvTimestamp = NULL;
+ const NTLM_CHALLENGE_MESSAGE empty = { 0 };
+ NTLM_CHALLENGE_MESSAGE* message = NULL;
+
+ if (!context || !buffer)
+ return SEC_E_INTERNAL_ERROR;
+
+ ntlm_generate_client_challenge(context);
+ message = &context->CHALLENGE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ StartOffset = Stream_GetPosition(s);
+
+ if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_CHALLENGE))
+ goto fail;
+
+ if (!ntlm_read_message_fields(s, &(message->TargetName))) /* TargetNameFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_CHALLENGE_MESSAGE"))
+ goto fail;
+
+ context->NegotiateFlags = message->NegotiateFlags;
+
+ if (!Stream_CheckAndLogRequiredLength(TAG, s, 16))
+ goto fail;
+
+ Stream_Read(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
+ CopyMemory(context->ServerChallenge, message->ServerChallenge, 8);
+ Stream_Read(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */
+
+ if (!ntlm_read_message_fields(s, &(message->TargetInfo))) /* TargetInfoFields (8 bytes) */
+ goto fail;
+
+ if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
+ goto fail;
+ }
+
+ /* Payload (variable) */
+ PayloadOffset = Stream_GetPosition(s);
+
+ status = SEC_E_INTERNAL_ERROR;
+ if (message->TargetName.Len > 0)
+ {
+ if (!ntlm_read_message_fields_buffer(s, &(message->TargetName)))
+ goto fail;
+ }
+
+ if (message->TargetInfo.Len > 0)
+ {
+ size_t cbAvTimestamp = 0;
+
+ if (!ntlm_read_message_fields_buffer(s, &(message->TargetInfo)))
+ goto fail;
+
+ context->ChallengeTargetInfo.pvBuffer = message->TargetInfo.Buffer;
+ context->ChallengeTargetInfo.cbBuffer = message->TargetInfo.Len;
+ AvTimestamp = ntlm_av_pair_get((NTLM_AV_PAIR*)message->TargetInfo.Buffer,
+ message->TargetInfo.Len, MsvAvTimestamp, &cbAvTimestamp);
+
+ if (AvTimestamp)
+ {
+ PBYTE ptr = ntlm_av_pair_get_value_pointer(AvTimestamp);
+
+ if (!ptr)
+ goto fail;
+
+ if (context->NTLMv2)
+ context->UseMIC = TRUE;
+
+ CopyMemory(context->ChallengeTimestamp, ptr, 8);
+ }
+ }
+
+ length = (PayloadOffset - StartOffset) + message->TargetName.Len + message->TargetInfo.Len;
+ if (length > buffer->cbBuffer)
+ goto fail;
+
+ if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length))
+ goto fail;
+
+ if (context->ChallengeMessage.pvBuffer)
+ CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s) + StartOffset, length);
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_challenge_message(&context->ChallengeMessage, message, NULL);
+#endif
+ /* AV_PAIRs */
+
+ if (context->NTLMv2)
+ {
+ if (!ntlm_construct_authenticate_target_info(context))
+ goto fail;
+
+ sspi_SecBufferFree(&context->ChallengeTargetInfo);
+ context->ChallengeTargetInfo.pvBuffer = context->AuthenticateTargetInfo.pvBuffer;
+ context->ChallengeTargetInfo.cbBuffer = context->AuthenticateTargetInfo.cbBuffer;
+ }
+
+ ntlm_generate_timestamp(context); /* Timestamp */
+
+ if (!ntlm_compute_lm_v2_response(context)) /* LmChallengeResponse */
+ goto fail;
+
+ if (!ntlm_compute_ntlm_v2_response(context)) /* NtChallengeResponse */
+ goto fail;
+
+ ntlm_generate_key_exchange_key(context); /* KeyExchangeKey */
+ ntlm_generate_random_session_key(context); /* RandomSessionKey */
+ ntlm_generate_exported_session_key(context); /* ExportedSessionKey */
+ ntlm_encrypt_random_session_key(context); /* EncryptedRandomSessionKey */
+ /* Generate signing keys */
+ if (!ntlm_generate_client_signing_key(context))
+ goto fail;
+ if (!ntlm_generate_server_signing_key(context))
+ goto fail;
+ /* Generate sealing keys */
+ if (!ntlm_generate_client_sealing_key(context))
+ goto fail;
+ if (!ntlm_generate_server_sealing_key(context))
+ goto fail;
+ /* Initialize RC4 seal state using client sealing key */
+ if (!ntlm_init_rc4_seal_states(context))
+ goto fail;
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_authentication_complete(context);
+#endif
+ ntlm_change_state(context, NTLM_STATE_AUTHENTICATE);
+ status = SEC_I_CONTINUE_NEEDED;
+fail:
+ ntlm_free_message_fields_buffer(&(message->TargetName));
+ return status;
+}
+
+SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, const PSecBuffer buffer)
+{
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ UINT32 PayloadOffset = 0;
+ const NTLM_CHALLENGE_MESSAGE empty = { 0 };
+ NTLM_CHALLENGE_MESSAGE* message = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(buffer);
+
+ message = &context->CHALLENGE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ ntlm_get_version_info(&(message->Version)); /* Version */
+ ntlm_generate_server_challenge(context); /* Server Challenge */
+ ntlm_generate_timestamp(context); /* Timestamp */
+
+ if (!ntlm_construct_challenge_target_info(context)) /* TargetInfo */
+ return SEC_E_INTERNAL_ERROR;
+
+ CopyMemory(message->ServerChallenge, context->ServerChallenge, 8); /* ServerChallenge */
+ message->NegotiateFlags = context->NegotiateFlags;
+ if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_CHALLENGE))
+ return SEC_E_INTERNAL_ERROR;
+
+ /* Message Header (12 bytes) */
+ if (!ntlm_write_message_header(s, &message->header))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
+ {
+ message->TargetName.Len = (UINT16)context->TargetName.cbBuffer;
+ message->TargetName.Buffer = (PBYTE)context->TargetName.pvBuffer;
+ }
+
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
+ {
+ message->TargetInfo.Len = (UINT16)context->ChallengeTargetInfo.cbBuffer;
+ message->TargetInfo.Buffer = (PBYTE)context->ChallengeTargetInfo.pvBuffer;
+ }
+
+ PayloadOffset = 48;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ PayloadOffset += 8;
+
+ message->TargetName.BufferOffset = PayloadOffset;
+ message->TargetInfo.BufferOffset = message->TargetName.BufferOffset + message->TargetName.Len;
+ /* TargetNameFields (8 bytes) */
+ if (!ntlm_write_message_fields(s, &(message->TargetName)))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_CHALLENGE_MESSAGE"))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!NTLM_CheckAndLogRequiredCapacity(TAG, s, 16, "NTLM_CHALLENGE_MESSAGE::ServerChallenge"))
+ return SEC_E_INTERNAL_ERROR;
+
+ Stream_Write(s, message->ServerChallenge, 8); /* ServerChallenge (8 bytes) */
+ Stream_Write(s, message->Reserved, 8); /* Reserved (8 bytes), should be ignored */
+
+ /* TargetInfoFields (8 bytes) */
+ if (!ntlm_write_message_fields(s, &(message->TargetInfo)))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ /* Payload (variable) */
+ if (message->NegotiateFlags & NTLMSSP_REQUEST_TARGET)
+ {
+ if (!ntlm_write_message_fields_buffer(s, &(message->TargetName)))
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_TARGET_INFO)
+ {
+ if (!ntlm_write_message_fields_buffer(s, &(message->TargetInfo)))
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ length = Stream_GetPosition(s);
+ WINPR_ASSERT(length <= ULONG_MAX);
+ buffer->cbBuffer = (ULONG)length;
+
+ if (!sspi_SecBufferAlloc(&context->ChallengeMessage, (ULONG)length))
+ return SEC_E_INTERNAL_ERROR;
+
+ CopyMemory(context->ChallengeMessage.pvBuffer, Stream_Buffer(s), length);
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_challenge_message(&context->ChallengeMessage, message,
+ &context->ChallengeTargetInfo);
+#endif
+ ntlm_change_state(context, NTLM_STATE_AUTHENTICATE);
+ return SEC_I_CONTINUE_NEEDED;
+}
+
+SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer)
+{
+ SECURITY_STATUS status = SEC_E_INVALID_TOKEN;
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ UINT32 flags = 0;
+ NTLM_AV_PAIR* AvFlags = NULL;
+ size_t PayloadBufferOffset = 0;
+ const NTLM_AUTHENTICATE_MESSAGE empty = { 0 };
+ NTLM_AUTHENTICATE_MESSAGE* message = NULL;
+ SSPI_CREDENTIALS* credentials = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(buffer);
+
+ credentials = context->credentials;
+ WINPR_ASSERT(credentials);
+
+ message = &context->AUTHENTICATE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticConstInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ if (!ntlm_read_message_header(s, &message->header, MESSAGE_TYPE_AUTHENTICATE))
+ goto fail;
+
+ if (!ntlm_read_message_fields(
+ s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_message_fields(
+ s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_message_fields(
+ s,
+ &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */
+ goto fail;
+
+ if (!ntlm_read_negotiate_flags(s, &message->NegotiateFlags, 0, "NTLM_AUTHENTICATE_MESSAGE"))
+ goto fail;
+
+ context->NegotiateKeyExchange =
+ (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH) ? TRUE : FALSE;
+
+ if ((context->NegotiateKeyExchange && !message->EncryptedRandomSessionKey.Len) ||
+ (!context->NegotiateKeyExchange && message->EncryptedRandomSessionKey.Len))
+ goto fail;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_read_version_info(s, &(message->Version))) /* Version (8 bytes) */
+ goto fail;
+ }
+
+ PayloadBufferOffset = Stream_GetPosition(s);
+
+ status = SEC_E_INTERNAL_ERROR;
+ if (!ntlm_read_message_fields_buffer(s, &(message->DomainName))) /* DomainName */
+ goto fail;
+
+ if (!ntlm_read_message_fields_buffer(s, &(message->UserName))) /* UserName */
+ goto fail;
+
+ if (!ntlm_read_message_fields_buffer(s, &(message->Workstation))) /* Workstation */
+ goto fail;
+
+ if (!ntlm_read_message_fields_buffer(s,
+ &(message->LmChallengeResponse))) /* LmChallengeResponse */
+ goto fail;
+
+ if (!ntlm_read_message_fields_buffer(s,
+ &(message->NtChallengeResponse))) /* NtChallengeResponse */
+ goto fail;
+
+ if (message->NtChallengeResponse.Len > 0)
+ {
+ size_t cbAvFlags = 0;
+ wStream ssbuffer;
+ wStream* snt = Stream_StaticConstInit(&ssbuffer, message->NtChallengeResponse.Buffer,
+ message->NtChallengeResponse.Len);
+
+ if (!snt)
+ goto fail;
+
+ status = SEC_E_INVALID_TOKEN;
+ if (!ntlm_read_ntlm_v2_response(snt, &(context->NTLMv2Response)))
+ goto fail;
+ status = SEC_E_INTERNAL_ERROR;
+
+ context->NtChallengeResponse.pvBuffer = message->NtChallengeResponse.Buffer;
+ context->NtChallengeResponse.cbBuffer = message->NtChallengeResponse.Len;
+ sspi_SecBufferFree(&(context->ChallengeTargetInfo));
+ context->ChallengeTargetInfo.pvBuffer = (void*)context->NTLMv2Response.Challenge.AvPairs;
+ context->ChallengeTargetInfo.cbBuffer = message->NtChallengeResponse.Len - (28 + 16);
+ CopyMemory(context->ClientChallenge, context->NTLMv2Response.Challenge.ClientChallenge, 8);
+ AvFlags =
+ ntlm_av_pair_get(context->NTLMv2Response.Challenge.AvPairs,
+ context->NTLMv2Response.Challenge.cbAvPairs, MsvAvFlags, &cbAvFlags);
+
+ if (AvFlags)
+ Data_Read_UINT32(ntlm_av_pair_get_value_pointer(AvFlags), flags);
+ }
+
+ if (!ntlm_read_message_fields_buffer(
+ s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
+ goto fail;
+
+ if (message->EncryptedRandomSessionKey.Len > 0)
+ {
+ if (message->EncryptedRandomSessionKey.Len != 16)
+ goto fail;
+
+ CopyMemory(context->EncryptedRandomSessionKey, message->EncryptedRandomSessionKey.Buffer,
+ 16);
+ }
+
+ length = Stream_GetPosition(s);
+ WINPR_ASSERT(length <= ULONG_MAX);
+
+ if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length))
+ goto fail;
+
+ CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
+ buffer->cbBuffer = (ULONG)length;
+ Stream_SetPosition(s, PayloadBufferOffset);
+
+ if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
+ {
+ status = SEC_E_INVALID_TOKEN;
+ if (!ntlm_read_message_integrity_check(
+ s, &context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
+ goto fail;
+ }
+
+ status = SEC_E_INTERNAL_ERROR;
+
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_authenticate_message(&context->AuthenticateMessage, message, flags, NULL);
+#endif
+
+ if (message->UserName.Len > 0)
+ {
+ credentials->identity.User = (UINT16*)malloc(message->UserName.Len);
+
+ if (!credentials->identity.User)
+ goto fail;
+
+ CopyMemory(credentials->identity.User, message->UserName.Buffer, message->UserName.Len);
+ credentials->identity.UserLength = message->UserName.Len / 2;
+ }
+
+ if (message->DomainName.Len > 0)
+ {
+ credentials->identity.Domain = (UINT16*)malloc(message->DomainName.Len);
+
+ if (!credentials->identity.Domain)
+ goto fail;
+
+ CopyMemory(credentials->identity.Domain, message->DomainName.Buffer,
+ message->DomainName.Len);
+ credentials->identity.DomainLength = message->DomainName.Len / 2;
+ }
+
+ if (context->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
+ {
+ if (!ntlm_compute_lm_v2_response(context)) /* LmChallengeResponse */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (!ntlm_compute_ntlm_v2_response(context)) /* NtChallengeResponse */
+ return SEC_E_INTERNAL_ERROR;
+
+ /* KeyExchangeKey */
+ ntlm_generate_key_exchange_key(context);
+ /* EncryptedRandomSessionKey */
+ ntlm_decrypt_random_session_key(context);
+ /* ExportedSessionKey */
+ ntlm_generate_exported_session_key(context);
+
+ if (flags & MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK)
+ {
+ BYTE messageIntegrityCheck[16] = { 0 };
+
+ ntlm_compute_message_integrity_check(context, messageIntegrityCheck,
+ sizeof(messageIntegrityCheck));
+ CopyMemory(
+ &((PBYTE)context->AuthenticateMessage.pvBuffer)[context->MessageIntegrityCheckOffset],
+ message->MessageIntegrityCheck, sizeof(message->MessageIntegrityCheck));
+
+ if (memcmp(messageIntegrityCheck, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck)) != 0)
+ {
+ WLog_ERR(TAG, "Message Integrity Check (MIC) verification failed!");
+#ifdef WITH_DEBUG_NTLM
+ WLog_ERR(TAG, "Expected MIC:");
+ winpr_HexDump(TAG, WLOG_ERROR, messageIntegrityCheck, sizeof(messageIntegrityCheck));
+ WLog_ERR(TAG, "Actual MIC:");
+ winpr_HexDump(TAG, WLOG_ERROR, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck));
+#endif
+ return SEC_E_MESSAGE_ALTERED;
+ }
+ }
+ else
+ {
+ /* no mic message was present
+
+ https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/f9e6fbc4-a953-4f24-b229-ccdcc213b9ec
+ the mic is optional, as not supported in Windows NT, Windows 2000, Windows XP, and
+ Windows Server 2003 and, as it seems, in the NTLMv2 implementation of Qt5.
+
+ now check the NtProofString, to detect if the entered client password matches the
+ expected password.
+ */
+
+#ifdef WITH_DEBUG_NTLM
+ WLog_VRB(TAG, "No MIC present, using NtProofString for verification.");
+#endif
+
+ if (memcmp(context->NTLMv2Response.Response, context->NtProofString, 16) != 0)
+ {
+ WLog_ERR(TAG, "NtProofString verification failed!");
+#ifdef WITH_DEBUG_NTLM
+ WLog_ERR(TAG, "Expected NtProofString:");
+ winpr_HexDump(TAG, WLOG_ERROR, context->NtProofString, sizeof(context->NtProofString));
+ WLog_ERR(TAG, "Actual NtProofString:");
+ winpr_HexDump(TAG, WLOG_ERROR, context->NTLMv2Response.Response,
+ sizeof(context->NTLMv2Response));
+#endif
+ return SEC_E_LOGON_DENIED;
+ }
+ }
+
+ /* Generate signing keys */
+ if (!ntlm_generate_client_signing_key(context))
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_generate_server_signing_key(context))
+ return SEC_E_INTERNAL_ERROR;
+ /* Generate sealing keys */
+ if (!ntlm_generate_client_sealing_key(context))
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_generate_server_sealing_key(context))
+ return SEC_E_INTERNAL_ERROR;
+ /* Initialize RC4 seal state */
+ if (!ntlm_init_rc4_seal_states(context))
+ return SEC_E_INTERNAL_ERROR;
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_authentication_complete(context);
+#endif
+ ntlm_change_state(context, NTLM_STATE_FINAL);
+ ntlm_free_message_fields_buffer(&(message->DomainName));
+ ntlm_free_message_fields_buffer(&(message->UserName));
+ ntlm_free_message_fields_buffer(&(message->Workstation));
+ ntlm_free_message_fields_buffer(&(message->LmChallengeResponse));
+ ntlm_free_message_fields_buffer(&(message->NtChallengeResponse));
+ ntlm_free_message_fields_buffer(&(message->EncryptedRandomSessionKey));
+ return SEC_E_OK;
+
+fail:
+ return status;
+}
+
+/**
+ * Send NTLMSSP AUTHENTICATE_MESSAGE. msdn{cc236643}
+ *
+ * @param context Pointer to the NTLM context
+ * @param buffer The buffer to write
+ */
+
+SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer)
+{
+ wStream sbuffer;
+ wStream* s = NULL;
+ size_t length = 0;
+ UINT32 PayloadBufferOffset = 0;
+ const NTLM_AUTHENTICATE_MESSAGE empty = { 0 };
+ NTLM_AUTHENTICATE_MESSAGE* message = NULL;
+ SSPI_CREDENTIALS* credentials = NULL;
+
+ WINPR_ASSERT(context);
+ WINPR_ASSERT(buffer);
+
+ credentials = context->credentials;
+ WINPR_ASSERT(credentials);
+
+ message = &context->AUTHENTICATE_MESSAGE;
+ WINPR_ASSERT(message);
+
+ *message = empty;
+
+ s = Stream_StaticInit(&sbuffer, buffer->pvBuffer, buffer->cbBuffer);
+
+ if (!s)
+ return SEC_E_INTERNAL_ERROR;
+
+ if (context->NTLMv2)
+ {
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_56;
+
+ if (context->SendVersionInfo)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_VERSION;
+ }
+
+ if (context->UseMIC)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_TARGET_INFO;
+
+ if (context->SendWorkstationName)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED;
+
+ if (context->confidentiality)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SEAL;
+
+ if (context->CHALLENGE_MESSAGE.NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_KEY_EXCH;
+
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_128;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_EXTENDED_SESSION_SECURITY;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_ALWAYS_SIGN;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_NTLM;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_SIGN;
+ message->NegotiateFlags |= NTLMSSP_REQUEST_TARGET;
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_UNICODE;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ ntlm_get_version_info(&(message->Version));
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ {
+ message->Workstation.Len = context->Workstation.Length;
+ message->Workstation.Buffer = (BYTE*)context->Workstation.Buffer;
+ }
+
+ if (credentials->identity.DomainLength > 0)
+ {
+ message->NegotiateFlags |= NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED;
+ message->DomainName.Len = (UINT16)credentials->identity.DomainLength * 2;
+ message->DomainName.Buffer = (BYTE*)credentials->identity.Domain;
+ }
+
+ message->UserName.Len = (UINT16)credentials->identity.UserLength * 2;
+ message->UserName.Buffer = (BYTE*)credentials->identity.User;
+ message->LmChallengeResponse.Len = (UINT16)context->LmChallengeResponse.cbBuffer;
+ message->LmChallengeResponse.Buffer = (BYTE*)context->LmChallengeResponse.pvBuffer;
+ message->NtChallengeResponse.Len = (UINT16)context->NtChallengeResponse.cbBuffer;
+ message->NtChallengeResponse.Buffer = (BYTE*)context->NtChallengeResponse.pvBuffer;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ {
+ message->EncryptedRandomSessionKey.Len = 16;
+ message->EncryptedRandomSessionKey.Buffer = context->EncryptedRandomSessionKey;
+ }
+
+ PayloadBufferOffset = 64;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ PayloadBufferOffset += 8; /* Version (8 bytes) */
+
+ if (context->UseMIC)
+ PayloadBufferOffset += 16; /* Message Integrity Check (16 bytes) */
+
+ message->DomainName.BufferOffset = PayloadBufferOffset;
+ message->UserName.BufferOffset = message->DomainName.BufferOffset + message->DomainName.Len;
+ message->Workstation.BufferOffset = message->UserName.BufferOffset + message->UserName.Len;
+ message->LmChallengeResponse.BufferOffset =
+ message->Workstation.BufferOffset + message->Workstation.Len;
+ message->NtChallengeResponse.BufferOffset =
+ message->LmChallengeResponse.BufferOffset + message->LmChallengeResponse.Len;
+ message->EncryptedRandomSessionKey.BufferOffset =
+ message->NtChallengeResponse.BufferOffset + message->NtChallengeResponse.Len;
+ if (!ntlm_populate_message_header(&message->header, MESSAGE_TYPE_AUTHENTICATE))
+ return SEC_E_INVALID_TOKEN;
+ if (!ntlm_write_message_header(s, &message->header)) /* Message Header (12 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(
+ s, &(message->LmChallengeResponse))) /* LmChallengeResponseFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(
+ s, &(message->NtChallengeResponse))) /* NtChallengeResponseFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(s, &(message->DomainName))) /* DomainNameFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(s, &(message->UserName))) /* UserNameFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(s, &(message->Workstation))) /* WorkstationFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_message_fields(
+ s,
+ &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKeyFields (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ if (!ntlm_write_negotiate_flags(s, message->NegotiateFlags, "NTLM_AUTHENTICATE_MESSAGE"))
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_VERSION)
+ {
+ if (!ntlm_write_version_info(s, &(message->Version))) /* Version (8 bytes) */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (context->UseMIC)
+ {
+ const BYTE data[WINPR_MD5_DIGEST_LENGTH] = { 0 };
+
+ context->MessageIntegrityCheckOffset = Stream_GetPosition(s);
+ if (!ntlm_write_message_integrity_check(s, Stream_GetPosition(s), data, sizeof(data),
+ "NTLM_AUTHENTICATE_MESSAGE"))
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_DOMAIN_SUPPLIED)
+ {
+ if (!ntlm_write_message_fields_buffer(s, &(message->DomainName))) /* DomainName */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (!ntlm_write_message_fields_buffer(s, &(message->UserName))) /* UserName */
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_WORKSTATION_SUPPLIED)
+ {
+ if (!ntlm_write_message_fields_buffer(s, &(message->Workstation))) /* Workstation */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_LM_KEY)
+ {
+ if (!ntlm_write_message_fields_buffer(
+ s, &(message->LmChallengeResponse))) /* LmChallengeResponse */
+ return SEC_E_INTERNAL_ERROR;
+ }
+ if (!ntlm_write_message_fields_buffer(
+ s, &(message->NtChallengeResponse))) /* NtChallengeResponse */
+ return SEC_E_INTERNAL_ERROR;
+
+ if (message->NegotiateFlags & NTLMSSP_NEGOTIATE_KEY_EXCH)
+ {
+ if (!ntlm_write_message_fields_buffer(
+ s, &(message->EncryptedRandomSessionKey))) /* EncryptedRandomSessionKey */
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+ length = Stream_GetPosition(s);
+ WINPR_ASSERT(length <= ULONG_MAX);
+
+ if (!sspi_SecBufferAlloc(&context->AuthenticateMessage, (ULONG)length))
+ return SEC_E_INTERNAL_ERROR;
+
+ CopyMemory(context->AuthenticateMessage.pvBuffer, Stream_Buffer(s), length);
+ buffer->cbBuffer = (ULONG)length;
+
+ if (context->UseMIC)
+ {
+ /* Message Integrity Check */
+ ntlm_compute_message_integrity_check(context, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck));
+ if (!ntlm_write_message_integrity_check(
+ s, context->MessageIntegrityCheckOffset, message->MessageIntegrityCheck,
+ sizeof(message->MessageIntegrityCheck), "NTLM_AUTHENTICATE_MESSAGE"))
+ return SEC_E_INTERNAL_ERROR;
+ }
+
+#if defined(WITH_DEBUG_NTLM)
+ ntlm_print_authenticate_message(&context->AuthenticateMessage, message,
+ context->UseMIC ? MSV_AV_FLAGS_MESSAGE_INTEGRITY_CHECK : 0,
+ &context->AuthenticateTargetInfo);
+#endif
+ ntlm_change_state(context, NTLM_STATE_FINAL);
+ return SEC_E_OK;
+}
diff --git a/winpr/libwinpr/sspi/NTLM/ntlm_message.h b/winpr/libwinpr/sspi/NTLM/ntlm_message.h
new file mode 100644
index 0000000..58ff35d
--- /dev/null
+++ b/winpr/libwinpr/sspi/NTLM/ntlm_message.h
@@ -0,0 +1,36 @@
+/**
+ * WinPR: Windows Portable Runtime
+ * NTLM Security Package (Message)
+ *
+ * Copyright 2011-2014 Marc-Andre Moreau <marcandre.moreau@gmail.com>
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef WINPR_SSPI_NTLM_MESSAGE_H
+#define WINPR_SSPI_NTLM_MESSAGE_H
+
+#include "ntlm.h"
+
+SECURITY_STATUS ntlm_read_NegotiateMessage(NTLM_CONTEXT* context, PSecBuffer buffer);
+SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer);
+SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, PSecBuffer buffer);
+SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, const PSecBuffer buffer);
+SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, PSecBuffer buffer);
+SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, const PSecBuffer buffer);
+
+SECURITY_STATUS ntlm_server_AuthenticateComplete(NTLM_CONTEXT* context);
+
+const char* ntlm_get_negotiate_string(UINT32 flag);
+
+#endif /* WINPR_SSPI_NTLM_MESSAGE_H */