Milestone 5: deliver embedded RDP sessions and lifecycle hardening

This commit is contained in:
Keith Smith
2026-03-03 18:59:26 -07:00
parent 230a401386
commit 36006bd4aa
2941 changed files with 724359 additions and 77 deletions

View File

@@ -0,0 +1,120 @@
# WinPR: Windows Portable Runtime
# libwinpr-sspi cmake build script
#
# Copyright 2011 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.
set(MODULE_PREFIX "WINPR_SSPI")
set(${MODULE_PREFIX}_NTLM_SRCS
NTLM/ntlm_av_pairs.c
NTLM/ntlm_av_pairs.h
NTLM/ntlm_compute.c
NTLM/ntlm_compute.h
NTLM/ntlm_message.c
NTLM/ntlm_message.h
NTLM/ntlm.c
NTLM/ntlm.h
NTLM/ntlm_export.h
)
set(${MODULE_PREFIX}_KERBEROS_SRCS Kerberos/kerberos.c Kerberos/kerberos.h)
set(${MODULE_PREFIX}_NEGOTIATE_SRCS Negotiate/negotiate.c Negotiate/negotiate.h)
set(${MODULE_PREFIX}_SCHANNEL_SRCS Schannel/schannel_openssl.c Schannel/schannel_openssl.h Schannel/schannel.c
Schannel/schannel.h
)
set(${MODULE_PREFIX}_CREDSSP_SRCS CredSSP/credssp.c CredSSP/credssp.h)
set(${MODULE_PREFIX}_SRCS
sspi_winpr.c
sspi_winpr.h
sspi_export.c
sspi_gss.c
sspi_gss.h
sspi.c
sspi.h
)
set(KRB5_DEFAULT OFF)
if(NOT WIN32 AND NOT ANDROID AND NOT IOS AND NOT APPLE)
set(KRB5_DEFAULT ON)
endif()
option(WITH_DEBUG_SCHANNEL "Compile support for SCHANNEL debug" ${DEFAULT_DEBUG_OPTION})
if(WITH_DEBUG_SCHANNEL)
winpr_definition_add(WITH_DEBUG_SCHANNEL)
endif()
option(WITH_KRB5 "Compile support for kerberos authentication." ${KRB5_DEFAULT})
if(WITH_KRB5)
find_package(KRB5 REQUIRED)
list(APPEND ${MODULE_PREFIX}_KERBEROS_SRCS Kerberos/krb5glue.h)
winpr_system_include_directory_add(${KRB5_INCLUDEDIR})
winpr_system_include_directory_add(${KRB5_INCLUDE_DIRS})
winpr_library_add_private(${KRB5_LIBRARIES})
winpr_library_add_private(${KRB5_LIBRARY})
winpr_library_add_compile_options(${KRB5_CFLAGS})
winpr_library_add_link_options(${KRB5_LDFLAGS})
winpr_library_add_link_directory(${KRB5_LIBRARY_DIRS})
winpr_definition_add(WITH_KRB5)
if(KRB5_FLAVOUR STREQUAL "MIT")
winpr_definition_add(WITH_KRB5_MIT)
winpr_pc_add_requires_private("mit-krb5")
list(APPEND ${MODULE_PREFIX}_KERBEROS_SRCS Kerberos/krb5glue_mit.c)
elseif(KRB5_FLAVOUR STREQUAL "Heimdal")
winpr_definition_add(WITH_KRB5_HEIMDAL)
winpr_pc_add_requires_private("krb5")
list(APPEND ${MODULE_PREFIX}_KERBEROS_SRCS Kerberos/krb5glue_heimdal.c)
else()
message(WARNING "Kerberos version not detected")
endif()
include(CMakeDependentOption)
cmake_dependent_option(
WITH_KRB5_NO_NTLM_FALLBACK "Do not fall back to NTLM if no kerberos ticket available" OFF "WITH_KRB5" OFF
)
if(WITH_KRB5_NO_NTLM_FALLBACK)
add_compile_definitions("WITH_KRB5_NO_NTLM_FALLBACK")
endif()
endif()
winpr_module_add(
${${MODULE_PREFIX}_CREDSSP_SRCS} ${${MODULE_PREFIX}_NTLM_SRCS} ${${MODULE_PREFIX}_KERBEROS_SRCS}
${${MODULE_PREFIX}_NEGOTIATE_SRCS} ${${MODULE_PREFIX}_SCHANNEL_SRCS} ${${MODULE_PREFIX}_SRCS}
)
if(OPENSSL_FOUND)
winpr_system_include_directory_add(${OPENSSL_INCLUDE_DIR})
winpr_library_add_private(${OPENSSL_LIBRARIES})
endif()
if(MBEDTLS_FOUND)
winpr_system_include_directory_add(${MBEDTLS_INCLUDE_DIR})
winpr_library_add_private(${MBEDTLS_LIBRARIES})
endif()
if(WIN32)
winpr_library_add_public(ws2_32)
endif()
if(BUILD_TESTING_INTERNAL OR BUILD_TESTING)
add_subdirectory(test)
endif()

View File

@@ -0,0 +1,339 @@
/**
* WinPR: Windows Portable Runtime
* Credential Security Support Provider (CredSSP)
*
* Copyright 2010-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/sspi.h>
#include "credssp.h"
#include "../sspi.h"
#include "../../log.h"
#define TAG WINPR_TAG("sspi.CredSSP")
static const char* CREDSSP_PACKAGE_NAME = "CredSSP";
static SECURITY_STATUS SEC_ENTRY credssp_InitializeSecurityContextW(
WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED PCtxtHandle phContext,
WINPR_ATTR_UNUSED SEC_WCHAR* pszTargetName, WINPR_ATTR_UNUSED ULONG fContextReq,
WINPR_ATTR_UNUSED ULONG Reserved1, WINPR_ATTR_UNUSED ULONG TargetDataRep,
WINPR_ATTR_UNUSED PSecBufferDesc pInput, WINPR_ATTR_UNUSED ULONG Reserved2,
WINPR_ATTR_UNUSED PCtxtHandle phNewContext, WINPR_ATTR_UNUSED PSecBufferDesc pOutput,
WINPR_ATTR_UNUSED PULONG pfContextAttr, WINPR_ATTR_UNUSED PTimeStamp ptsExpiry)
{
WLog_ERR(TAG, "TODO: Implement");
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY credssp_InitializeSecurityContextA(
PCredHandle phCredential, PCtxtHandle phContext, WINPR_ATTR_UNUSED SEC_CHAR* pszTargetName,
WINPR_ATTR_UNUSED ULONG fContextReq, WINPR_ATTR_UNUSED ULONG Reserved1,
WINPR_ATTR_UNUSED ULONG TargetDataRep, WINPR_ATTR_UNUSED PSecBufferDesc pInput,
WINPR_ATTR_UNUSED ULONG Reserved2, PCtxtHandle phNewContext,
WINPR_ATTR_UNUSED PSecBufferDesc pOutput, WINPR_ATTR_UNUSED PULONG pfContextAttr,
WINPR_ATTR_UNUSED PTimeStamp ptsExpiry)
{
CREDSSP_CONTEXT* context = nullptr;
SSPI_CREDENTIALS* credentials = nullptr;
/* behave like windows SSPIs that don't want empty context */
if (phContext && !phContext->dwLower && !phContext->dwUpper)
return SEC_E_INVALID_HANDLE;
context = (CREDSSP_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
if (!context)
{
union
{
const void* cpv;
void* pv;
} cnv;
context = credssp_ContextNew();
if (!context)
return SEC_E_INSUFFICIENT_MEMORY;
credentials = (SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
if (!credentials)
{
credssp_ContextFree(context);
return SEC_E_INVALID_HANDLE;
}
sspi_SecureHandleSetLowerPointer(phNewContext, context);
cnv.cpv = CREDSSP_PACKAGE_NAME;
sspi_SecureHandleSetUpperPointer(phNewContext, cnv.pv);
}
return SEC_E_OK;
}
CREDSSP_CONTEXT* credssp_ContextNew(void)
{
CREDSSP_CONTEXT* context = nullptr;
context = (CREDSSP_CONTEXT*)calloc(1, sizeof(CREDSSP_CONTEXT));
if (!context)
return nullptr;
return context;
}
void credssp_ContextFree(CREDSSP_CONTEXT* context)
{
free(context);
}
static SECURITY_STATUS SEC_ENTRY credssp_QueryContextAttributes(PCtxtHandle phContext,
WINPR_ATTR_UNUSED ULONG ulAttribute,
void* pBuffer)
{
if (!phContext)
return SEC_E_INVALID_HANDLE;
if (!pBuffer)
return SEC_E_INSUFFICIENT_MEMORY;
WLog_ERR(TAG, "TODO: Implement");
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY credssp_AcquireCredentialsHandleW(
WINPR_ATTR_UNUSED SEC_WCHAR* pszPrincipal, WINPR_ATTR_UNUSED SEC_WCHAR* pszPackage,
WINPR_ATTR_UNUSED ULONG fCredentialUse, WINPR_ATTR_UNUSED void* pvLogonID,
WINPR_ATTR_UNUSED void* pAuthData, WINPR_ATTR_UNUSED SEC_GET_KEY_FN pGetKeyFn,
WINPR_ATTR_UNUSED void* pvGetKeyArgument, WINPR_ATTR_UNUSED PCredHandle phCredential,
WINPR_ATTR_UNUSED PTimeStamp ptsExpiry)
{
WLog_ERR(TAG, "TODO: Implement");
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY credssp_AcquireCredentialsHandleA(
WINPR_ATTR_UNUSED SEC_CHAR* pszPrincipal, WINPR_ATTR_UNUSED SEC_CHAR* pszPackage,
WINPR_ATTR_UNUSED ULONG fCredentialUse, WINPR_ATTR_UNUSED void* pvLogonID,
WINPR_ATTR_UNUSED void* pAuthData, WINPR_ATTR_UNUSED SEC_GET_KEY_FN pGetKeyFn,
WINPR_ATTR_UNUSED void* pvGetKeyArgument, WINPR_ATTR_UNUSED PCredHandle phCredential,
WINPR_ATTR_UNUSED PTimeStamp ptsExpiry)
{
SSPI_CREDENTIALS* credentials = nullptr;
SEC_WINNT_AUTH_IDENTITY* identity = nullptr;
if (fCredentialUse == SECPKG_CRED_OUTBOUND)
{
union
{
const void* cpv;
void* pv;
} cnv;
credentials = sspi_CredentialsNew();
if (!credentials)
return SEC_E_INSUFFICIENT_MEMORY;
identity = (SEC_WINNT_AUTH_IDENTITY*)pAuthData;
CopyMemory(&(credentials->identity), identity, sizeof(SEC_WINNT_AUTH_IDENTITY));
sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials);
cnv.cpv = CREDSSP_PACKAGE_NAME;
sspi_SecureHandleSetUpperPointer(phCredential, cnv.pv);
return SEC_E_OK;
}
WLog_ERR(TAG, "TODO: Implement");
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY credssp_QueryCredentialsAttributesW(
WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
WINPR_ATTR_UNUSED void* pBuffer)
{
WLog_ERR(TAG, "TODO: Implement");
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY credssp_QueryCredentialsAttributesA(
WINPR_ATTR_UNUSED PCredHandle phCredential, WINPR_ATTR_UNUSED ULONG ulAttribute,
WINPR_ATTR_UNUSED void* pBuffer)
{
if (ulAttribute == SECPKG_CRED_ATTR_NAMES)
{
SSPI_CREDENTIALS* credentials =
(SSPI_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
if (!credentials)
return SEC_E_INVALID_HANDLE;
return SEC_E_OK;
}
WLog_ERR(TAG, "TODO: Implement");
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY credssp_FreeCredentialsHandle(PCredHandle phCredential)
{
SSPI_CREDENTIALS* credentials = nullptr;
if (!phCredential)
return SEC_E_INVALID_HANDLE;
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 credssp_EncryptMessage(WINPR_ATTR_UNUSED PCtxtHandle phContext,
WINPR_ATTR_UNUSED ULONG fQOP,
WINPR_ATTR_UNUSED PSecBufferDesc pMessage,
WINPR_ATTR_UNUSED ULONG MessageSeqNo)
{
WLog_ERR(TAG, "TODO: Implement");
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY credssp_DecryptMessage(WINPR_ATTR_UNUSED PCtxtHandle phContext,
WINPR_ATTR_UNUSED PSecBufferDesc pMessage,
WINPR_ATTR_UNUSED ULONG MessageSeqNo,
WINPR_ATTR_UNUSED ULONG* pfQOP)
{
WLog_ERR(TAG, "TODO: Implement");
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY credssp_MakeSignature(WINPR_ATTR_UNUSED PCtxtHandle phContext,
WINPR_ATTR_UNUSED ULONG fQOP,
WINPR_ATTR_UNUSED PSecBufferDesc pMessage,
WINPR_ATTR_UNUSED ULONG MessageSeqNo)
{
WLog_ERR(TAG, "TODO: Implement");
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY credssp_VerifySignature(WINPR_ATTR_UNUSED PCtxtHandle phContext,
WINPR_ATTR_UNUSED PSecBufferDesc pMessage,
WINPR_ATTR_UNUSED ULONG MessageSeqNo,
WINPR_ATTR_UNUSED ULONG* pfQOP)
{
WLog_ERR(TAG, "TODO: Implement");
return SEC_E_UNSUPPORTED_FUNCTION;
}
const SecurityFunctionTableA CREDSSP_SecurityFunctionTableA = {
3, /* dwVersion */
nullptr, /* EnumerateSecurityPackages */
credssp_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
credssp_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
credssp_FreeCredentialsHandle, /* FreeCredentialsHandle */
nullptr, /* Reserved2 */
credssp_InitializeSecurityContextA, /* InitializeSecurityContext */
nullptr, /* AcceptSecurityContext */
nullptr, /* CompleteAuthToken */
nullptr, /* DeleteSecurityContext */
nullptr, /* ApplyControlToken */
credssp_QueryContextAttributes, /* QueryContextAttributes */
nullptr, /* ImpersonateSecurityContext */
nullptr, /* RevertSecurityContext */
credssp_MakeSignature, /* MakeSignature */
credssp_VerifySignature, /* VerifySignature */
nullptr, /* FreeContextBuffer */
nullptr, /* QuerySecurityPackageInfo */
nullptr, /* Reserved3 */
nullptr, /* Reserved4 */
nullptr, /* ExportSecurityContext */
nullptr, /* ImportSecurityContext */
nullptr, /* AddCredentials */
nullptr, /* Reserved8 */
nullptr, /* QuerySecurityContextToken */
credssp_EncryptMessage, /* EncryptMessage */
credssp_DecryptMessage, /* DecryptMessage */
nullptr, /* SetContextAttributes */
nullptr, /* SetCredentialsAttributes */
};
const SecurityFunctionTableW CREDSSP_SecurityFunctionTableW = {
3, /* dwVersion */
nullptr, /* EnumerateSecurityPackages */
credssp_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
credssp_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
credssp_FreeCredentialsHandle, /* FreeCredentialsHandle */
nullptr, /* Reserved2 */
credssp_InitializeSecurityContextW, /* InitializeSecurityContext */
nullptr, /* AcceptSecurityContext */
nullptr, /* CompleteAuthToken */
nullptr, /* DeleteSecurityContext */
nullptr, /* ApplyControlToken */
credssp_QueryContextAttributes, /* QueryContextAttributes */
nullptr, /* ImpersonateSecurityContext */
nullptr, /* RevertSecurityContext */
credssp_MakeSignature, /* MakeSignature */
credssp_VerifySignature, /* VerifySignature */
nullptr, /* FreeContextBuffer */
nullptr, /* QuerySecurityPackageInfo */
nullptr, /* Reserved3 */
nullptr, /* Reserved4 */
nullptr, /* ExportSecurityContext */
nullptr, /* ImportSecurityContext */
nullptr, /* AddCredentials */
nullptr, /* Reserved8 */
nullptr, /* QuerySecurityContextToken */
credssp_EncryptMessage, /* EncryptMessage */
credssp_DecryptMessage, /* DecryptMessage */
nullptr, /* SetContextAttributes */
nullptr, /* SetCredentialsAttributes */
};
const SecPkgInfoA CREDSSP_SecPkgInfoA = {
0x000110733, /* fCapabilities */
1, /* wVersion */
0xFFFF, /* wRPCID */
0x000090A8, /* cbMaxToken */
"CREDSSP", /* Name */
"Microsoft CredSSP Security Provider" /* Comment */
};
static WCHAR CREDSSP_SecPkgInfoW_NameBuffer[128] = WINPR_C_ARRAY_INIT;
static WCHAR CREDSSP_SecPkgInfoW_CommentBuffer[128] = WINPR_C_ARRAY_INIT;
const SecPkgInfoW CREDSSP_SecPkgInfoW = {
0x000110733, /* fCapabilities */
1, /* wVersion */
0xFFFF, /* wRPCID */
0x000090A8, /* cbMaxToken */
CREDSSP_SecPkgInfoW_NameBuffer, /* Name */
CREDSSP_SecPkgInfoW_CommentBuffer /* Comment */
};
BOOL CREDSSP_init(void)
{
InitializeConstWCharFromUtf8(CREDSSP_SecPkgInfoA.Name, CREDSSP_SecPkgInfoW_NameBuffer,
ARRAYSIZE(CREDSSP_SecPkgInfoW_NameBuffer));
InitializeConstWCharFromUtf8(CREDSSP_SecPkgInfoA.Comment, CREDSSP_SecPkgInfoW_CommentBuffer,
ARRAYSIZE(CREDSSP_SecPkgInfoW_CommentBuffer));
return TRUE;
}

View File

@@ -0,0 +1,42 @@
/**
* WinPR: Windows Portable Runtime
* Credential Security Support Provider (CredSSP)
*
* Copyright 2010-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_CREDSSP_PRIVATE_H
#define WINPR_SSPI_CREDSSP_PRIVATE_H
#include <winpr/sspi.h>
#include "../sspi.h"
typedef struct
{
BOOL server;
} CREDSSP_CONTEXT;
CREDSSP_CONTEXT* credssp_ContextNew(void);
void credssp_ContextFree(CREDSSP_CONTEXT* context);
extern const SecPkgInfoA CREDSSP_SecPkgInfoA;
extern const SecPkgInfoW CREDSSP_SecPkgInfoW;
extern const SecurityFunctionTableA CREDSSP_SecurityFunctionTableA;
extern const SecurityFunctionTableW CREDSSP_SecurityFunctionTableW;
BOOL CREDSSP_init(void);
#endif /* WINPR_SSPI_CREDSSP_PRIVATE_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,39 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* Kerberos Auth Protocol
*
* Copyright 2015 ANSSI, Author Thomas Calderon
* Copyright 2017 Dorian Ducournau <dorian.ducournau@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_KERBEROS_PRIVATE_H
#define WINPR_SSPI_KERBEROS_PRIVATE_H
#include <winpr/sspi.h>
#include <winpr/windows.h>
#include "../sspi.h"
#include "../../log.h"
typedef struct s_KRB_CONTEXT KRB_CONTEXT;
extern const SecPkgInfoA KERBEROS_SecPkgInfoA;
extern const SecPkgInfoW KERBEROS_SecPkgInfoW;
extern const SecurityFunctionTableA KERBEROS_SecurityFunctionTableA;
extern const SecurityFunctionTableW KERBEROS_SecurityFunctionTableW;
BOOL KERBEROS_init(void);
#endif /* WINPR_SSPI_KERBEROS_PRIVATE_H */

View File

@@ -0,0 +1,108 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* Kerberos Auth Protocol
*
* Copyright 2022 Isaac Klein <fifthdegree@protonmail.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_KERBEROS_GLUE_PRIVATE_H
#define WINPR_SSPI_KERBEROS_GLUE_PRIVATE_H
#include <winpr/winpr.h>
#include <winpr/sspi.h>
#include <krb5.h>
#if defined(WITH_KRB5_MIT)
typedef krb5_key krb5glue_key;
typedef krb5_authenticator* krb5glue_authenticator;
#define krb5glue_free_authenticator(ctx, auth) krb5_free_authenticator((ctx), (auth))
#define krb5glue_crypto_length(ctx, key, type, size) \
krb5_c_crypto_length(ctx, krb5_k_key_enctype(ctx, key), type, size)
#define krb5glue_crypto_length_iov(ctx, key, iov, size) \
krb5_c_crypto_length_iov(ctx, krb5_k_key_enctype(ctx, key), iov, size)
#define krb5glue_encrypt_iov(ctx, key, usage, iov, size) \
krb5_k_encrypt_iov(ctx, key, usage, nullptr, iov, size)
#define krb5glue_decrypt_iov(ctx, key, usage, iov, size) \
krb5_k_decrypt_iov(ctx, key, usage, nullptr, iov, size)
#define krb5glue_make_checksum_iov(ctx, key, usage, iov, size) \
krb5_k_make_checksum_iov(ctx, 0, key, usage, iov, size)
#define krb5glue_verify_checksum_iov(ctx, key, usage, iov, size, is_valid) \
krb5_k_verify_checksum_iov(ctx, 0, key, usage, iov, size, is_valid)
#define krb5glue_auth_con_set_cksumtype(ctx, auth_ctx, cksumtype) \
krb5_auth_con_set_req_cksumtype(ctx, auth_ctx, cksumtype)
#define krb5glue_set_principal_realm(ctx, principal, realm) \
krb5_set_principal_realm(ctx, principal, realm)
#define krb5glue_free_keytab_entry_contents(ctx, entry) krb5_free_keytab_entry_contents(ctx, entry)
#define krb5glue_auth_con_setuseruserkey(ctx, auth_ctx, keytab) \
krb5_auth_con_setuseruserkey(ctx, auth_ctx, keytab)
#define krb5glue_free_data_contents(ctx, data) krb5_free_data_contents(ctx, data)
krb5_prompt_type krb5glue_get_prompt_type(krb5_context ctx, krb5_prompt prompts[], int index);
#define krb5glue_creds_getkey(creds) creds.keyblock
#elif defined(WITH_KRB5_HEIMDAL)
typedef krb5_crypto krb5glue_key;
typedef krb5_authenticator krb5glue_authenticator;
#define krb5glue_free_authenticator(ctx, auth) krb5_free_authenticator((ctx), &(auth))
krb5_error_code krb5glue_crypto_length(krb5_context ctx, krb5glue_key key, int type,
unsigned int* size);
#define krb5glue_crypto_length_iov(ctx, key, iov, size) krb5_crypto_length_iov(ctx, key, iov, size)
#define krb5glue_encrypt_iov(ctx, key, usage, iov, size) \
krb5_encrypt_iov_ivec(ctx, key, usage, iov, size, nullptr)
#define krb5glue_decrypt_iov(ctx, key, usage, iov, size) \
krb5_decrypt_iov_ivec(ctx, key, usage, iov, size, nullptr)
#define krb5glue_make_checksum_iov(ctx, key, usage, iov, size) \
krb5_create_checksum_iov(ctx, key, usage, iov, size, nullptr)
krb5_error_code krb5glue_verify_checksum_iov(krb5_context ctx, krb5glue_key key,
krb5_keyusage usage, krb5_crypto_iov* iov,
unsigned int iov_size, krb5_boolean* is_valid);
#define krb5glue_auth_con_set_cksumtype(ctx, auth_ctx, cksumtype) \
krb5_auth_con_setcksumtype(ctx, auth_ctx, cksumtype)
#define krb5glue_set_principal_realm(ctx, principal, realm) \
krb5_principal_set_realm(ctx, principal, realm)
#define krb5glue_free_keytab_entry_contents(ctx, entry) krb5_kt_free_entry(ctx, entry)
#define krb5glue_auth_con_setuseruserkey(ctx, auth_ctx, keytab) \
krb5_auth_con_setuserkey(ctx, auth_ctx, keytab)
#define krb5glue_free_data_contents(ctx, data) krb5_data_free(data)
#define krb5glue_get_prompt_type(ctx, prompts, index) prompts[index].type
#define krb5glue_creds_getkey(creds) creds.session
#else
#error "Missing implementation for KRB5 provider"
#endif
struct krb5glue_keyset
{
krb5glue_key session_key;
krb5glue_key initiator_key;
krb5glue_key acceptor_key;
};
void krb5glue_keys_free(krb5_context ctx, struct krb5glue_keyset* keyset);
krb5_error_code krb5glue_update_keyset(krb5_context ctx, krb5_auth_context auth_ctx, BOOL acceptor,
struct krb5glue_keyset* keyset);
krb5_error_code krb5glue_log_error(krb5_context ctx, krb5_data* msg, const char* tag);
BOOL krb5glue_authenticator_validate_chksum(krb5glue_authenticator authenticator, int cksumtype,
uint32_t* flags);
krb5_error_code krb5glue_get_init_creds(krb5_context ctx, krb5_principal princ, krb5_ccache ccache,
krb5_prompter_fct prompter, char* password,
SEC_WINPR_KERBEROS_SETTINGS* krb_settings);
#endif /* WINPR_SSPI_KERBEROS_GLUE_PRIVATE_H */

View File

@@ -0,0 +1,216 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* Kerberos Auth Protocol
*
* Copyright 2022 Isaac Klein <fifthdegree@protonmail.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 WITH_KRB5_HEIMDAL
#error "This file must only be included with HEIMDAL kerberos"
#endif
#include <winpr/endian.h>
#include <winpr/wlog.h>
#include <winpr/assert.h>
#include "krb5glue.h"
void krb5glue_keys_free(krb5_context ctx, struct krb5glue_keyset* keyset)
{
if (!ctx || !keyset)
return;
if (keyset->session_key)
krb5_crypto_destroy(ctx, keyset->session_key);
if (keyset->initiator_key)
krb5_crypto_destroy(ctx, keyset->initiator_key);
if (keyset->acceptor_key)
krb5_crypto_destroy(ctx, keyset->acceptor_key);
}
krb5_error_code krb5glue_update_keyset(krb5_context ctx, krb5_auth_context auth_ctx, BOOL acceptor,
struct krb5glue_keyset* keyset)
{
krb5_keyblock* keyblock = nullptr;
krb5_error_code rv = 0;
WINPR_ASSERT(ctx);
WINPR_ASSERT(auth_ctx);
WINPR_ASSERT(keyset);
krb5glue_keys_free(ctx, keyset);
rv = krb5_auth_con_getkey(ctx, auth_ctx, &keyblock);
if (!rv)
{
krb5_crypto_init(ctx, keyblock, ENCTYPE_NULL, &keyset->session_key);
krb5_free_keyblock(ctx, keyblock);
keyblock = nullptr;
}
if (acceptor)
rv = krb5_auth_con_getremotesubkey(ctx, auth_ctx, &keyblock);
else
rv = krb5_auth_con_getlocalsubkey(ctx, auth_ctx, &keyblock);
if (!rv && keyblock)
{
krb5_crypto_init(ctx, keyblock, ENCTYPE_NULL, &keyset->initiator_key);
krb5_free_keyblock(ctx, keyblock);
keyblock = nullptr;
}
if (acceptor)
rv = krb5_auth_con_getlocalsubkey(ctx, auth_ctx, &keyblock);
else
rv = krb5_auth_con_getremotesubkey(ctx, auth_ctx, &keyblock);
if (!rv && keyblock)
{
krb5_crypto_init(ctx, keyblock, ENCTYPE_NULL, &keyset->acceptor_key);
krb5_free_keyblock(ctx, keyblock);
}
return rv;
}
krb5_error_code krb5glue_verify_checksum_iov(krb5_context ctx, krb5glue_key key,
krb5_keyusage usage, krb5_crypto_iov* iov,
unsigned int iov_size, krb5_boolean* is_valid)
{
krb5_error_code rv = 0;
WINPR_ASSERT(ctx);
WINPR_ASSERT(key);
WINPR_ASSERT(is_valid);
rv = krb5_verify_checksum_iov(ctx, key, usage, iov, iov_size, nullptr);
*is_valid = (rv == 0);
return rv;
}
krb5_error_code krb5glue_crypto_length(krb5_context ctx, krb5glue_key key, int type,
unsigned int* size)
{
krb5_error_code rv = 0;
size_t s = 0;
WINPR_ASSERT(ctx);
WINPR_ASSERT(key);
WINPR_ASSERT(size);
rv = krb5_crypto_length(ctx, key, type, &s);
*size = (UINT)s;
return rv;
}
krb5_error_code krb5glue_log_error(krb5_context ctx, krb5_data* msg, const char* tag)
{
krb5_error error = WINPR_C_ARRAY_INIT;
krb5_error_code rv = 0;
WINPR_ASSERT(ctx);
WINPR_ASSERT(msg);
WINPR_ASSERT(tag);
if (!(rv = krb5_rd_error(ctx, msg, &error)))
{
WLog_ERR(tag, "KRB_ERROR: %" PRIx32, error.error_code);
krb5_free_error_contents(ctx, &error);
}
return rv;
}
BOOL krb5glue_authenticator_validate_chksum(krb5glue_authenticator authenticator, int cksumtype,
uint32_t* flags)
{
WINPR_ASSERT(flags);
if (!authenticator || !authenticator->cksum || authenticator->cksum->cksumtype != cksumtype ||
authenticator->cksum->checksum.length < 24)
return FALSE;
const BYTE* data = authenticator->cksum->checksum.data;
Data_Read_UINT32((data + 20), (*flags));
return TRUE;
}
krb5_error_code krb5glue_get_init_creds(krb5_context ctx, krb5_principal princ, krb5_ccache ccache,
krb5_prompter_fct prompter, char* password,
SEC_WINPR_KERBEROS_SETTINGS* krb_settings)
{
krb5_error_code rv = 0;
krb5_deltat start_time = 0;
krb5_get_init_creds_opt* gic_opt = nullptr;
krb5_init_creds_context creds_ctx = nullptr;
krb5_creds creds = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(ctx);
do
{
if ((rv = krb5_get_init_creds_opt_alloc(ctx, &gic_opt)) != 0)
break;
krb5_get_init_creds_opt_set_forwardable(gic_opt, 0);
krb5_get_init_creds_opt_set_proxiable(gic_opt, 0);
if (krb_settings)
{
if (krb_settings->startTime)
start_time = krb_settings->startTime;
if (krb_settings->lifeTime)
krb5_get_init_creds_opt_set_tkt_life(gic_opt, krb_settings->lifeTime);
if (krb_settings->renewLifeTime)
krb5_get_init_creds_opt_set_renew_life(gic_opt, krb_settings->renewLifeTime);
if (krb_settings->withPac)
krb5_get_init_creds_opt_set_pac_request(ctx, gic_opt, TRUE);
if (krb_settings->pkinitX509Anchors || krb_settings->pkinitX509Identity)
{
if ((rv = krb5_get_init_creds_opt_set_pkinit(
ctx, gic_opt, princ, krb_settings->pkinitX509Identity,
krb_settings->pkinitX509Anchors, nullptr, nullptr, 0, prompter, password,
password)) != 0)
break;
}
}
if ((rv = krb5_init_creds_init(ctx, princ, prompter, password, start_time, gic_opt,
&creds_ctx)) != 0)
break;
if ((rv = krb5_init_creds_set_password(ctx, creds_ctx, password)) != 0)
break;
if (krb_settings && krb_settings->armorCache)
{
krb5_ccache armor_cc = nullptr;
if ((rv = krb5_cc_resolve(ctx, krb_settings->armorCache, &armor_cc)) != 0)
break;
if ((rv = krb5_init_creds_set_fast_ccache(ctx, creds_ctx, armor_cc)) != 0)
break;
krb5_cc_close(ctx, armor_cc);
}
if ((rv = krb5_init_creds_get(ctx, creds_ctx)) != 0)
break;
if ((rv = krb5_init_creds_get_creds(ctx, creds_ctx, &creds)) != 0)
break;
if ((rv = krb5_cc_store_cred(ctx, ccache, &creds)) != 0)
break;
} while (0);
krb5_free_cred_contents(ctx, &creds);
krb5_init_creds_free(ctx, creds_ctx);
krb5_get_init_creds_opt_free(ctx, gic_opt);
return rv;
}

View File

@@ -0,0 +1,258 @@
/**
* FreeRDP: A Remote Desktop Protocol Client
* Kerberos Auth Protocol
*
* Copyright 2022 Isaac Klein <fifthdegree@protonmail.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 WITH_KRB5_MIT
#error "This file must only be included with MIT kerberos"
#endif
#include <string.h>
#include <winpr/path.h>
#include <winpr/wlog.h>
#include <winpr/endian.h>
#include <winpr/crypto.h>
#include <winpr/print.h>
#include <winpr/assert.h>
#include <errno.h>
#include "krb5glue.h"
#include <profile.h>
static char* create_temporary_file(void)
{
BYTE buffer[32];
char* hex = nullptr;
char* path = nullptr;
if (winpr_RAND(buffer, sizeof(buffer)) < 0)
return nullptr;
hex = winpr_BinToHexString(buffer, sizeof(buffer), FALSE);
path = GetKnownSubPath(KNOWN_PATH_TEMP, hex);
free(hex);
return path;
}
void krb5glue_keys_free(krb5_context ctx, struct krb5glue_keyset* keyset)
{
WINPR_ASSERT(ctx);
WINPR_ASSERT(keyset);
krb5_k_free_key(ctx, keyset->session_key);
krb5_k_free_key(ctx, keyset->initiator_key);
krb5_k_free_key(ctx, keyset->acceptor_key);
}
krb5_error_code krb5glue_update_keyset(krb5_context ctx, krb5_auth_context auth_ctx, BOOL acceptor,
struct krb5glue_keyset* keyset)
{
WINPR_ASSERT(ctx);
WINPR_ASSERT(auth_ctx);
WINPR_ASSERT(keyset);
krb5glue_keys_free(ctx, keyset);
krb5_auth_con_getkey_k(ctx, auth_ctx, &keyset->session_key);
if (acceptor)
{
krb5_auth_con_getsendsubkey_k(ctx, auth_ctx, &keyset->acceptor_key);
krb5_auth_con_getrecvsubkey_k(ctx, auth_ctx, &keyset->initiator_key);
}
else
{
krb5_auth_con_getsendsubkey_k(ctx, auth_ctx, &keyset->initiator_key);
krb5_auth_con_getrecvsubkey_k(ctx, auth_ctx, &keyset->acceptor_key);
}
return 0;
}
krb5_prompt_type krb5glue_get_prompt_type(krb5_context ctx, krb5_prompt prompts[], int index)
{
WINPR_ASSERT(ctx);
WINPR_ASSERT(prompts);
WINPR_UNUSED(prompts);
krb5_prompt_type* types = krb5_get_prompt_types(ctx);
return types ? types[index] : 0;
}
krb5_error_code krb5glue_log_error(krb5_context ctx, krb5_data* msg, const char* tag)
{
krb5_error* error = nullptr;
krb5_error_code rv = 0;
WINPR_ASSERT(ctx);
WINPR_ASSERT(msg);
WINPR_ASSERT(tag);
if (!(rv = krb5_rd_error(ctx, msg, &error)))
{
WLog_ERR(tag, "KRB_ERROR: %s", error->text.data);
krb5_free_error(ctx, error);
}
return rv;
}
BOOL krb5glue_authenticator_validate_chksum(krb5glue_authenticator authenticator, int cksumtype,
uint32_t* flags)
{
WINPR_ASSERT(flags);
if (!authenticator || !authenticator->checksum ||
authenticator->checksum->checksum_type != cksumtype || authenticator->checksum->length < 24)
return FALSE;
*flags = winpr_Data_Get_UINT32((authenticator->checksum->contents + 20));
return TRUE;
}
krb5_error_code krb5glue_get_init_creds(krb5_context ctx, krb5_principal princ, krb5_ccache ccache,
krb5_prompter_fct prompter, char* password,
SEC_WINPR_KERBEROS_SETTINGS* krb_settings)
{
krb5_error_code rv = 0;
krb5_deltat start_time = 0;
krb5_get_init_creds_opt* gic_opt = nullptr;
krb5_init_creds_context creds_ctx = nullptr;
char* tmp_profile_path = create_temporary_file();
profile_t profile = nullptr;
BOOL is_temp_ctx = FALSE;
WINPR_ASSERT(ctx);
rv = krb5_get_init_creds_opt_alloc(ctx, &gic_opt);
if (rv)
goto cleanup;
krb5_get_init_creds_opt_set_forwardable(gic_opt, 0);
krb5_get_init_creds_opt_set_proxiable(gic_opt, 0);
if (krb_settings)
{
if (krb_settings->startTime)
start_time = krb_settings->startTime;
if (krb_settings->lifeTime)
krb5_get_init_creds_opt_set_tkt_life(gic_opt, krb_settings->lifeTime);
if (krb_settings->renewLifeTime)
krb5_get_init_creds_opt_set_renew_life(gic_opt, krb_settings->renewLifeTime);
if (krb_settings->withPac)
{
rv = krb5_get_init_creds_opt_set_pac_request(ctx, gic_opt, TRUE);
if (rv)
goto cleanup;
}
if (krb_settings->armorCache)
{
rv = krb5_get_init_creds_opt_set_fast_ccache_name(ctx, gic_opt,
krb_settings->armorCache);
if (rv)
goto cleanup;
}
if (krb_settings->pkinitX509Identity)
{
rv = krb5_get_init_creds_opt_set_pa(ctx, gic_opt, "X509_user_identity",
krb_settings->pkinitX509Identity);
if (rv)
goto cleanup;
}
if (krb_settings->pkinitX509Anchors)
{
rv = krb5_get_init_creds_opt_set_pa(ctx, gic_opt, "X509_anchors",
krb_settings->pkinitX509Anchors);
if (rv)
goto cleanup;
}
if (krb_settings->kdcUrl && (strnlen(krb_settings->kdcUrl, 2) > 0))
{
const char* names[4] = WINPR_C_ARRAY_INIT;
char* realm = nullptr;
char* kdc_url = nullptr;
size_t size = 0;
if ((rv = krb5_get_profile(ctx, &profile)))
goto cleanup;
rv = ENOMEM;
if (winpr_asprintf(&kdc_url, &size, "https://%s/KdcProxy", krb_settings->kdcUrl) <= 0)
{
free(kdc_url);
goto cleanup;
}
realm = calloc(princ->realm.length + 1, 1);
if (!realm)
{
free(kdc_url);
goto cleanup;
}
CopyMemory(realm, princ->realm.data, princ->realm.length);
names[0] = "realms";
names[1] = realm;
names[2] = "kdc";
profile_clear_relation(profile, names);
profile_add_relation(profile, names, kdc_url);
/* Since we know who the KDC is, tell krb5 that its certificate is valid for pkinit */
names[2] = "pkinit_kdc_hostname";
profile_add_relation(profile, names, krb_settings->kdcUrl);
free(kdc_url);
free(realm);
long lrv = profile_flush_to_file(profile, tmp_profile_path);
if (lrv)
goto cleanup;
profile_abandon(profile);
profile = nullptr;
lrv = profile_init_path(tmp_profile_path, &profile);
if (lrv)
goto cleanup;
rv = krb5_init_context_profile(profile, 0, &ctx);
if (rv)
goto cleanup;
is_temp_ctx = TRUE;
}
}
if ((rv = krb5_get_init_creds_opt_set_in_ccache(ctx, gic_opt, ccache)))
goto cleanup;
if ((rv = krb5_get_init_creds_opt_set_out_ccache(ctx, gic_opt, ccache)))
goto cleanup;
if ((rv =
krb5_init_creds_init(ctx, princ, prompter, password, start_time, gic_opt, &creds_ctx)))
goto cleanup;
if ((rv = krb5_init_creds_get(ctx, creds_ctx)))
goto cleanup;
cleanup:
krb5_init_creds_free(ctx, creds_ctx);
krb5_get_init_creds_opt_free(ctx, gic_opt);
if (is_temp_ctx)
krb5_free_context(ctx);
profile_abandon(profile);
winpr_DeleteFile(tmp_profile_path);
free(tmp_profile_path);
return rv;
}

View File

@@ -0,0 +1,7 @@
set(MINWIN_LAYER "0")
set(MINWIN_GROUP "none")
set(MINWIN_MAJOR_VERSION "0")
set(MINWIN_MINOR_VERSION "0")
set(MINWIN_SHORT_NAME "sspi")
set(MINWIN_LONG_NAME "Security Support Provider Interface")
set(MODULE_LIBRARY_NAME "${MINWIN_SHORT_NAME}")

File diff suppressed because it is too large Load Diff

View File

@@ -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 */

View File

@@ -0,0 +1,808 @@
/**
* 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"
#if defined(WITH_DEBUG_NTLM)
#include "../../log.h"
#define TAG WINPR_TAG("sspi.NTLM")
#endif
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;
}
#ifdef WITH_DEBUG_NTLM
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";
}
}
#endif
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);
winpr_Data_Write_UINT16(&pAvPair->AvId, id);
}
static inline void ntlm_av_pair_set_len(NTLM_AV_PAIR* pAvPair, UINT16 len)
{
WINPR_ASSERT(pAvPair);
winpr_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)
{
if (!pAvPair || !pair)
return FALSE;
if (size < sizeof(NTLM_AV_PAIR))
return FALSE;
const UINT16 AvId = winpr_Data_Get_UINT16(&pAvPair->AvId);
*pair = AvId;
return TRUE;
}
ULONG ntlm_av_pair_list_length(NTLM_AV_PAIR* pAvPairList, size_t cbAvPairList)
{
size_t cbAvPair = 0;
NTLM_AV_PAIR* pAvPair = nullptr;
pAvPair = ntlm_av_pair_get(pAvPairList, cbAvPairList, MsvAvEOL, &cbAvPair);
if (!pAvPair)
return 0;
if (pAvPair < pAvPairList)
return 0;
const size_t size = WINPR_ASSERTING_INT_CAST(size_t, ((PBYTE)pAvPair - (PBYTE)pAvPairList)) +
sizeof(NTLM_AV_PAIR);
WINPR_ASSERT(size <= UINT32_MAX);
WINPR_ASSERT(size >= 0);
return (ULONG)size;
}
static inline BOOL ntlm_av_pair_get_len(const NTLM_AV_PAIR* pAvPair, size_t size, size_t* pAvLen)
{
if (!pAvPair)
return FALSE;
if (size < sizeof(NTLM_AV_PAIR))
return FALSE;
const UINT16 AvLen = winpr_Data_Get_UINT16(&pAvPair->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 size_t ntlm_av_pair_list_size(size_t AvPairsCount, size_t AvPairsValueLength)
{
/* size of headers + value lengths + terminating MsvAvEOL AV_PAIR */
return ((AvPairsCount + 1) * 4ULL) + 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 nullptr;
if (!ntlm_av_pair_check(pAvPair, *pcbAvPair))
return nullptr;
if (!ntlm_av_pair_get_next_offset(pAvPair, *pcbAvPair, &offset))
return nullptr;
*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 = nullptr;
while (pAvPair && ntlm_av_pair_get_id(pAvPair, cbAvPair, &id))
{
if (id == AvId)
break;
if (id == MsvAvEOL)
{
pAvPair = nullptr;
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 = nullptr;
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 != nullptr);
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_valid(UINT16 pair)
{
switch (pair)
{
case MsvAvEOL:
case MsvAvNbComputerName:
case MsvAvNbDomainName:
case MsvAvDnsComputerName:
case MsvAvDnsDomainName:
case MsvAvDnsTreeName:
case MsvAvFlags:
case MsvAvTimestamp:
case MsvAvSingleHost:
case MsvAvTargetName:
case MsvAvChannelBindings:
return TRUE;
default:
return FALSE;
}
}
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;
if (!ntlm_av_pair_valid(pair))
return FALSE;
WINPR_ASSERT(avLen <= UINT16_MAX);
return ntlm_av_pair_add(pAvPairList, cbAvPairList, WINPR_ASSERTING_INT_CAST(NTLM_AV_ID, pair),
ntlm_av_pair_get_value_pointer(pAvPair), (UINT16)avLen);
}
static char* get_name(COMPUTER_NAME_FORMAT type)
{
DWORD nSize = 0;
if (GetComputerNameExA(type, nullptr, &nSize))
return nullptr;
if (GetLastError() != ERROR_MORE_DATA)
return nullptr;
char* computerName = calloc(1, nSize);
if (!computerName)
return nullptr;
if (!GetComputerNameExA(type, computerName, &nSize))
{
free(computerName);
return nullptr;
}
return computerName;
}
static int ntlm_get_target_computer_name(PUNICODE_STRING pName,
WINPR_ATTR_UNUSED COMPUTER_NAME_FORMAT type)
{
int status = -1;
WINPR_ASSERT(pName);
char* name = get_name(ComputerNameNetBIOS);
if (!name)
return -1;
CharUpperA(name);
size_t len = 0;
pName->Buffer = ConvertUtf8ToWCharAlloc(name, &len);
free(name);
if (!pName->Buffer || (len == 0) || (len > UINT16_MAX / sizeof(WCHAR)))
{
free(pName->Buffer);
pName->Buffer = nullptr;
return status;
}
pName->Length = (USHORT)((len) * sizeof(WCHAR));
pName->MaximumLength = pName->Length;
return 1;
}
static void ntlm_free_unicode_string(PUNICODE_STRING string)
{
if (string)
{
if (string->Length > 0)
{
free(string->Buffer);
string->Buffer = nullptr;
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 = nullptr;
BYTE* ChannelBindingToken = nullptr;
UINT32 ChannelBindingTokenLength = 0;
SEC_CHANNEL_BINDINGS* ChannelBindings = nullptr;
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.
*/
winpr_Data_Write_UINT32(&context->SingleHostData.Size, 48);
winpr_Data_Write_UINT32(&context->SingleHostData.Z4, 0);
winpr_Data_Write_UINT32(&context->SingleHostData.DataPresent, 1);
winpr_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 AvPairsCount = 0;
ULONG AvPairsLength = 0;
NTLM_AV_PAIR* pAvPairList = nullptr;
size_t cbAvPairList = 0;
UNICODE_STRING NbDomainName = WINPR_C_ARRAY_INIT;
UNICODE_STRING NbComputerName = WINPR_C_ARRAY_INIT;
UNICODE_STRING DnsDomainName = WINPR_C_ARRAY_INIT;
UNICODE_STRING DnsComputerName = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(context);
if (ntlm_get_target_computer_name(&NbDomainName, ComputerNameNetBIOS) < 0)
goto fail;
NbComputerName.Buffer = nullptr;
if (ntlm_get_target_computer_name(&NbComputerName, ComputerNameNetBIOS) < 0)
goto fail;
DnsDomainName.Buffer = nullptr;
if (ntlm_get_target_computer_name(&DnsDomainName, ComputerNameDnsDomain) < 0)
goto fail;
DnsComputerName.Buffer = nullptr;
if (ntlm_get_target_computer_name(&DnsComputerName, ComputerNameDnsHostname) < 0)
goto fail;
AvPairsCount = 5;
AvPairsLength = NbDomainName.Length + NbComputerName.Length + DnsDomainName.Length +
DnsComputerName.Length + 8;
{
const size_t length = ntlm_av_pair_list_size(AvPairsCount, AvPairsLength);
if (!sspi_SecBufferAlloc(&context->ChallengeTargetInfo,
WINPR_ASSERTING_INT_CAST(uint32_t, 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 AvPairsCount = 0;
size_t AvPairsValueLength = 0;
NTLM_AV_PAIR* AvTimestamp = nullptr;
NTLM_AV_PAIR* AvNbDomainName = nullptr;
NTLM_AV_PAIR* AvNbComputerName = nullptr;
NTLM_AV_PAIR* AvDnsDomainName = nullptr;
NTLM_AV_PAIR* AvDnsComputerName = nullptr;
NTLM_AV_PAIR* AvDnsTreeName = nullptr;
NTLM_AV_PAIR* ChallengeTargetInfo = nullptr;
NTLM_AV_PAIR* AuthenticateTargetInfo = nullptr;
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;
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_t size = ntlm_av_pair_list_size(AvPairsCount, AvPairsValueLength);
if (context->NTLMv2)
size += 8; /* unknown 8-byte padding */
if (!sspi_SecBufferAlloc(&context->AuthenticateTargetInfo,
WINPR_ASSERTING_INT_CAST(uint32_t, 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;
winpr_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 = nullptr;
AvEOL = ntlm_av_pair_get(ChallengeTargetInfo, cbChallengeTargetInfo, MsvAvEOL, nullptr);
if (!AvEOL)
goto fail;
ZeroMemory(AvEOL, sizeof(NTLM_AV_PAIR));
}
return TRUE;
fail:
sspi_SecBufferFree(&context->AuthenticateTargetInfo);
return FALSE;
}

View File

@@ -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 */

View File

@@ -0,0 +1,960 @@
/**
* 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 = WINPR_C_ARRAY_INIT;
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
*/
static void ntlm_current_time(BYTE* timestamp, WINPR_ATTR_UNUSED size_t size)
{
FILETIME ft = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(timestamp);
WINPR_ASSERT(size >= sizeof(ft));
GetSystemTimeAsFileTime(&ft);
CopyMemory(timestamp, &(ft), sizeof(ft));
}
/**
* 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, sizeof(context->Timestamp));
}
static BOOL ntlm_fetch_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash)
{
BOOL rc = FALSE;
WINPR_SAM* sam = nullptr;
WINPR_SAM_ENTRY* entry = nullptr;
SSPI_CREDENTIALS* credentials = nullptr;
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), nullptr, 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), 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 hexchar2nibble(WCHAR wc)
{
#if defined(__BIG_ENDIAN__)
union
{
BYTE b[2];
WCHAR w;
} cnv;
cnv.w = wc;
const BYTE b = cnv.b[0];
cnv.b[0] = cnv.b[1];
cnv.b[1] = b;
wc = cnv.w;
#endif
switch (wc)
{
case L'0':
case L'1':
case L'2':
case L'3':
case L'4':
case L'5':
case L'6':
case L'7':
case L'8':
case L'9':
return wc - L'0';
case L'a':
case L'b':
case L'c':
case L'd':
case L'e':
case L'f':
return wc - L'a' + 10;
case L'A':
case L'B':
case L'C':
case L'D':
case L'E':
case L'F':
return wc - L'A' + 10;
default:
return -1;
}
}
static int ntlm_convert_password_hash(NTLM_CONTEXT* context, BYTE* hash, size_t hashlen)
{
const size_t required_len = 2ull * hashlen;
WINPR_ASSERT(context);
WINPR_ASSERT(hash);
SSPI_CREDENTIALS* credentials = context->credentials;
/* Password contains a password hash of length (PasswordLength -
* SSPI_CREDENTIALS_HASH_LENGTH_OFFSET) */
const ULONG PasswordHashLength = credentials->identity.PasswordLength -
/* Macro [globalScope] */ SSPI_CREDENTIALS_HASH_LENGTH_OFFSET;
if (PasswordHashLength != required_len)
{
WLog_ERR(TAG,
"PasswordHash has invalid length %" PRIu32 " must be exactly %" PRIuz " bytes",
PasswordHashLength, required_len);
return -1;
}
const WCHAR* PasswordHash = credentials->identity.Password;
for (size_t x = 0; x < hashlen; x++)
{
const int hi = hexchar2nibble(PasswordHash[2 * x]);
if (hi < 0)
{
WLog_ERR(TAG, "PasswordHash has an invalid value at position %" PRIuz, 2 * x);
return -1;
}
const int lo = hexchar2nibble(PasswordHash[2 * x + 1]);
if (lo < 0)
{
WLog_ERR(TAG, "PasswordHash has an invalid value at position %" PRIuz, 2 * x + 1);
return -1;
}
const BYTE val = (BYTE)((hi << 4) | lo);
hash[x] = val;
}
return 1;
}
static BOOL ntlm_compute_ntlm_v2_hash(NTLM_CONTEXT* context, BYTE* hash)
{
SSPI_CREDENTIALS* credentials = nullptr;
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)
{
return NTOWFv2FromHashW(context->NtlmHash, (LPWSTR)credentials->identity.User,
credentials->identity.UserLength * 2,
(LPWSTR)credentials->identity.Domain,
credentials->identity.DomainLength * 2, 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, sizeof(context->NtlmHash)) < 0)
return FALSE;
return NTOWFv2FromHashW(context->NtlmHash, (LPWSTR)credentials->identity.User,
credentials->identity.UserLength * 2,
(LPWSTR)credentials->identity.Domain,
credentials->identity.DomainLength * 2, hash);
}
else if (credentials->identity.Password)
{
return 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, hash);
}
else if (context->HashCallback)
{
SecBuffer proofValue = WINPR_C_ARRAY_INIT;
SecBuffer micValue = WINPR_C_ARRAY_INIT;
if (ntlm_computeProofValue(context, &proofValue) != SEC_E_OK)
return FALSE;
if (ntlm_computeMicValue(context, &micValue) != SEC_E_OK)
{
sspi_SecBufferFree(&proofValue);
return FALSE;
}
const SECURITY_STATUS ret = context->HashCallback(
context->HashCallbackArg, &credentials->identity, &proofValue,
context->EncryptedRandomSessionKey, context->AUTHENTICATE_MESSAGE.MessageIntegrityCheck,
&micValue, hash);
sspi_SecBufferFree(&proofValue);
sspi_SecBufferFree(&micValue);
return ret == SEC_E_OK;
}
else if (context->UseSamFileDatabase)
{
return ntlm_fetch_ntlm_v2_hash(context, hash);
}
return TRUE;
}
SECURITY_STATUS ntlm_compute_lm_v2_response(NTLM_CONTEXT* context)
{
BYTE* response = nullptr;
BYTE value[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(context);
if (context->LmCompatibilityLevel < 2)
{
if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24))
return SEC_E_INSUFFICIENT_MEMORY;
ZeroMemory(context->LmChallengeResponse.pvBuffer, 24);
return SEC_E_OK;
}
/* Compute the NTLMv2 hash */
if (!ntlm_compute_ntlm_v2_hash(context, context->NtlmV2Hash))
return SEC_E_NO_CREDENTIALS;
/* Concatenate the server and client challenges */
CopyMemory(value, context->ServerChallenge, 8);
CopyMemory(&value[8], context->ClientChallenge, 8);
if (!sspi_SecBufferAlloc(&context->LmChallengeResponse, 24))
return SEC_E_INSUFFICIENT_MEMORY;
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, 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 SEC_E_OK;
}
/**
* 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
*/
SECURITY_STATUS ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context)
{
SecBuffer ntlm_v2_temp = WINPR_C_ARRAY_INIT;
SecBuffer ntlm_v2_temp_chal = WINPR_C_ARRAY_INIT;
WINPR_ASSERT(context);
PSecBuffer TargetInfo = &context->ChallengeTargetInfo;
SECURITY_STATUS ret = SEC_E_INSUFFICIENT_MEMORY;
if (!sspi_SecBufferAlloc(&ntlm_v2_temp, TargetInfo->cbBuffer + 28))
goto exit;
ZeroMemory(ntlm_v2_temp.pvBuffer, ntlm_v2_temp.cbBuffer);
{
BYTE* blob = (BYTE*)ntlm_v2_temp.pvBuffer;
/* Compute the NTLMv2 hash */
ret = SEC_E_NO_CREDENTIALS;
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 */
ret = SEC_E_INSUFFICIENT_MEMORY;
if (!sspi_SecBufferAlloc(&ntlm_v2_temp_chal, ntlm_v2_temp.cbBuffer + 8))
goto exit;
{
BYTE* 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;
{
BYTE* 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 = SEC_E_OK;
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
*/
BOOL ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext)
{
WINPR_RC4_CTX* rc4 = winpr_RC4_New(key, 16);
if (!rc4)
return FALSE;
const BOOL rc = winpr_RC4_Update(rc4, length, plaintext, ciphertext);
winpr_RC4_Free(rc4);
return rc;
}
/**
* Generate client challenge (8-byte nonce).
* @param context A pointer to the NTLM context
*/
BOOL 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)
return TRUE;
return winpr_RAND(context->ClientChallenge, sizeof(context->ClientChallenge)) >= 0;
}
/**
* Generate server challenge (8-byte nonce).
* @param context A pointer to the NTLM context
*/
BOOL ntlm_generate_server_challenge(NTLM_CONTEXT* context)
{
WINPR_ASSERT(context);
if (memcmp(context->ServerChallenge, NTLM_NULL_BUFFER, sizeof(context->ServerChallenge)) != 0)
return TRUE;
return winpr_RAND(context->ServerChallenge, sizeof(context->ServerChallenge)) >= 0;
}
/**
* Generate KeyExchangeKey (the 128-bit SessionBaseKey). msdn{cc236710}
* @param context A pointer to the NTLM context
*/
BOOL 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));
return TRUE;
}
/**
* Generate RandomSessionKey (16-byte nonce).
* @param context A pointer to the NTLM context
*/
BOOL ntlm_generate_random_session_key(NTLM_CONTEXT* context)
{
WINPR_ASSERT(context);
return winpr_RAND(context->RandomSessionKey, sizeof(context->RandomSessionKey)) >= 0;
}
/**
* Generate ExportedSessionKey (the RandomSessionKey, exported)
* @param context A pointer to the NTLM context
*/
BOOL ntlm_generate_exported_session_key(NTLM_CONTEXT* context)
{
WINPR_ASSERT(context);
WINPR_ASSERT(sizeof(context->ExportedSessionKey) >= sizeof(context->RandomSessionKey));
CopyMemory(context->ExportedSessionKey, context->RandomSessionKey,
sizeof(context->ExportedSessionKey));
return TRUE;
}
/**
* Encrypt RandomSessionKey (RC4-encrypted RandomSessionKey, using KeyExchangeKey as the key).
* @param context A pointer to the NTLM context
*/
BOOL ntlm_encrypt_random_session_key(NTLM_CONTEXT* context)
{
/* In NTLMv2, EncryptedRandomSessionKey is the ExportedSessionKey RC4-encrypted with the
* KeyExchangeKey */
WINPR_ASSERT(context);
return 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
*/
BOOL 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));
return 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));
}
return TRUE;
}
/**
* 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 = nullptr;
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))
goto fail;
if (!winpr_HMAC_Update(hmac, (BYTE*)context->NegotiateMessage.pvBuffer,
context->NegotiateMessage.cbBuffer))
goto fail;
if (!winpr_HMAC_Update(hmac, (BYTE*)context->ChallengeMessage.pvBuffer,
context->ChallengeMessage.cbBuffer))
goto fail;
if (context->MessageIntegrityCheckOffset > 0)
{
const BYTE* auth = (BYTE*)context->AuthenticateMessage.pvBuffer;
const BYTE data[WINPR_MD5_DIGEST_LENGTH] = WINPR_C_ARRAY_INIT;
const size_t rest = context->MessageIntegrityCheckOffset + sizeof(data);
WINPR_ASSERT(rest <= context->AuthenticateMessage.cbBuffer);
if (!winpr_HMAC_Update(hmac, &auth[0], context->MessageIntegrityCheckOffset))
goto fail;
if (!winpr_HMAC_Update(hmac, data, sizeof(data)))
goto fail;
if (!winpr_HMAC_Update(hmac, &auth[rest], context->AuthenticateMessage.cbBuffer - rest))
goto fail;
}
else
{
if (!winpr_HMAC_Update(hmac, (BYTE*)context->AuthenticateMessage.pvBuffer,
context->AuthenticateMessage.cbBuffer))
goto fail;
}
rc = winpr_HMAC_Final(hmac, mic, WINPR_MD5_DIGEST_LENGTH);
fail:
winpr_HMAC_Free(hmac);
return rc;
}

View File

@@ -0,0 +1,63 @@
/**
* 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_generate_timestamp(NTLM_CONTEXT* context);
SECURITY_STATUS ntlm_compute_lm_v2_response(NTLM_CONTEXT* context);
SECURITY_STATUS ntlm_compute_ntlm_v2_response(NTLM_CONTEXT* context);
BOOL ntlm_rc4k(BYTE* key, size_t length, BYTE* plaintext, BYTE* ciphertext);
BOOL ntlm_generate_client_challenge(NTLM_CONTEXT* context);
BOOL ntlm_generate_server_challenge(NTLM_CONTEXT* context);
BOOL ntlm_generate_key_exchange_key(NTLM_CONTEXT* context);
BOOL ntlm_generate_random_session_key(NTLM_CONTEXT* context);
BOOL ntlm_generate_exported_session_key(NTLM_CONTEXT* context);
BOOL ntlm_encrypt_random_session_key(NTLM_CONTEXT* context);
BOOL 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 */

View File

@@ -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

File diff suppressed because it is too large Load Diff

View File

@@ -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, SecBuffer* buffer);
SECURITY_STATUS ntlm_write_NegotiateMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
SECURITY_STATUS ntlm_read_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
SECURITY_STATUS ntlm_write_ChallengeMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
SECURITY_STATUS ntlm_read_AuthenticateMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
SECURITY_STATUS ntlm_write_AuthenticateMessage(NTLM_CONTEXT* context, SecBuffer* buffer);
SECURITY_STATUS ntlm_server_AuthenticateComplete(NTLM_CONTEXT* context);
const char* ntlm_get_negotiate_string(UINT32 flag);
#endif /* WINPR_SSPI_NTLM_MESSAGE_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,68 @@
/**
* WinPR: Windows Portable Runtime
* Negotiate Security Package
*
* Copyright 2011-2012 Jiten Pathy
*
* 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_NEGOTIATE_PRIVATE_H
#define WINPR_SSPI_NEGOTIATE_PRIVATE_H
#include <winpr/sspi.h>
#include "../sspi.h"
#define NTLM_OID "1.3.6.1.4.1.311.2.2.10"
typedef enum
{
NEGOTIATE_STATE_INITIAL,
NEGOTIATE_STATE_FINAL_OPTIMISTIC,
NEGOTIATE_STATE_NEGORESP,
NEGOTIATE_STATE_MIC,
NEGOTIATE_STATE_FINAL,
} NEGOTIATE_STATE;
typedef struct Mech_st Mech;
typedef struct
{
NEGOTIATE_STATE state;
CtxtHandle sub_context;
SecBuffer mechTypes;
const Mech* mech;
BOOL mic;
BOOL spnego;
} NEGOTIATE_CONTEXT;
static inline NEGOTIATE_CONTEXT NEGOTIATE_CONTEXT_init(void)
{
const NEGOTIATE_CONTEXT empty = { .state = NEGOTIATE_STATE_INITIAL,
.sub_context = { 0 },
.mechTypes = { 0 },
.mech = nullptr,
.mic = FALSE,
.spnego = FALSE };
return empty;
}
extern const SecPkgInfoA NEGOTIATE_SecPkgInfoA;
extern const SecPkgInfoW NEGOTIATE_SecPkgInfoW;
extern const SecurityFunctionTableA NEGOTIATE_SecurityFunctionTableA;
extern const SecurityFunctionTableW NEGOTIATE_SecurityFunctionTableW;
BOOL NEGOTIATE_init(void);
#endif /* WINPR_SSPI_NEGOTIATE_PRIVATE_H */

View File

@@ -0,0 +1,475 @@
/**
* WinPR: Windows Portable Runtime
* Schannel Security Package
*
* Copyright 2012-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/sspi.h>
#include "schannel.h"
#include "../sspi.h"
#include "../../log.h"
static char* SCHANNEL_PACKAGE_NAME = "Schannel";
#define TAG WINPR_TAG("sspi.Schannel")
SCHANNEL_CONTEXT* schannel_ContextNew(void)
{
SCHANNEL_CONTEXT* context = nullptr;
context = (SCHANNEL_CONTEXT*)calloc(1, sizeof(SCHANNEL_CONTEXT));
if (!context)
return nullptr;
context->openssl = schannel_openssl_new();
if (!context->openssl)
{
free(context);
return nullptr;
}
return context;
}
void schannel_ContextFree(SCHANNEL_CONTEXT* context)
{
if (!context)
return;
schannel_openssl_free(context->openssl);
free(context);
}
static SCHANNEL_CREDENTIALS* schannel_CredentialsNew(void)
{
SCHANNEL_CREDENTIALS* credentials = nullptr;
credentials = (SCHANNEL_CREDENTIALS*)calloc(1, sizeof(SCHANNEL_CREDENTIALS));
return credentials;
}
static void schannel_CredentialsFree(SCHANNEL_CREDENTIALS* credentials)
{
free(credentials);
}
static ALG_ID schannel_SupportedAlgs[] = { CALG_AES_128,
CALG_AES_256,
CALG_RC4,
CALG_DES,
CALG_3DES,
CALG_MD5,
CALG_SHA1,
CALG_SHA_256,
CALG_SHA_384,
CALG_SHA_512,
CALG_RSA_SIGN,
CALG_DH_EPHEM,
(ALG_CLASS_KEY_EXCHANGE | ALG_TYPE_RESERVED7 |
6), /* what is this? */
CALG_DSS_SIGN,
CALG_ECDSA };
static SECURITY_STATUS SEC_ENTRY schannel_QueryCredentialsAttributesW(
WINPR_ATTR_UNUSED PCredHandle phCredential, ULONG ulAttribute, void* pBuffer)
{
if (ulAttribute == SECPKG_ATTR_SUPPORTED_ALGS)
{
PSecPkgCred_SupportedAlgs SupportedAlgs = (PSecPkgCred_SupportedAlgs)pBuffer;
SupportedAlgs->cSupportedAlgs = sizeof(schannel_SupportedAlgs) / sizeof(ALG_ID);
SupportedAlgs->palgSupportedAlgs = (ALG_ID*)schannel_SupportedAlgs;
return SEC_E_OK;
}
else if (ulAttribute == SECPKG_ATTR_CIPHER_STRENGTHS)
{
PSecPkgCred_CipherStrengths CipherStrengths = (PSecPkgCred_CipherStrengths)pBuffer;
CipherStrengths->dwMinimumCipherStrength = 40;
CipherStrengths->dwMaximumCipherStrength = 256;
return SEC_E_OK;
}
else if (ulAttribute == SECPKG_ATTR_SUPPORTED_PROTOCOLS)
{
PSecPkgCred_SupportedProtocols SupportedProtocols = (PSecPkgCred_SupportedProtocols)pBuffer;
/* Observed SupportedProtocols: 0x208A0 */
SupportedProtocols->grbitProtocol = (SP_PROT_CLIENTS | SP_PROT_SERVERS);
return SEC_E_OK;
}
WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY schannel_QueryCredentialsAttributesA(PCredHandle phCredential,
ULONG ulAttribute,
void* pBuffer)
{
return schannel_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
}
static SECURITY_STATUS SEC_ENTRY schannel_AcquireCredentialsHandleW(
WINPR_ATTR_UNUSED SEC_WCHAR* pszPrincipal, WINPR_ATTR_UNUSED SEC_WCHAR* pszPackage,
ULONG fCredentialUse, WINPR_ATTR_UNUSED void* pvLogonID, void* pAuthData,
WINPR_ATTR_UNUSED SEC_GET_KEY_FN pGetKeyFn, WINPR_ATTR_UNUSED void* pvGetKeyArgument,
PCredHandle phCredential, WINPR_ATTR_UNUSED PTimeStamp ptsExpiry)
{
SCHANNEL_CREDENTIALS* credentials = nullptr;
if (fCredentialUse == SECPKG_CRED_OUTBOUND)
{
SCHANNEL_CRED* cred = nullptr;
credentials = schannel_CredentialsNew();
credentials->fCredentialUse = fCredentialUse;
cred = (SCHANNEL_CRED*)pAuthData;
if (cred)
{
CopyMemory(&credentials->cred, cred, sizeof(SCHANNEL_CRED));
}
sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials);
sspi_SecureHandleSetUpperPointer(phCredential, (void*)SCHANNEL_PACKAGE_NAME);
return SEC_E_OK;
}
else if (fCredentialUse == SECPKG_CRED_INBOUND)
{
credentials = schannel_CredentialsNew();
credentials->fCredentialUse = fCredentialUse;
sspi_SecureHandleSetLowerPointer(phCredential, (void*)credentials);
sspi_SecureHandleSetUpperPointer(phCredential, (void*)SCHANNEL_PACKAGE_NAME);
return SEC_E_OK;
}
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY schannel_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 = 0;
SEC_WCHAR* pszPrincipalW = nullptr;
SEC_WCHAR* pszPackageW = nullptr;
if (pszPrincipal)
pszPrincipalW = ConvertUtf8ToWCharAlloc(pszPrincipal, nullptr);
if (pszPackage)
pszPackageW = ConvertUtf8ToWCharAlloc(pszPackage, nullptr);
status = schannel_AcquireCredentialsHandleW(pszPrincipalW, pszPackageW, fCredentialUse,
pvLogonID, pAuthData, pGetKeyFn, pvGetKeyArgument,
phCredential, ptsExpiry);
free(pszPrincipalW);
free(pszPackageW);
return status;
}
static SECURITY_STATUS SEC_ENTRY schannel_FreeCredentialsHandle(PCredHandle phCredential)
{
SCHANNEL_CREDENTIALS* credentials = nullptr;
if (!phCredential)
return SEC_E_INVALID_HANDLE;
credentials = (SCHANNEL_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
if (!credentials)
return SEC_E_INVALID_HANDLE;
schannel_CredentialsFree(credentials);
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY schannel_InitializeSecurityContextW(
PCredHandle phCredential, PCtxtHandle phContext, WINPR_ATTR_UNUSED SEC_WCHAR* pszTargetName,
WINPR_ATTR_UNUSED ULONG fContextReq, WINPR_ATTR_UNUSED ULONG Reserved1,
WINPR_ATTR_UNUSED ULONG TargetDataRep, PSecBufferDesc pInput, WINPR_ATTR_UNUSED ULONG Reserved2,
PCtxtHandle phNewContext, PSecBufferDesc pOutput, WINPR_ATTR_UNUSED PULONG pfContextAttr,
WINPR_ATTR_UNUSED PTimeStamp ptsExpiry)
{
SECURITY_STATUS status = 0;
SCHANNEL_CONTEXT* context = nullptr;
SCHANNEL_CREDENTIALS* credentials = nullptr;
/* behave like windows SSPIs that don't want empty context */
if (phContext && !phContext->dwLower && !phContext->dwUpper)
return SEC_E_INVALID_HANDLE;
context = sspi_SecureHandleGetLowerPointer(phContext);
if (!context)
{
context = schannel_ContextNew();
if (!context)
return SEC_E_INSUFFICIENT_MEMORY;
credentials = (SCHANNEL_CREDENTIALS*)sspi_SecureHandleGetLowerPointer(phCredential);
context->server = FALSE;
CopyMemory(&context->cred, &credentials->cred, sizeof(SCHANNEL_CRED));
sspi_SecureHandleSetLowerPointer(phNewContext, context);
sspi_SecureHandleSetUpperPointer(phNewContext, (void*)SCHANNEL_PACKAGE_NAME);
schannel_openssl_client_init(context->openssl);
}
status = schannel_openssl_client_process_tokens(context->openssl, pInput, pOutput);
return status;
}
static SECURITY_STATUS SEC_ENTRY schannel_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 = nullptr;
if (pszTargetName != nullptr)
{
pszTargetNameW = ConvertUtf8ToWCharAlloc(pszTargetName, nullptr);
if (!pszTargetNameW)
return SEC_E_INSUFFICIENT_MEMORY;
}
status = schannel_InitializeSecurityContextW(
phCredential, phContext, pszTargetNameW, fContextReq, Reserved1, TargetDataRep, pInput,
Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry);
free(pszTargetNameW);
return status;
}
static SECURITY_STATUS SEC_ENTRY schannel_AcceptSecurityContext(
WINPR_ATTR_UNUSED PCredHandle phCredential, PCtxtHandle phContext, PSecBufferDesc pInput,
WINPR_ATTR_UNUSED ULONG fContextReq, WINPR_ATTR_UNUSED ULONG TargetDataRep,
PCtxtHandle phNewContext, PSecBufferDesc pOutput, WINPR_ATTR_UNUSED PULONG pfContextAttr,
WINPR_ATTR_UNUSED PTimeStamp ptsTimeStamp)
{
SECURITY_STATUS status = 0;
SCHANNEL_CONTEXT* context = nullptr;
/* behave like windows SSPIs that don't want empty context */
if (phContext && !phContext->dwLower && !phContext->dwUpper)
return SEC_E_INVALID_HANDLE;
context = (SCHANNEL_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
if (!context)
{
context = schannel_ContextNew();
if (!context)
return SEC_E_INSUFFICIENT_MEMORY;
context->server = TRUE;
sspi_SecureHandleSetLowerPointer(phNewContext, context);
sspi_SecureHandleSetUpperPointer(phNewContext, (void*)SCHANNEL_PACKAGE_NAME);
schannel_openssl_server_init(context->openssl);
}
status = schannel_openssl_server_process_tokens(context->openssl, pInput, pOutput);
return status;
}
static SECURITY_STATUS SEC_ENTRY schannel_DeleteSecurityContext(PCtxtHandle phContext)
{
SCHANNEL_CONTEXT* context = nullptr;
context = (SCHANNEL_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
if (!context)
return SEC_E_INVALID_HANDLE;
schannel_ContextFree(context);
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY schannel_QueryContextAttributes(PCtxtHandle phContext,
ULONG ulAttribute, void* pBuffer)
{
if (!phContext)
return SEC_E_INVALID_HANDLE;
if (!pBuffer)
return SEC_E_INSUFFICIENT_MEMORY;
if (ulAttribute == SECPKG_ATTR_SIZES)
{
SecPkgContext_Sizes* Sizes = (SecPkgContext_Sizes*)pBuffer;
Sizes->cbMaxToken = 0x6000;
Sizes->cbMaxSignature = 16;
Sizes->cbBlockSize = 0;
Sizes->cbSecurityTrailer = 16;
return SEC_E_OK;
}
else if (ulAttribute == SECPKG_ATTR_STREAM_SIZES)
{
SecPkgContext_StreamSizes* StreamSizes = (SecPkgContext_StreamSizes*)pBuffer;
StreamSizes->cbHeader = 5;
StreamSizes->cbTrailer = 36;
StreamSizes->cbMaximumMessage = 0x4000;
StreamSizes->cBuffers = 4;
StreamSizes->cbBlockSize = 16;
return SEC_E_OK;
}
WLog_ERR(TAG, "TODO: Implement ulAttribute=%08" PRIx32, ulAttribute);
return SEC_E_UNSUPPORTED_FUNCTION;
}
static SECURITY_STATUS SEC_ENTRY schannel_MakeSignature(WINPR_ATTR_UNUSED PCtxtHandle phContext,
WINPR_ATTR_UNUSED ULONG fQOP,
WINPR_ATTR_UNUSED PSecBufferDesc pMessage,
WINPR_ATTR_UNUSED ULONG MessageSeqNo)
{
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY schannel_VerifySignature(WINPR_ATTR_UNUSED PCtxtHandle phContext,
WINPR_ATTR_UNUSED PSecBufferDesc pMessage,
WINPR_ATTR_UNUSED ULONG MessageSeqNo,
WINPR_ATTR_UNUSED ULONG* pfQOP)
{
return SEC_E_OK;
}
static SECURITY_STATUS SEC_ENTRY schannel_EncryptMessage(WINPR_ATTR_UNUSED PCtxtHandle phContext,
WINPR_ATTR_UNUSED ULONG fQOP,
PSecBufferDesc pMessage,
WINPR_ATTR_UNUSED ULONG MessageSeqNo)
{
SECURITY_STATUS status = 0;
SCHANNEL_CONTEXT* context = nullptr;
context = (SCHANNEL_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
if (!context)
return SEC_E_INVALID_HANDLE;
status = schannel_openssl_encrypt_message(context->openssl, pMessage);
return status;
}
static SECURITY_STATUS SEC_ENTRY schannel_DecryptMessage(PCtxtHandle phContext,
PSecBufferDesc pMessage,
WINPR_ATTR_UNUSED ULONG MessageSeqNo,
WINPR_ATTR_UNUSED ULONG* pfQOP)
{
SECURITY_STATUS status = 0;
SCHANNEL_CONTEXT* context = nullptr;
context = (SCHANNEL_CONTEXT*)sspi_SecureHandleGetLowerPointer(phContext);
if (!context)
return SEC_E_INVALID_HANDLE;
status = schannel_openssl_decrypt_message(context->openssl, pMessage);
return status;
}
const SecurityFunctionTableA SCHANNEL_SecurityFunctionTableA = {
3, /* dwVersion */
nullptr, /* EnumerateSecurityPackages */
schannel_QueryCredentialsAttributesA, /* QueryCredentialsAttributes */
schannel_AcquireCredentialsHandleA, /* AcquireCredentialsHandle */
schannel_FreeCredentialsHandle, /* FreeCredentialsHandle */
nullptr, /* Reserved2 */
schannel_InitializeSecurityContextA, /* InitializeSecurityContext */
schannel_AcceptSecurityContext, /* AcceptSecurityContext */
nullptr, /* CompleteAuthToken */
schannel_DeleteSecurityContext, /* DeleteSecurityContext */
nullptr, /* ApplyControlToken */
schannel_QueryContextAttributes, /* QueryContextAttributes */
nullptr, /* ImpersonateSecurityContext */
nullptr, /* RevertSecurityContext */
schannel_MakeSignature, /* MakeSignature */
schannel_VerifySignature, /* VerifySignature */
nullptr, /* FreeContextBuffer */
nullptr, /* QuerySecurityPackageInfo */
nullptr, /* Reserved3 */
nullptr, /* Reserved4 */
nullptr, /* ExportSecurityContext */
nullptr, /* ImportSecurityContext */
nullptr, /* AddCredentials */
nullptr, /* Reserved8 */
nullptr, /* QuerySecurityContextToken */
schannel_EncryptMessage, /* EncryptMessage */
schannel_DecryptMessage, /* DecryptMessage */
nullptr, /* SetContextAttributes */
nullptr, /* SetCredentialsAttributes */
};
const SecurityFunctionTableW SCHANNEL_SecurityFunctionTableW = {
3, /* dwVersion */
nullptr, /* EnumerateSecurityPackages */
schannel_QueryCredentialsAttributesW, /* QueryCredentialsAttributes */
schannel_AcquireCredentialsHandleW, /* AcquireCredentialsHandle */
schannel_FreeCredentialsHandle, /* FreeCredentialsHandle */
nullptr, /* Reserved2 */
schannel_InitializeSecurityContextW, /* InitializeSecurityContext */
schannel_AcceptSecurityContext, /* AcceptSecurityContext */
nullptr, /* CompleteAuthToken */
schannel_DeleteSecurityContext, /* DeleteSecurityContext */
nullptr, /* ApplyControlToken */
schannel_QueryContextAttributes, /* QueryContextAttributes */
nullptr, /* ImpersonateSecurityContext */
nullptr, /* RevertSecurityContext */
schannel_MakeSignature, /* MakeSignature */
schannel_VerifySignature, /* VerifySignature */
nullptr, /* FreeContextBuffer */
nullptr, /* QuerySecurityPackageInfo */
nullptr, /* Reserved3 */
nullptr, /* Reserved4 */
nullptr, /* ExportSecurityContext */
nullptr, /* ImportSecurityContext */
nullptr, /* AddCredentials */
nullptr, /* Reserved8 */
nullptr, /* QuerySecurityContextToken */
schannel_EncryptMessage, /* EncryptMessage */
schannel_DecryptMessage, /* DecryptMessage */
nullptr, /* SetContextAttributes */
nullptr, /* SetCredentialsAttributes */
};
const SecPkgInfoA SCHANNEL_SecPkgInfoA = {
0x000107B3, /* fCapabilities */
1, /* wVersion */
0x000E, /* wRPCID */
SCHANNEL_CB_MAX_TOKEN, /* cbMaxToken */
"Schannel", /* Name */
"Schannel Security Package" /* Comment */
};
static WCHAR SCHANNEL_SecPkgInfoW_NameBuffer[32] = WINPR_C_ARRAY_INIT;
static WCHAR SCHANNEL_SecPkgInfoW_CommentBuffer[32] = WINPR_C_ARRAY_INIT;
const SecPkgInfoW SCHANNEL_SecPkgInfoW = {
0x000107B3, /* fCapabilities */
1, /* wVersion */
0x000E, /* wRPCID */
SCHANNEL_CB_MAX_TOKEN, /* cbMaxToken */
SCHANNEL_SecPkgInfoW_NameBuffer, /* Name */
SCHANNEL_SecPkgInfoW_CommentBuffer /* Comment */
};
BOOL SCHANNEL_init(void)
{
InitializeConstWCharFromUtf8(SCHANNEL_SecPkgInfoA.Name, SCHANNEL_SecPkgInfoW_NameBuffer,
ARRAYSIZE(SCHANNEL_SecPkgInfoW_NameBuffer));
InitializeConstWCharFromUtf8(SCHANNEL_SecPkgInfoA.Comment, SCHANNEL_SecPkgInfoW_CommentBuffer,
ARRAYSIZE(SCHANNEL_SecPkgInfoW_CommentBuffer));
return TRUE;
}

View File

@@ -0,0 +1,53 @@
/**
* WinPR: Windows Portable Runtime
* Schannel Security Package
*
* Copyright 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_SCHANNEL_PRIVATE_H
#define WINPR_SSPI_SCHANNEL_PRIVATE_H
#include <winpr/sspi.h>
#include <winpr/schannel.h>
#include "../sspi.h"
#include "schannel_openssl.h"
typedef struct
{
SCHANNEL_CRED cred;
ULONG fCredentialUse;
} SCHANNEL_CREDENTIALS;
typedef struct
{
BOOL server;
SCHANNEL_CRED cred;
SCHANNEL_OPENSSL* openssl;
} SCHANNEL_CONTEXT;
SCHANNEL_CONTEXT* schannel_ContextNew(void);
void schannel_ContextFree(SCHANNEL_CONTEXT* context);
extern const SecPkgInfoA SCHANNEL_SecPkgInfoA;
extern const SecPkgInfoW SCHANNEL_SecPkgInfoW;
extern const SecurityFunctionTableA SCHANNEL_SecurityFunctionTableA;
extern const SecurityFunctionTableW SCHANNEL_SecurityFunctionTableW;
BOOL SCHANNEL_init(void);
#endif /* WINPR_SSPI_SCHANNEL_PRIVATE_H */

View File

@@ -0,0 +1,657 @@
/**
* WinPR: Windows Portable Runtime
* Schannel Security Package (OpenSSL)
*
* Copyright 2012-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 "schannel_openssl.h"
#ifdef WITH_OPENSSL
#include <winpr/crt.h>
#include <winpr/sspi.h>
#include <winpr/ssl.h>
#include <winpr/print.h>
#include <winpr/crypto.h>
#include <openssl/ssl.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#define LIMIT_INTMAX(a) ((a) > INT32_MAX) ? INT32_MAX : (int)(a)
struct S_SCHANNEL_OPENSSL
{
SSL* ssl;
SSL_CTX* ctx;
BOOL connected;
BIO* bioRead;
BIO* bioWrite;
BYTE* ReadBuffer;
BYTE* WriteBuffer;
};
#include "../../log.h"
#define TAG WINPR_TAG("sspi.schannel")
static char* openssl_get_ssl_error_string(int ssl_error)
{
switch (ssl_error)
{
case SSL_ERROR_ZERO_RETURN:
return "SSL_ERROR_ZERO_RETURN";
case SSL_ERROR_WANT_READ:
return "SSL_ERROR_WANT_READ";
case SSL_ERROR_WANT_WRITE:
return "SSL_ERROR_WANT_WRITE";
case SSL_ERROR_SYSCALL:
return "SSL_ERROR_SYSCALL";
case SSL_ERROR_SSL:
return "SSL_ERROR_SSL";
default:
break;
}
return "SSL_ERROR_UNKNOWN";
}
static void schannel_context_cleanup(SCHANNEL_OPENSSL* context)
{
WINPR_ASSERT(context);
free(context->ReadBuffer);
context->ReadBuffer = nullptr;
if (context->bioWrite)
BIO_free_all(context->bioWrite);
context->bioWrite = nullptr;
if (context->bioRead)
BIO_free_all(context->bioRead);
context->bioRead = nullptr;
if (context->ssl)
SSL_free(context->ssl);
context->ssl = nullptr;
if (context->ctx)
SSL_CTX_free(context->ctx);
context->ctx = nullptr;
}
static const SSL_METHOD* get_method(BOOL server)
{
if (server)
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
return SSLv23_server_method();
#else
return TLS_server_method();
#endif
}
else
{
#if OPENSSL_VERSION_NUMBER < 0x10100000L || defined(LIBRESSL_VERSION_NUMBER)
return SSLv23_client_method();
#else
return TLS_client_method();
#endif
}
}
int schannel_openssl_client_init(SCHANNEL_OPENSSL* context)
{
int status = 0;
long options = 0;
context->ctx = SSL_CTX_new(get_method(FALSE));
if (!context->ctx)
{
WLog_ERR(TAG, "SSL_CTX_new failed");
return -1;
}
/**
* SSL_OP_NO_COMPRESSION:
*
* The Microsoft RDP server does not advertise support
* for TLS compression, but alternative servers may support it.
* This was observed between early versions of the FreeRDP server
* and the FreeRDP client, and caused major performance issues,
* which is why we're disabling it.
*/
#ifdef SSL_OP_NO_COMPRESSION
options |= SSL_OP_NO_COMPRESSION;
#endif
/**
* SSL_OP_TLS_BLOCK_PADDING_BUG:
*
* The Microsoft RDP server does *not* support TLS padding.
* It absolutely needs to be disabled otherwise it won't work.
*/
options |= SSL_OP_TLS_BLOCK_PADDING_BUG;
/**
* SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS:
*
* Just like TLS padding, the Microsoft RDP server does not
* support empty fragments. This needs to be disabled.
*/
options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
SSL_CTX_set_options(context->ctx, WINPR_ASSERTING_INT_CAST(uint64_t, options));
context->ssl = SSL_new(context->ctx);
if (!context->ssl)
{
WLog_ERR(TAG, "SSL_new failed");
goto fail;
}
context->bioRead = BIO_new(BIO_s_mem());
if (!context->bioRead)
{
WLog_ERR(TAG, "BIO_new failed");
goto fail;
}
status = BIO_set_write_buf_size(context->bioRead, SCHANNEL_CB_MAX_TOKEN);
if (status != 1)
{
WLog_ERR(TAG, "BIO_set_write_buf_size on bioRead failed");
goto fail;
}
context->bioWrite = BIO_new(BIO_s_mem());
if (!context->bioWrite)
{
WLog_ERR(TAG, "BIO_new failed");
goto fail;
}
status = BIO_set_write_buf_size(context->bioWrite, SCHANNEL_CB_MAX_TOKEN);
if (status != 1)
{
WLog_ERR(TAG, "BIO_set_write_buf_size on bioWrite failed");
goto fail;
}
status = BIO_make_bio_pair(context->bioRead, context->bioWrite);
if (status != 1)
{
WLog_ERR(TAG, "BIO_make_bio_pair failed");
goto fail;
}
SSL_set_bio(context->ssl, context->bioRead, context->bioWrite);
context->ReadBuffer = (BYTE*)malloc(SCHANNEL_CB_MAX_TOKEN);
if (!context->ReadBuffer)
{
WLog_ERR(TAG, "Failed to allocate ReadBuffer");
goto fail;
}
context->WriteBuffer = (BYTE*)malloc(SCHANNEL_CB_MAX_TOKEN);
if (!context->WriteBuffer)
{
WLog_ERR(TAG, "Failed to allocate ReadBuffer");
goto fail;
}
return 0;
fail:
schannel_context_cleanup(context);
return -1;
}
int schannel_openssl_server_init(SCHANNEL_OPENSSL* context)
{
int status = 0;
unsigned long options = 0;
context->ctx = SSL_CTX_new(get_method(TRUE));
if (!context->ctx)
{
WLog_ERR(TAG, "SSL_CTX_new failed");
return -1;
}
/*
* SSL_OP_NO_SSLv2:
*
* We only want SSLv3 and TLSv1, so disable SSLv2.
* SSLv3 is used by, eg. Microsoft RDC for Mac OS X.
*/
options |= SSL_OP_NO_SSLv2;
/**
* SSL_OP_NO_COMPRESSION:
*
* The Microsoft RDP server does not advertise support
* for TLS compression, but alternative servers may support it.
* This was observed between early versions of the FreeRDP server
* and the FreeRDP client, and caused major performance issues,
* which is why we're disabling it.
*/
#ifdef SSL_OP_NO_COMPRESSION
options |= SSL_OP_NO_COMPRESSION;
#endif
/**
* SSL_OP_TLS_BLOCK_PADDING_BUG:
*
* The Microsoft RDP server does *not* support TLS padding.
* It absolutely needs to be disabled otherwise it won't work.
*/
options |= SSL_OP_TLS_BLOCK_PADDING_BUG;
/**
* SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS:
*
* Just like TLS padding, the Microsoft RDP server does not
* support empty fragments. This needs to be disabled.
*/
options |= SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS;
SSL_CTX_set_options(context->ctx, options);
#if defined(WITH_DEBUG_SCHANNEL)
if (SSL_CTX_use_RSAPrivateKey_file(context->ctx, "/tmp/localhost.key", SSL_FILETYPE_PEM) <= 0)
{
WLog_ERR(TAG, "SSL_CTX_use_RSAPrivateKey_file failed");
goto fail;
}
#endif
context->ssl = SSL_new(context->ctx);
if (!context->ssl)
{
WLog_ERR(TAG, "SSL_new failed");
goto fail;
}
if (SSL_use_certificate_file(context->ssl, "/tmp/localhost.crt", SSL_FILETYPE_PEM) <= 0)
{
WLog_ERR(TAG, "SSL_use_certificate_file failed");
goto fail;
}
context->bioRead = BIO_new(BIO_s_mem());
if (!context->bioRead)
{
WLog_ERR(TAG, "BIO_new failed");
goto fail;
}
status = BIO_set_write_buf_size(context->bioRead, SCHANNEL_CB_MAX_TOKEN);
if (status != 1)
{
WLog_ERR(TAG, "BIO_set_write_buf_size failed for bioRead");
goto fail;
}
context->bioWrite = BIO_new(BIO_s_mem());
if (!context->bioWrite)
{
WLog_ERR(TAG, "BIO_new failed");
goto fail;
}
status = BIO_set_write_buf_size(context->bioWrite, SCHANNEL_CB_MAX_TOKEN);
if (status != 1)
{
WLog_ERR(TAG, "BIO_set_write_buf_size failed for bioWrite");
goto fail;
}
status = BIO_make_bio_pair(context->bioRead, context->bioWrite);
if (status != 1)
{
WLog_ERR(TAG, "BIO_make_bio_pair failed");
goto fail;
}
SSL_set_bio(context->ssl, context->bioRead, context->bioWrite);
context->ReadBuffer = (BYTE*)malloc(SCHANNEL_CB_MAX_TOKEN);
if (!context->ReadBuffer)
{
WLog_ERR(TAG, "Failed to allocate memory for ReadBuffer");
goto fail;
}
context->WriteBuffer = (BYTE*)malloc(SCHANNEL_CB_MAX_TOKEN);
if (!context->WriteBuffer)
{
WLog_ERR(TAG, "Failed to allocate memory for WriteBuffer");
goto fail;
}
return 0;
fail:
schannel_context_cleanup(context);
return -1;
}
SECURITY_STATUS schannel_openssl_client_process_tokens(SCHANNEL_OPENSSL* context,
PSecBufferDesc pInput,
PSecBufferDesc pOutput)
{
int status = 0;
int ssl_error = 0;
PSecBuffer pBuffer = nullptr;
if (!context->connected)
{
if (pInput)
{
if (pInput->cBuffers < 1)
return SEC_E_INVALID_TOKEN;
pBuffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
if (!pBuffer)
return SEC_E_INVALID_TOKEN;
ERR_clear_error();
status =
BIO_write(context->bioRead, pBuffer->pvBuffer, LIMIT_INTMAX(pBuffer->cbBuffer));
if (status < 0)
return SEC_E_INVALID_TOKEN;
}
status = SSL_connect(context->ssl);
if (status < 0)
{
ssl_error = SSL_get_error(context->ssl, status);
WLog_ERR(TAG, "SSL_connect error: %s", openssl_get_ssl_error_string(ssl_error));
}
if (status == 1)
context->connected = TRUE;
ERR_clear_error();
status = BIO_read(context->bioWrite, context->ReadBuffer, SCHANNEL_CB_MAX_TOKEN);
if (pOutput->cBuffers < 1)
return SEC_E_INVALID_TOKEN;
pBuffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
if (!pBuffer)
return SEC_E_INVALID_TOKEN;
if (status > 0)
{
if (pBuffer->cbBuffer < WINPR_ASSERTING_INT_CAST(uint32_t, status))
return SEC_E_INSUFFICIENT_MEMORY;
CopyMemory(pBuffer->pvBuffer, context->ReadBuffer,
WINPR_ASSERTING_INT_CAST(uint32_t, status));
pBuffer->cbBuffer = WINPR_ASSERTING_INT_CAST(uint32_t, status);
return (context->connected) ? SEC_E_OK : SEC_I_CONTINUE_NEEDED;
}
else
{
pBuffer->cbBuffer = 0;
return (context->connected) ? SEC_E_OK : SEC_I_CONTINUE_NEEDED;
}
}
return SEC_E_OK;
}
SECURITY_STATUS schannel_openssl_server_process_tokens(SCHANNEL_OPENSSL* context,
PSecBufferDesc pInput,
PSecBufferDesc pOutput)
{
int status = 0;
int ssl_error = 0;
PSecBuffer pBuffer = nullptr;
if (!context->connected)
{
if (pInput->cBuffers < 1)
return SEC_E_INVALID_TOKEN;
pBuffer = sspi_FindSecBuffer(pInput, SECBUFFER_TOKEN);
if (!pBuffer)
return SEC_E_INVALID_TOKEN;
ERR_clear_error();
status = BIO_write(context->bioRead, pBuffer->pvBuffer, LIMIT_INTMAX(pBuffer->cbBuffer));
if (status >= 0)
status = SSL_accept(context->ssl);
if (status < 0)
{
ssl_error = SSL_get_error(context->ssl, status);
WLog_ERR(TAG, "SSL_accept error: %s", openssl_get_ssl_error_string(ssl_error));
return SEC_E_INVALID_TOKEN;
}
if (status == 1)
context->connected = TRUE;
ERR_clear_error();
status = BIO_read(context->bioWrite, context->ReadBuffer, SCHANNEL_CB_MAX_TOKEN);
if (status < 0)
{
ssl_error = SSL_get_error(context->ssl, status);
WLog_ERR(TAG, "BIO_read: %s", openssl_get_ssl_error_string(ssl_error));
return SEC_E_INVALID_TOKEN;
}
if (pOutput->cBuffers < 1)
return SEC_E_INVALID_TOKEN;
pBuffer = sspi_FindSecBuffer(pOutput, SECBUFFER_TOKEN);
if (!pBuffer)
return SEC_E_INVALID_TOKEN;
if (status > 0)
{
if (pBuffer->cbBuffer < WINPR_ASSERTING_INT_CAST(uint32_t, status))
return SEC_E_INSUFFICIENT_MEMORY;
CopyMemory(pBuffer->pvBuffer, context->ReadBuffer,
WINPR_ASSERTING_INT_CAST(uint32_t, status));
pBuffer->cbBuffer = WINPR_ASSERTING_INT_CAST(uint32_t, status);
return (context->connected) ? SEC_E_OK : SEC_I_CONTINUE_NEEDED;
}
else
{
pBuffer->cbBuffer = 0;
return (context->connected) ? SEC_E_OK : SEC_I_CONTINUE_NEEDED;
}
}
return SEC_E_OK;
}
SECURITY_STATUS schannel_openssl_encrypt_message(SCHANNEL_OPENSSL* context, PSecBufferDesc pMessage)
{
int status = 0;
int ssl_error = 0;
PSecBuffer pStreamBodyBuffer = nullptr;
PSecBuffer pStreamHeaderBuffer = nullptr;
PSecBuffer pStreamTrailerBuffer = nullptr;
pStreamHeaderBuffer = sspi_FindSecBuffer(pMessage, SECBUFFER_STREAM_HEADER);
pStreamBodyBuffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA);
pStreamTrailerBuffer = sspi_FindSecBuffer(pMessage, SECBUFFER_STREAM_TRAILER);
if ((!pStreamHeaderBuffer) || (!pStreamBodyBuffer) || (!pStreamTrailerBuffer))
return SEC_E_INVALID_TOKEN;
status = SSL_write(context->ssl, pStreamBodyBuffer->pvBuffer,
LIMIT_INTMAX(pStreamBodyBuffer->cbBuffer));
if (status < 0)
{
ssl_error = SSL_get_error(context->ssl, status);
WLog_ERR(TAG, "SSL_write: %s", openssl_get_ssl_error_string(ssl_error));
}
ERR_clear_error();
status = BIO_read(context->bioWrite, context->ReadBuffer, SCHANNEL_CB_MAX_TOKEN);
if (status > 0)
{
size_t ustatus = (size_t)status;
size_t length = 0;
size_t offset = 0;
length =
(pStreamHeaderBuffer->cbBuffer > ustatus) ? ustatus : pStreamHeaderBuffer->cbBuffer;
CopyMemory(pStreamHeaderBuffer->pvBuffer, &context->ReadBuffer[offset], length);
ustatus -= length;
offset += length;
length = (pStreamBodyBuffer->cbBuffer > ustatus) ? ustatus : pStreamBodyBuffer->cbBuffer;
CopyMemory(pStreamBodyBuffer->pvBuffer, &context->ReadBuffer[offset], length);
ustatus -= length;
offset += length;
length =
(pStreamTrailerBuffer->cbBuffer > ustatus) ? ustatus : pStreamTrailerBuffer->cbBuffer;
CopyMemory(pStreamTrailerBuffer->pvBuffer, &context->ReadBuffer[offset], length);
}
return SEC_E_OK;
}
SECURITY_STATUS schannel_openssl_decrypt_message(SCHANNEL_OPENSSL* context, PSecBufferDesc pMessage)
{
int status = 0;
int length = 0;
BYTE* buffer = nullptr;
int ssl_error = 0;
PSecBuffer pBuffer = nullptr;
pBuffer = sspi_FindSecBuffer(pMessage, SECBUFFER_DATA);
if (!pBuffer)
return SEC_E_INVALID_TOKEN;
ERR_clear_error();
status = BIO_write(context->bioRead, pBuffer->pvBuffer, LIMIT_INTMAX(pBuffer->cbBuffer));
if (status > 0)
status = SSL_read(context->ssl, pBuffer->pvBuffer, LIMIT_INTMAX(pBuffer->cbBuffer));
if (status < 0)
{
ssl_error = SSL_get_error(context->ssl, status);
WLog_ERR(TAG, "SSL_read: %s", openssl_get_ssl_error_string(ssl_error));
}
length = status;
buffer = pBuffer->pvBuffer;
pMessage->pBuffers[0].BufferType = SECBUFFER_STREAM_HEADER;
pMessage->pBuffers[0].cbBuffer = 5;
pMessage->pBuffers[1].BufferType = SECBUFFER_DATA;
pMessage->pBuffers[1].pvBuffer = buffer;
pMessage->pBuffers[1].cbBuffer = WINPR_ASSERTING_INT_CAST(uint32_t, length);
pMessage->pBuffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
pMessage->pBuffers[2].cbBuffer = 36;
pMessage->pBuffers[3].BufferType = SECBUFFER_EMPTY;
pMessage->pBuffers[3].cbBuffer = 0;
return SEC_E_OK;
}
SCHANNEL_OPENSSL* schannel_openssl_new(void)
{
SCHANNEL_OPENSSL* context = nullptr;
context = (SCHANNEL_OPENSSL*)calloc(1, sizeof(SCHANNEL_OPENSSL));
if (context != nullptr)
{
winpr_InitializeSSL(WINPR_SSL_INIT_DEFAULT);
context->connected = FALSE;
}
return context;
}
void schannel_openssl_free(SCHANNEL_OPENSSL* context)
{
if (context)
{
free(context->ReadBuffer);
free(context->WriteBuffer);
free(context);
}
}
#else
int schannel_openssl_client_init(SCHANNEL_OPENSSL* context)
{
return 0;
}
int schannel_openssl_server_init(SCHANNEL_OPENSSL* context)
{
return 0;
}
SECURITY_STATUS schannel_openssl_client_process_tokens(SCHANNEL_OPENSSL* context,
PSecBufferDesc pInput,
PSecBufferDesc pOutput)
{
return SEC_E_OK;
}
SECURITY_STATUS schannel_openssl_server_process_tokens(SCHANNEL_OPENSSL* context,
PSecBufferDesc pInput,
PSecBufferDesc pOutput)
{
return SEC_E_OK;
}
SECURITY_STATUS schannel_openssl_encrypt_message(SCHANNEL_OPENSSL* context, PSecBufferDesc pMessage)
{
return SEC_E_OK;
}
SECURITY_STATUS schannel_openssl_decrypt_message(SCHANNEL_OPENSSL* context, PSecBufferDesc pMessage)
{
return SEC_E_OK;
}
SCHANNEL_OPENSSL* schannel_openssl_new(void)
{
return nullptr;
}
void schannel_openssl_free(SCHANNEL_OPENSSL* context)
{
}
#endif

View File

@@ -0,0 +1,53 @@
/**
* WinPR: Windows Portable Runtime
* Schannel Security Package (OpenSSL)
*
* Copyright 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_SCHANNEL_OPENSSL_H
#define WINPR_SSPI_SCHANNEL_OPENSSL_H
#include <winpr/sspi.h>
#include "../sspi.h"
/* OpenSSL includes windows.h */
#include <winpr/windows.h>
typedef struct S_SCHANNEL_OPENSSL SCHANNEL_OPENSSL;
int schannel_openssl_client_init(SCHANNEL_OPENSSL* context);
int schannel_openssl_server_init(SCHANNEL_OPENSSL* context);
SECURITY_STATUS schannel_openssl_client_process_tokens(SCHANNEL_OPENSSL* context,
PSecBufferDesc pInput,
PSecBufferDesc pOutput);
SECURITY_STATUS schannel_openssl_server_process_tokens(SCHANNEL_OPENSSL* context,
PSecBufferDesc pInput,
PSecBufferDesc pOutput);
SECURITY_STATUS schannel_openssl_encrypt_message(SCHANNEL_OPENSSL* context,
PSecBufferDesc pMessage);
SECURITY_STATUS schannel_openssl_decrypt_message(SCHANNEL_OPENSSL* context,
PSecBufferDesc pMessage);
void schannel_openssl_free(SCHANNEL_OPENSSL* context);
WINPR_ATTR_MALLOC(schannel_openssl_free, 1)
WINPR_ATTR_NODISCARD
SCHANNEL_OPENSSL* schannel_openssl_new(void);
#endif /* WINPR_SSPI_SCHANNEL_OPENSSL_H */

View File

@@ -0,0 +1,990 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Security Support Provider Interface (SSPI)
*
* Copyright 2012-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/platform.h>
#include <winpr/config.h>
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_RESERVED_ID_MACRO
WINPR_PRAGMA_DIAG_IGNORED_UNUSED_MACRO
#define _NO_KSECDD_IMPORT_ 1 // NOLINT(bugprone-reserved-identifier,cert-dcl37-c,cert-dcl51-cpp)
WINPR_PRAGMA_DIAG_POP
#include <winpr/sspi.h>
#include <winpr/crt.h>
#include <winpr/synch.h>
#include <winpr/wlog.h>
#include <winpr/library.h>
#include <winpr/environment.h>
#include "sspi.h"
#if defined(__GNUC__) || defined(__clang__)
#define IFCALLRESULT(_default_return, _cb, ...) \
__extension__({ \
if (_cb == nullptr) \
{ \
WLog_VRB("com.winpr.api", "IFCALLRESULT(" #_cb ") == nullptr"); \
} \
((_cb != nullptr) ? _cb(__VA_ARGS__) : (_default_return)); \
})
#else
#define IFCALLRESULT(_default_return, _cb, ...) \
((_cb != nullptr) ? _cb(__VA_ARGS__) : (_default_return))
#endif
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISSING_PROTOTYPES
static wLog* g_Log = nullptr;
static INIT_ONCE g_Initialized = INIT_ONCE_STATIC_INIT;
#if defined(WITH_NATIVE_SSPI)
static HMODULE g_SspiModule = nullptr;
static SecurityFunctionTableW windows_SecurityFunctionTableW = WINPR_C_ARRAY_INIT;
static SecurityFunctionTableA windows_SecurityFunctionTableA = WINPR_C_ARRAY_INIT;
#endif
static SecurityFunctionTableW* g_SspiW = nullptr;
static SecurityFunctionTableA* g_SspiA = nullptr;
#if defined(WITH_NATIVE_SSPI)
static BOOL ShouldUseNativeSspi(void);
static BOOL InitializeSspiModule_Native(void);
#endif
#if defined(WITH_NATIVE_SSPI)
BOOL ShouldUseNativeSspi(void)
{
BOOL status = FALSE;
#ifdef _WIN32
LPCSTR sspi = "WINPR_NATIVE_SSPI";
DWORD nSize;
char* env = nullptr;
nSize = GetEnvironmentVariableA(sspi, nullptr, 0);
if (!nSize)
return TRUE;
env = (LPSTR)malloc(nSize);
if (!env)
return TRUE;
if (GetEnvironmentVariableA(sspi, env, nSize) != nSize - 1)
{
free(env);
return TRUE;
}
if (strcmp(env, "0") == 0)
status = FALSE;
else
status = TRUE;
free(env);
#endif
return status;
}
#endif
#if defined(WITH_NATIVE_SSPI)
BOOL InitializeSspiModule_Native(void)
{
SecurityFunctionTableW* pSspiW = nullptr;
SecurityFunctionTableA* pSspiA = nullptr;
INIT_SECURITY_INTERFACE_W pInitSecurityInterfaceW;
INIT_SECURITY_INTERFACE_A pInitSecurityInterfaceA;
g_SspiModule = LoadLibraryA("secur32.dll");
if (!g_SspiModule)
g_SspiModule = LoadLibraryA("sspicli.dll");
if (!g_SspiModule)
return FALSE;
pInitSecurityInterfaceW =
GetProcAddressAs(g_SspiModule, "InitSecurityInterfaceW", INIT_SECURITY_INTERFACE_W);
pInitSecurityInterfaceA =
GetProcAddressAs(g_SspiModule, "InitSecurityInterfaceA", INIT_SECURITY_INTERFACE_A);
if (pInitSecurityInterfaceW)
{
pSspiW = pInitSecurityInterfaceW();
if (pSspiW)
{
g_SspiW = &windows_SecurityFunctionTableW;
CopyMemory(g_SspiW, pSspiW,
FIELD_OFFSET(SecurityFunctionTableW, SetContextAttributesW));
g_SspiW->dwVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION_3;
g_SspiW->SetContextAttributesW = GetProcAddressAs(g_SspiModule, "SetContextAttributesW",
SET_CONTEXT_ATTRIBUTES_FN_W);
g_SspiW->SetCredentialsAttributesW = GetProcAddressAs(
g_SspiModule, "SetCredentialsAttributesW", SET_CREDENTIALS_ATTRIBUTES_FN_W);
}
}
if (pInitSecurityInterfaceA)
{
pSspiA = pInitSecurityInterfaceA();
if (pSspiA)
{
g_SspiA = &windows_SecurityFunctionTableA;
CopyMemory(g_SspiA, pSspiA,
FIELD_OFFSET(SecurityFunctionTableA, SetContextAttributesA));
g_SspiA->dwVersion = SECURITY_SUPPORT_PROVIDER_INTERFACE_VERSION_3;
g_SspiA->SetContextAttributesA = GetProcAddressAs(g_SspiModule, "SetContextAttributesA",
SET_CONTEXT_ATTRIBUTES_FN_W);
g_SspiA->SetCredentialsAttributesA = GetProcAddressAs(
g_SspiModule, "SetCredentialsAttributesA", SET_CREDENTIALS_ATTRIBUTES_FN_W);
}
}
return TRUE;
}
#endif
static BOOL CALLBACK InitializeSspiModuleInt(WINPR_ATTR_UNUSED PINIT_ONCE once,
WINPR_ATTR_UNUSED PVOID param,
WINPR_ATTR_UNUSED PVOID* context)
{
BOOL status = FALSE;
#if defined(WITH_NATIVE_SSPI)
DWORD flags = 0;
if (param)
flags = *(DWORD*)param;
#endif
sspi_GlobalInit();
g_Log = WLog_Get("com.winpr.sspi");
#if defined(WITH_NATIVE_SSPI)
if (flags && (flags & SSPI_INTERFACE_NATIVE))
{
status = InitializeSspiModule_Native();
}
else if (flags && (flags & SSPI_INTERFACE_WINPR))
{
g_SspiW = winpr_InitSecurityInterfaceW();
g_SspiA = winpr_InitSecurityInterfaceA();
status = TRUE;
}
if (!status && ShouldUseNativeSspi())
{
status = InitializeSspiModule_Native();
}
#endif
if (!status)
{
g_SspiW = winpr_InitSecurityInterfaceW();
g_SspiA = winpr_InitSecurityInterfaceA();
}
return TRUE;
}
const char* GetSecurityStatusString(SECURITY_STATUS status)
{
switch (status)
{
case SEC_E_OK:
return "SEC_E_OK";
case SEC_E_INSUFFICIENT_MEMORY:
return "SEC_E_INSUFFICIENT_MEMORY";
case SEC_E_INVALID_HANDLE:
return "SEC_E_INVALID_HANDLE";
case SEC_E_UNSUPPORTED_FUNCTION:
return "SEC_E_UNSUPPORTED_FUNCTION";
case SEC_E_TARGET_UNKNOWN:
return "SEC_E_TARGET_UNKNOWN";
case SEC_E_INTERNAL_ERROR:
return "SEC_E_INTERNAL_ERROR";
case SEC_E_SECPKG_NOT_FOUND:
return "SEC_E_SECPKG_NOT_FOUND";
case SEC_E_NOT_OWNER:
return "SEC_E_NOT_OWNER";
case SEC_E_CANNOT_INSTALL:
return "SEC_E_CANNOT_INSTALL";
case SEC_E_INVALID_TOKEN:
return "SEC_E_INVALID_TOKEN";
case SEC_E_CANNOT_PACK:
return "SEC_E_CANNOT_PACK";
case SEC_E_QOP_NOT_SUPPORTED:
return "SEC_E_QOP_NOT_SUPPORTED";
case SEC_E_NO_IMPERSONATION:
return "SEC_E_NO_IMPERSONATION";
case SEC_E_LOGON_DENIED:
return "SEC_E_LOGON_DENIED";
case SEC_E_UNKNOWN_CREDENTIALS:
return "SEC_E_UNKNOWN_CREDENTIALS";
case SEC_E_NO_CREDENTIALS:
return "SEC_E_NO_CREDENTIALS";
case SEC_E_MESSAGE_ALTERED:
return "SEC_E_MESSAGE_ALTERED";
case SEC_E_OUT_OF_SEQUENCE:
return "SEC_E_OUT_OF_SEQUENCE";
case SEC_E_NO_AUTHENTICATING_AUTHORITY:
return "SEC_E_NO_AUTHENTICATING_AUTHORITY";
case SEC_E_BAD_PKGID:
return "SEC_E_BAD_PKGID";
case SEC_E_CONTEXT_EXPIRED:
return "SEC_E_CONTEXT_EXPIRED";
case SEC_E_INCOMPLETE_MESSAGE:
return "SEC_E_INCOMPLETE_MESSAGE";
case SEC_E_INCOMPLETE_CREDENTIALS:
return "SEC_E_INCOMPLETE_CREDENTIALS";
case SEC_E_BUFFER_TOO_SMALL:
return "SEC_E_BUFFER_TOO_SMALL";
case SEC_E_WRONG_PRINCIPAL:
return "SEC_E_WRONG_PRINCIPAL";
case SEC_E_TIME_SKEW:
return "SEC_E_TIME_SKEW";
case SEC_E_UNTRUSTED_ROOT:
return "SEC_E_UNTRUSTED_ROOT";
case SEC_E_ILLEGAL_MESSAGE:
return "SEC_E_ILLEGAL_MESSAGE";
case SEC_E_CERT_UNKNOWN:
return "SEC_E_CERT_UNKNOWN";
case SEC_E_CERT_EXPIRED:
return "SEC_E_CERT_EXPIRED";
case SEC_E_ENCRYPT_FAILURE:
return "SEC_E_ENCRYPT_FAILURE";
case SEC_E_DECRYPT_FAILURE:
return "SEC_E_DECRYPT_FAILURE";
case SEC_E_ALGORITHM_MISMATCH:
return "SEC_E_ALGORITHM_MISMATCH";
case SEC_E_SECURITY_QOS_FAILED:
return "SEC_E_SECURITY_QOS_FAILED";
case SEC_E_UNFINISHED_CONTEXT_DELETED:
return "SEC_E_UNFINISHED_CONTEXT_DELETED";
case SEC_E_NO_TGT_REPLY:
return "SEC_E_NO_TGT_REPLY";
case SEC_E_NO_IP_ADDRESSES:
return "SEC_E_NO_IP_ADDRESSES";
case SEC_E_WRONG_CREDENTIAL_HANDLE:
return "SEC_E_WRONG_CREDENTIAL_HANDLE";
case SEC_E_CRYPTO_SYSTEM_INVALID:
return "SEC_E_CRYPTO_SYSTEM_INVALID";
case SEC_E_MAX_REFERRALS_EXCEEDED:
return "SEC_E_MAX_REFERRALS_EXCEEDED";
case SEC_E_MUST_BE_KDC:
return "SEC_E_MUST_BE_KDC";
case SEC_E_STRONG_CRYPTO_NOT_SUPPORTED:
return "SEC_E_STRONG_CRYPTO_NOT_SUPPORTED";
case SEC_E_TOO_MANY_PRINCIPALS:
return "SEC_E_TOO_MANY_PRINCIPALS";
case SEC_E_NO_PA_DATA:
return "SEC_E_NO_PA_DATA";
case SEC_E_PKINIT_NAME_MISMATCH:
return "SEC_E_PKINIT_NAME_MISMATCH";
case SEC_E_SMARTCARD_LOGON_REQUIRED:
return "SEC_E_SMARTCARD_LOGON_REQUIRED";
case SEC_E_SHUTDOWN_IN_PROGRESS:
return "SEC_E_SHUTDOWN_IN_PROGRESS";
case SEC_E_KDC_INVALID_REQUEST:
return "SEC_E_KDC_INVALID_REQUEST";
case SEC_E_KDC_UNABLE_TO_REFER:
return "SEC_E_KDC_UNABLE_TO_REFER";
case SEC_E_KDC_UNKNOWN_ETYPE:
return "SEC_E_KDC_UNKNOWN_ETYPE";
case SEC_E_UNSUPPORTED_PREAUTH:
return "SEC_E_UNSUPPORTED_PREAUTH";
case SEC_E_DELEGATION_REQUIRED:
return "SEC_E_DELEGATION_REQUIRED";
case SEC_E_BAD_BINDINGS:
return "SEC_E_BAD_BINDINGS";
case SEC_E_MULTIPLE_ACCOUNTS:
return "SEC_E_MULTIPLE_ACCOUNTS";
case SEC_E_NO_KERB_KEY:
return "SEC_E_NO_KERB_KEY";
case SEC_E_CERT_WRONG_USAGE:
return "SEC_E_CERT_WRONG_USAGE";
case SEC_E_DOWNGRADE_DETECTED:
return "SEC_E_DOWNGRADE_DETECTED";
case SEC_E_SMARTCARD_CERT_REVOKED:
return "SEC_E_SMARTCARD_CERT_REVOKED";
case SEC_E_ISSUING_CA_UNTRUSTED:
return "SEC_E_ISSUING_CA_UNTRUSTED";
case SEC_E_REVOCATION_OFFLINE_C:
return "SEC_E_REVOCATION_OFFLINE_C";
case SEC_E_PKINIT_CLIENT_FAILURE:
return "SEC_E_PKINIT_CLIENT_FAILURE";
case SEC_E_SMARTCARD_CERT_EXPIRED:
return "SEC_E_SMARTCARD_CERT_EXPIRED";
case SEC_E_NO_S4U_PROT_SUPPORT:
return "SEC_E_NO_S4U_PROT_SUPPORT";
case SEC_E_CROSSREALM_DELEGATION_FAILURE:
return "SEC_E_CROSSREALM_DELEGATION_FAILURE";
case SEC_E_REVOCATION_OFFLINE_KDC:
return "SEC_E_REVOCATION_OFFLINE_KDC";
case SEC_E_ISSUING_CA_UNTRUSTED_KDC:
return "SEC_E_ISSUING_CA_UNTRUSTED_KDC";
case SEC_E_KDC_CERT_EXPIRED:
return "SEC_E_KDC_CERT_EXPIRED";
case SEC_E_KDC_CERT_REVOKED:
return "SEC_E_KDC_CERT_REVOKED";
case SEC_E_INVALID_PARAMETER:
return "SEC_E_INVALID_PARAMETER";
case SEC_E_DELEGATION_POLICY:
return "SEC_E_DELEGATION_POLICY";
case SEC_E_POLICY_NLTM_ONLY:
return "SEC_E_POLICY_NLTM_ONLY";
case SEC_E_NO_CONTEXT:
return "SEC_E_NO_CONTEXT";
case SEC_E_PKU2U_CERT_FAILURE:
return "SEC_E_PKU2U_CERT_FAILURE";
case SEC_E_MUTUAL_AUTH_FAILED:
return "SEC_E_MUTUAL_AUTH_FAILED";
case SEC_I_CONTINUE_NEEDED:
return "SEC_I_CONTINUE_NEEDED";
case SEC_I_COMPLETE_NEEDED:
return "SEC_I_COMPLETE_NEEDED";
case SEC_I_COMPLETE_AND_CONTINUE:
return "SEC_I_COMPLETE_AND_CONTINUE";
case SEC_I_LOCAL_LOGON:
return "SEC_I_LOCAL_LOGON";
case SEC_I_CONTEXT_EXPIRED:
return "SEC_I_CONTEXT_EXPIRED";
case SEC_I_INCOMPLETE_CREDENTIALS:
return "SEC_I_INCOMPLETE_CREDENTIALS";
case SEC_I_RENEGOTIATE:
return "SEC_I_RENEGOTIATE";
case SEC_I_NO_LSA_CONTEXT:
return "SEC_I_NO_LSA_CONTEXT";
case SEC_I_SIGNATURE_NEEDED:
return "SEC_I_SIGNATURE_NEEDED";
case SEC_I_NO_RENEGOTIATION:
return "SEC_I_NO_RENEGOTIATION";
default:
break;
}
return NtStatus2Tag(status);
}
BOOL IsSecurityStatusError(SECURITY_STATUS status)
{
BOOL error = TRUE;
switch (status)
{
case SEC_E_OK:
case SEC_I_CONTINUE_NEEDED:
case SEC_I_COMPLETE_NEEDED:
case SEC_I_COMPLETE_AND_CONTINUE:
case SEC_I_LOCAL_LOGON:
case SEC_I_CONTEXT_EXPIRED:
case SEC_I_INCOMPLETE_CREDENTIALS:
case SEC_I_RENEGOTIATE:
case SEC_I_NO_LSA_CONTEXT:
case SEC_I_SIGNATURE_NEEDED:
case SEC_I_NO_RENEGOTIATION:
error = FALSE;
break;
default:
break;
}
return error;
}
SecurityFunctionTableW* SEC_ENTRY InitSecurityInterfaceExW(DWORD flags)
{
if (!InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, &flags, nullptr))
return nullptr;
WLog_Print(g_Log, WLOG_DEBUG, "InitSecurityInterfaceExW");
return g_SspiW;
}
SecurityFunctionTableA* SEC_ENTRY InitSecurityInterfaceExA(DWORD flags)
{
if (!InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, &flags, nullptr))
return nullptr;
WLog_Print(g_Log, WLOG_DEBUG, "InitSecurityInterfaceExA");
return g_SspiA;
}
/**
* Standard SSPI API
*/
static SECURITY_STATUS sspi_init(void)
{
if (!InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, nullptr, nullptr))
return SEC_E_INTERNAL_ERROR;
if (!g_SspiA || !g_SspiW)
{
WLog_Print(g_Log, WLOG_WARN, "Security module does not provide an implementation");
return SEC_E_UNSUPPORTED_FUNCTION;
}
return SEC_E_OK;
}
/* Package Management */
SECURITY_STATUS SEC_ENTRY sspi_EnumerateSecurityPackagesW(ULONG* pcPackages,
PSecPkgInfoW* ppPackageInfo)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = g_SspiW->EnumerateSecurityPackagesW(pcPackages, ppPackageInfo);
WLog_Print(g_Log, WLOG_DEBUG, "EnumerateSecurityPackagesW: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_EnumerateSecurityPackagesA(ULONG* pcPackages,
PSecPkgInfoA* ppPackageInfo)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiA->EnumerateSecurityPackagesA,
pcPackages, ppPackageInfo);
WLog_Print(g_Log, WLOG_DEBUG, "EnumerateSecurityPackagesA: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SecurityFunctionTableW* SEC_ENTRY sspi_InitSecurityInterfaceW(void)
{
if (!InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, nullptr, nullptr))
return nullptr;
WLog_Print(g_Log, WLOG_DEBUG, "InitSecurityInterfaceW");
return g_SspiW;
}
SecurityFunctionTableA* SEC_ENTRY sspi_InitSecurityInterfaceA(void)
{
if (!InitOnceExecuteOnce(&g_Initialized, InitializeSspiModuleInt, nullptr, nullptr))
return nullptr;
WLog_Print(g_Log, WLOG_DEBUG, "InitSecurityInterfaceA");
return g_SspiA;
}
SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityPackageInfoW(SEC_WCHAR* pszPackageName,
PSecPkgInfoW* ppPackageInfo)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->QuerySecurityPackageInfoW,
pszPackageName, ppPackageInfo);
WLog_Print(g_Log, WLOG_DEBUG, "QuerySecurityPackageInfoW: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityPackageInfoA(SEC_CHAR* pszPackageName,
PSecPkgInfoA* ppPackageInfo)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiA->QuerySecurityPackageInfoA,
pszPackageName, ppPackageInfo);
WLog_Print(g_Log, WLOG_DEBUG, "QuerySecurityPackageInfoA: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
/* Credential Management */
SECURITY_STATUS SEC_ENTRY sspi_AcquireCredentialsHandleW(
SEC_WCHAR* pszPrincipal, SEC_WCHAR* pszPackage, ULONG fCredentialUse, void* pvLogonID,
void* pAuthData, SEC_GET_KEY_FN pGetKeyFn, void* pvGetKeyArgument, PCredHandle phCredential,
PTimeStamp ptsExpiry)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->AcquireCredentialsHandleW,
pszPrincipal, pszPackage, fCredentialUse, pvLogonID, pAuthData, pGetKeyFn,
pvGetKeyArgument, phCredential, ptsExpiry);
WLog_Print(g_Log, WLOG_DEBUG, "AcquireCredentialsHandleW: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_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 = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiA->AcquireCredentialsHandleA,
pszPrincipal, pszPackage, fCredentialUse, pvLogonID, pAuthData, pGetKeyFn,
pvGetKeyArgument, phCredential, ptsExpiry);
WLog_Print(g_Log, WLOG_DEBUG, "AcquireCredentialsHandleA: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_ExportSecurityContext(PCtxtHandle phContext, ULONG fFlags,
PSecBuffer pPackedContext, HANDLE* pToken)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->ExportSecurityContext, phContext,
fFlags, pPackedContext, pToken);
WLog_Print(g_Log, WLOG_DEBUG, "ExportSecurityContext: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_FreeCredentialsHandle(PCredHandle phCredential)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->FreeCredentialsHandle, phCredential);
WLog_Print(g_Log, WLOG_DEBUG, "FreeCredentialsHandle: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_ImportSecurityContextW(SEC_WCHAR* pszPackage,
PSecBuffer pPackedContext, HANDLE pToken,
PCtxtHandle phContext)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->ImportSecurityContextW, pszPackage,
pPackedContext, pToken, phContext);
WLog_Print(g_Log, WLOG_DEBUG, "ImportSecurityContextW: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_ImportSecurityContextA(SEC_CHAR* pszPackage,
PSecBuffer pPackedContext, HANDLE pToken,
PCtxtHandle phContext)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiA->ImportSecurityContextA, pszPackage,
pPackedContext, pToken, phContext);
WLog_Print(g_Log, WLOG_DEBUG, "ImportSecurityContextA: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_QueryCredentialsAttributesW(PCredHandle phCredential,
ULONG ulAttribute, void* pBuffer)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->QueryCredentialsAttributesW,
phCredential, ulAttribute, pBuffer);
WLog_Print(g_Log, WLOG_DEBUG, "QueryCredentialsAttributesW: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_QueryCredentialsAttributesA(PCredHandle phCredential,
ULONG ulAttribute, void* pBuffer)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiA->QueryCredentialsAttributesA,
phCredential, ulAttribute, pBuffer);
WLog_Print(g_Log, WLOG_DEBUG, "QueryCredentialsAttributesA: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
/* Context Management */
SECURITY_STATUS SEC_ENTRY sspi_AcceptSecurityContext(PCredHandle phCredential,
PCtxtHandle phContext, PSecBufferDesc pInput,
ULONG fContextReq, ULONG TargetDataRep,
PCtxtHandle phNewContext,
PSecBufferDesc pOutput, PULONG pfContextAttr,
PTimeStamp ptsTimeStamp)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->AcceptSecurityContext, phCredential,
phContext, pInput, fContextReq, TargetDataRep, phNewContext, pOutput,
pfContextAttr, ptsTimeStamp);
WLog_Print(g_Log, WLOG_DEBUG, "AcceptSecurityContext: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_ApplyControlToken(PCtxtHandle phContext, PSecBufferDesc pInput)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status =
IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->ApplyControlToken, phContext, pInput);
WLog_Print(g_Log, WLOG_DEBUG, "ApplyControlToken: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_CompleteAuthToken(PCtxtHandle phContext, PSecBufferDesc pToken)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status =
IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->CompleteAuthToken, phContext, pToken);
WLog_Print(g_Log, WLOG_DEBUG, "CompleteAuthToken: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_DeleteSecurityContext(PCtxtHandle phContext)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->DeleteSecurityContext, phContext);
WLog_Print(g_Log, WLOG_DEBUG, "DeleteSecurityContext: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_FreeContextBuffer(void* pvContextBuffer)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->FreeContextBuffer, pvContextBuffer);
WLog_Print(g_Log, WLOG_DEBUG, "FreeContextBuffer: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_ImpersonateSecurityContext(PCtxtHandle phContext)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status =
IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->ImpersonateSecurityContext, phContext);
WLog_Print(g_Log, WLOG_DEBUG, "ImpersonateSecurityContext: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_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 = sspi_init();
if (status != SEC_E_OK)
return status;
status =
IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->InitializeSecurityContextW, phCredential,
phContext, pszTargetName, fContextReq, Reserved1, TargetDataRep, pInput,
Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry);
WLog_Print(g_Log, WLOG_DEBUG, "InitializeSecurityContextW: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_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 = sspi_init();
if (status != SEC_E_OK)
return status;
status =
IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiA->InitializeSecurityContextA, phCredential,
phContext, pszTargetName, fContextReq, Reserved1, TargetDataRep, pInput,
Reserved2, phNewContext, pOutput, pfContextAttr, ptsExpiry);
WLog_Print(g_Log, WLOG_DEBUG, "InitializeSecurityContextA: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_QueryContextAttributesW(PCtxtHandle phContext, ULONG ulAttribute,
void* pBuffer)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->QueryContextAttributesW, phContext,
ulAttribute, pBuffer);
WLog_Print(g_Log, WLOG_DEBUG, "QueryContextAttributesW: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_QueryContextAttributesA(PCtxtHandle phContext, ULONG ulAttribute,
void* pBuffer)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiA->QueryContextAttributesA, phContext,
ulAttribute, pBuffer);
WLog_Print(g_Log, WLOG_DEBUG, "QueryContextAttributesA: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityContextToken(PCtxtHandle phContext, HANDLE* phToken)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->QuerySecurityContextToken, phContext,
phToken);
WLog_Print(g_Log, WLOG_DEBUG, "QuerySecurityContextToken: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_SetContextAttributesW(PCtxtHandle phContext, ULONG ulAttribute,
void* pBuffer, ULONG cbBuffer)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->SetContextAttributesW, phContext,
ulAttribute, pBuffer, cbBuffer);
WLog_Print(g_Log, WLOG_DEBUG, "SetContextAttributesW: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_SetContextAttributesA(PCtxtHandle phContext, ULONG ulAttribute,
void* pBuffer, ULONG cbBuffer)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiA->SetContextAttributesA, phContext,
ulAttribute, pBuffer, cbBuffer);
WLog_Print(g_Log, WLOG_DEBUG, "SetContextAttributesA: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_RevertSecurityContext(PCtxtHandle phContext)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->RevertSecurityContext, phContext);
WLog_Print(g_Log, WLOG_DEBUG, "RevertSecurityContext: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
/* Message Support */
SECURITY_STATUS SEC_ENTRY sspi_DecryptMessage(PCtxtHandle phContext, PSecBufferDesc pMessage,
ULONG MessageSeqNo, PULONG pfQOP)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->DecryptMessage, phContext, pMessage,
MessageSeqNo, pfQOP);
WLog_Print(g_Log, WLOG_DEBUG, "DecryptMessage: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_EncryptMessage(PCtxtHandle phContext, ULONG fQOP,
PSecBufferDesc pMessage, ULONG MessageSeqNo)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->EncryptMessage, phContext, fQOP,
pMessage, MessageSeqNo);
WLog_Print(g_Log, WLOG_DEBUG, "EncryptMessage: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_MakeSignature(PCtxtHandle phContext, ULONG fQOP,
PSecBufferDesc pMessage, ULONG MessageSeqNo)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->MakeSignature, phContext, fQOP,
pMessage, MessageSeqNo);
WLog_Print(g_Log, WLOG_DEBUG, "MakeSignature: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
SECURITY_STATUS SEC_ENTRY sspi_VerifySignature(PCtxtHandle phContext, PSecBufferDesc pMessage,
ULONG MessageSeqNo, PULONG pfQOP)
{
SECURITY_STATUS status = sspi_init();
if (status != SEC_E_OK)
return status;
status = IFCALLRESULT(SEC_E_UNSUPPORTED_FUNCTION, g_SspiW->VerifySignature, phContext, pMessage,
MessageSeqNo, pfQOP);
WLog_Print(g_Log, WLOG_DEBUG, "VerifySignature: %s (0x%08" PRIX32 ")",
GetSecurityStatusString(status), WINPR_CXX_COMPAT_CAST(UINT32, status));
return status;
}
WINPR_PRAGMA_DIAG_POP
static void zfree(WCHAR* str, size_t len, BOOL isWCHAR)
{
if (str)
memset(str, 0, len * (isWCHAR ? sizeof(WCHAR) : sizeof(char)));
free(str);
}
void sspi_FreeAuthIdentity(SEC_WINNT_AUTH_IDENTITY* identity)
{
if (!identity)
return;
const BOOL wc = (identity->Flags & SEC_WINNT_AUTH_IDENTITY_UNICODE) != 0;
zfree(identity->User, identity->UserLength, wc);
zfree(identity->Domain, identity->DomainLength, wc);
/* identity->PasswordLength does have a dual use. In Pass The Hash (PTH) mode the maximum
* password length (512) is added to the real length to mark this as a hash. when we free up
* this field without removing these additional bytes we would corrupt the stack.
*/
size_t len = identity->PasswordLength;
if (len > SSPI_CREDENTIALS_HASH_LENGTH_OFFSET)
len -= SSPI_CREDENTIALS_HASH_LENGTH_OFFSET;
zfree(identity->Password, len, wc);
const SEC_WINNT_AUTH_IDENTITY empty = WINPR_C_ARRAY_INIT;
*identity = empty;
}

View File

@@ -0,0 +1,93 @@
/**
* WinPR: Windows Portable Runtime
* Security Support Provider Interface (SSPI)
*
* Copyright 2012-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_PRIVATE_H
#define WINPR_SSPI_PRIVATE_H
#include <winpr/sspi.h>
#define SCHANNEL_CB_MAX_TOKEN 0x00006000
#define SSPI_CREDENTIALS_PASSWORD_HASH 0x00000001
#define SSPI_CREDENTIALS_HASH_LENGTH_OFFSET 512
typedef struct
{
DWORD flags;
ULONG fCredentialUse;
SEC_GET_KEY_FN pGetKeyFn;
void* pvGetKeyArgument;
SEC_WINNT_AUTH_IDENTITY identity;
SEC_WINPR_NTLM_SETTINGS ntlmSettings;
SEC_WINPR_KERBEROS_SETTINGS kerbSettings;
} SSPI_CREDENTIALS;
SSPI_CREDENTIALS* sspi_CredentialsNew(void);
void sspi_CredentialsFree(SSPI_CREDENTIALS* credentials);
PSecBuffer sspi_FindSecBuffer(PSecBufferDesc pMessage, ULONG BufferType);
SecHandle* sspi_SecureHandleAlloc(void);
void sspi_SecureHandleInvalidate(SecHandle* handle);
void* sspi_SecureHandleGetLowerPointer(SecHandle* handle);
void sspi_SecureHandleSetLowerPointer(SecHandle* handle, void* pointer);
void* sspi_SecureHandleGetUpperPointer(SecHandle* handle);
void sspi_SecureHandleSetUpperPointer(SecHandle* handle, void* pointer);
void sspi_SecureHandleFree(SecHandle* handle);
enum SecurityFunctionTableIndex
{
EnumerateSecurityPackagesIndex = 1,
Reserved1Index = 2,
QueryCredentialsAttributesIndex = 3,
AcquireCredentialsHandleIndex = 4,
FreeCredentialsHandleIndex = 5,
Reserved2Index = 6,
InitializeSecurityContextIndex = 7,
AcceptSecurityContextIndex = 8,
CompleteAuthTokenIndex = 9,
DeleteSecurityContextIndex = 10,
ApplyControlTokenIndex = 11,
QueryContextAttributesIndex = 12,
ImpersonateSecurityContextIndex = 13,
RevertSecurityContextIndex = 14,
MakeSignatureIndex = 15,
VerifySignatureIndex = 16,
FreeContextBufferIndex = 17,
QuerySecurityPackageInfoIndex = 18,
Reserved3Index = 19,
Reserved4Index = 20,
ExportSecurityContextIndex = 21,
ImportSecurityContextIndex = 22,
AddCredentialsIndex = 23,
Reserved8Index = 24,
QuerySecurityContextTokenIndex = 25,
EncryptMessageIndex = 26,
DecryptMessageIndex = 27,
SetContextAttributesIndex = 28,
SetCredentialsAttributesIndex = 29
};
BOOL IsSecurityStatusError(SECURITY_STATUS status);
#include "sspi_gss.h"
#include "sspi_winpr.h"
#endif /* WINPR_SSPI_PRIVATE_H */

View File

@@ -0,0 +1,347 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Security Support Provider Interface (SSPI)
*
* Copyright 2012-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/platform.h>
#include <winpr/wtypes.h>
#include <winpr/config.h>
#ifdef _WIN32
#define SEC_ENTRY __stdcall
#define SSPI_EXPORT __declspec(dllexport)
#else
#include <winpr/winpr.h>
#if defined(SSPI_DLL)
#define SEC_ENTRY
#define SSPI_EXPORT WINPR_API
#endif
#endif
#ifdef _WIN32
typedef long LONG;
typedef unsigned long ULONG;
#endif
typedef LONG SECURITY_STATUS;
WINPR_PRAGMA_DIAG_PUSH
WINPR_PRAGMA_DIAG_IGNORED_MISSING_PROTOTYPES
#ifdef SSPI_DLL
/**
* Standard SSPI API
*/
/* Package Management */
extern SECURITY_STATUS SEC_ENTRY sspi_EnumerateSecurityPackagesW(void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY EnumerateSecurityPackagesW(void* pcPackages,
void* ppPackageInfo)
{
return sspi_EnumerateSecurityPackagesW(pcPackages, ppPackageInfo);
}
extern SECURITY_STATUS SEC_ENTRY sspi_EnumerateSecurityPackagesA(void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY EnumerateSecurityPackagesA(void* pcPackages,
void* ppPackageInfo)
{
return sspi_EnumerateSecurityPackagesA(pcPackages, ppPackageInfo);
}
extern void* SEC_ENTRY sspi_InitSecurityInterfaceW(void);
SSPI_EXPORT void* SEC_ENTRY InitSecurityInterfaceW(void)
{
return sspi_InitSecurityInterfaceW();
}
extern void* SEC_ENTRY sspi_InitSecurityInterfaceA(void);
SSPI_EXPORT void* SEC_ENTRY InitSecurityInterfaceA(void)
{
return sspi_InitSecurityInterfaceA();
}
extern SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityPackageInfoW(void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QuerySecurityPackageInfoW(void* pszPackageName,
void* ppPackageInfo)
{
return sspi_QuerySecurityPackageInfoW(pszPackageName, ppPackageInfo);
}
extern SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityPackageInfoA(void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QuerySecurityPackageInfoA(void* pszPackageName,
void* ppPackageInfo)
{
return sspi_QuerySecurityPackageInfoA(pszPackageName, ppPackageInfo);
}
/* Credential Management */
extern SECURITY_STATUS SEC_ENTRY sspi_AcquireCredentialsHandleW(void*, void*, ULONG, void*, void*,
void*, void*, void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleW(
void* pszPrincipal, void* pszPackage, ULONG fCredentialUse, void* pvLogonID, void* pAuthData,
void* pGetKeyFn, void* pvGetKeyArgument, void* phCredential, void* ptsExpiry)
{
return sspi_AcquireCredentialsHandleW(pszPrincipal, pszPackage, fCredentialUse, pvLogonID,
pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential,
ptsExpiry);
}
extern SECURITY_STATUS SEC_ENTRY sspi_AcquireCredentialsHandleA(void*, void*, ULONG, void*, void*,
void*, void*, void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY AcquireCredentialsHandleA(
void* pszPrincipal, void* pszPackage, ULONG fCredentialUse, void* pvLogonID, void* pAuthData,
void* pGetKeyFn, void* pvGetKeyArgument, void* phCredential, void* ptsExpiry)
{
return sspi_AcquireCredentialsHandleA(pszPrincipal, pszPackage, fCredentialUse, pvLogonID,
pAuthData, pGetKeyFn, pvGetKeyArgument, phCredential,
ptsExpiry);
}
extern SECURITY_STATUS SEC_ENTRY sspi_ExportSecurityContext(void*, ULONG, void*, void**);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY ExportSecurityContext(void* phContext, ULONG fFlags,
void* pPackedContext, void** pToken)
{
return sspi_ExportSecurityContext(phContext, fFlags, pPackedContext, pToken);
}
extern SECURITY_STATUS SEC_ENTRY sspi_FreeCredentialsHandle(void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY FreeCredentialsHandle(void* phCredential)
{
return sspi_FreeCredentialsHandle(phCredential);
}
extern SECURITY_STATUS SEC_ENTRY sspi_ImportSecurityContextW(void*, void*, void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY ImportSecurityContextW(void* pszPackage, void* pPackedContext,
void* pToken, void* phContext)
{
return sspi_ImportSecurityContextW(pszPackage, pPackedContext, pToken, phContext);
}
extern SECURITY_STATUS SEC_ENTRY sspi_ImportSecurityContextA(void*, void*, void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY ImportSecurityContextA(void* pszPackage, void* pPackedContext,
void* pToken, void* phContext)
{
return sspi_ImportSecurityContextA(pszPackage, pPackedContext, pToken, phContext);
}
extern SECURITY_STATUS SEC_ENTRY sspi_QueryCredentialsAttributesW(void*, ULONG, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QueryCredentialsAttributesW(void* phCredential,
ULONG ulAttribute, void* pBuffer)
{
return sspi_QueryCredentialsAttributesW(phCredential, ulAttribute, pBuffer);
}
extern SECURITY_STATUS SEC_ENTRY sspi_QueryCredentialsAttributesA(void*, ULONG, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QueryCredentialsAttributesA(void* phCredential,
ULONG ulAttribute, void* pBuffer)
{
return sspi_QueryCredentialsAttributesA(phCredential, ulAttribute, pBuffer);
}
/* Context Management */
extern SECURITY_STATUS SEC_ENTRY sspi_AcceptSecurityContext(void*, void*, void*, ULONG, ULONG,
void*, void*, void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY AcceptSecurityContext(void* phCredential, void* phContext,
void* pInput, ULONG fContextReq,
ULONG TargetDataRep, void* phNewContext,
void* pOutput, void* pfContextAttr,
void* ptsTimeStamp)
{
return sspi_AcceptSecurityContext(phCredential, phContext, pInput, fContextReq, TargetDataRep,
phNewContext, pOutput, pfContextAttr, ptsTimeStamp);
}
extern SECURITY_STATUS SEC_ENTRY sspi_ApplyControlToken(void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY ApplyControlToken(void* phContext, void* pInput)
{
return sspi_ApplyControlToken(phContext, pInput);
}
extern SECURITY_STATUS SEC_ENTRY sspi_CompleteAuthToken(void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY CompleteAuthToken(void* phContext, void* pToken)
{
return sspi_CompleteAuthToken(phContext, pToken);
}
extern SECURITY_STATUS SEC_ENTRY sspi_DeleteSecurityContext(void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY DeleteSecurityContext(void* phContext)
{
return sspi_DeleteSecurityContext(phContext);
}
extern SECURITY_STATUS SEC_ENTRY sspi_FreeContextBuffer(void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY FreeContextBuffer(void* pvContextBuffer)
{
return sspi_FreeContextBuffer(pvContextBuffer);
}
extern SECURITY_STATUS SEC_ENTRY sspi_ImpersonateSecurityContext(void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY ImpersonateSecurityContext(void* phContext)
{
return sspi_ImpersonateSecurityContext(phContext);
}
extern SECURITY_STATUS SEC_ENTRY sspi_InitializeSecurityContextW(void*, void*, void*, ULONG, ULONG,
ULONG, void*, ULONG, void*, void*,
void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY InitializeSecurityContextW(
void* phCredential, void* phContext, void* pszTargetName, ULONG fContextReq, ULONG Reserved1,
ULONG TargetDataRep, void* pInput, ULONG Reserved2, void* phNewContext, void* pOutput,
void* pfContextAttr, void* ptsExpiry)
{
return sspi_InitializeSecurityContextW(phCredential, phContext, pszTargetName, fContextReq,
Reserved1, TargetDataRep, pInput, Reserved2,
phNewContext, pOutput, pfContextAttr, ptsExpiry);
}
extern SECURITY_STATUS SEC_ENTRY sspi_InitializeSecurityContextA(void*, void*, void*, ULONG, ULONG,
ULONG, void*, ULONG, void*, void*,
void*, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY InitializeSecurityContextA(
void* phCredential, void* phContext, void* pszTargetName, ULONG fContextReq, ULONG Reserved1,
ULONG TargetDataRep, void* pInput, ULONG Reserved2, void* phNewContext, void* pOutput,
void* pfContextAttr, void* ptsExpiry)
{
return sspi_InitializeSecurityContextA(phCredential, phContext, pszTargetName, fContextReq,
Reserved1, TargetDataRep, pInput, Reserved2,
phNewContext, pOutput, pfContextAttr, ptsExpiry);
}
extern SECURITY_STATUS SEC_ENTRY sspi_QueryContextAttributesW(void*, ULONG, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QueryContextAttributesW(void* phContext, ULONG ulAttribute,
void* pBuffer)
{
return sspi_QueryContextAttributesW(phContext, ulAttribute, pBuffer);
}
extern SECURITY_STATUS SEC_ENTRY sspi_QueryContextAttributesA(void*, ULONG, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QueryContextAttributesA(void* phContext, ULONG ulAttribute,
void* pBuffer)
{
return sspi_QueryContextAttributesA(phContext, ulAttribute, pBuffer);
}
extern SECURITY_STATUS SEC_ENTRY sspi_QuerySecurityContextToken(void*, void**);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY QuerySecurityContextToken(void* phContext, void** phToken)
{
return sspi_QuerySecurityContextToken(phContext, phToken);
}
extern SECURITY_STATUS SEC_ENTRY sspi_SetContextAttributesW(void*, ULONG, void*, ULONG);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY SetContextAttributesW(void* phContext, ULONG ulAttribute,
void* pBuffer, ULONG cbBuffer)
{
return sspi_SetContextAttributesW(phContext, ulAttribute, pBuffer, cbBuffer);
}
extern SECURITY_STATUS SEC_ENTRY sspi_SetContextAttributesA(void*, ULONG, void*, ULONG);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY SetContextAttributesA(void* phContext, ULONG ulAttribute,
void* pBuffer, ULONG cbBuffer)
{
return sspi_SetContextAttributesA(phContext, ulAttribute, pBuffer, cbBuffer);
}
extern SECURITY_STATUS SEC_ENTRY sspi_SetCredentialsAttributesW(void*, ULONG, void*, ULONG);
static SECURITY_STATUS SEC_ENTRY SetCredentialsAttributesW(void* phCredential, ULONG ulAttribute,
void* pBuffer, ULONG cbBuffer)
{
return sspi_SetCredentialsAttributesW(phCredential, ulAttribute, pBuffer, cbBuffer);
}
extern SECURITY_STATUS SEC_ENTRY sspi_SetCredentialsAttributesA(void*, ULONG, void*, ULONG);
static SECURITY_STATUS SEC_ENTRY SetCredentialsAttributesA(void* phCredential, ULONG ulAttribute,
void* pBuffer, ULONG cbBuffer)
{
return sspi_SetCredentialsAttributesA(phCredential, ulAttribute, pBuffer, cbBuffer);
}
extern SECURITY_STATUS SEC_ENTRY sspi_RevertSecurityContext(void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY RevertSecurityContext(void* phContext)
{
return sspi_RevertSecurityContext(phContext);
}
/* Message Support */
extern SECURITY_STATUS SEC_ENTRY sspi_DecryptMessage(void*, void*, ULONG, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY DecryptMessage(void* phContext, void* pMessage,
ULONG MessageSeqNo, void* pfQOP)
{
return sspi_DecryptMessage(phContext, pMessage, MessageSeqNo, pfQOP);
}
extern SECURITY_STATUS SEC_ENTRY sspi_EncryptMessage(void*, ULONG, void*, ULONG);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY EncryptMessage(void* phContext, ULONG fQOP, void* pMessage,
ULONG MessageSeqNo)
{
return sspi_EncryptMessage(phContext, fQOP, pMessage, MessageSeqNo);
}
extern SECURITY_STATUS SEC_ENTRY sspi_MakeSignature(void*, ULONG, void*, ULONG);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY MakeSignature(void* phContext, ULONG fQOP, void* pMessage,
ULONG MessageSeqNo)
{
return sspi_MakeSignature(phContext, fQOP, pMessage, MessageSeqNo);
}
extern SECURITY_STATUS SEC_ENTRY sspi_VerifySignature(void*, void*, ULONG, void*);
SSPI_EXPORT SECURITY_STATUS SEC_ENTRY VerifySignature(void* phContext, void* pMessage,
ULONG MessageSeqNo, void* pfQOP)
{
return sspi_VerifySignature(phContext, pMessage, MessageSeqNo, pfQOP);
}
#endif /* SSPI_DLL */
WINPR_PRAGMA_DIAG_POP

View File

@@ -0,0 +1,120 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Generic Security Service Application Program Interface (GSSAPI)
*
* Copyright 2015 ANSSI, Author Thomas Calderon
* Copyright 2015 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2017 Dorian Ducournau <dorian.ducournau@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/crt.h>
#include <winpr/assert.h>
#include <winpr/endian.h>
#include <winpr/asn1.h>
#include <winpr/stream.h>
#include "sspi_gss.h"
BOOL sspi_gss_wrap_token(SecBuffer* buf, const WinPrAsn1_OID* oid, uint16_t tok_id,
const sspi_gss_data* token)
{
WinPrAsn1Encoder* enc = nullptr;
BYTE tok_id_buf[2];
WinPrAsn1_MemoryChunk mc = { 2, tok_id_buf };
wStream s;
size_t len = 0;
BOOL ret = FALSE;
WINPR_ASSERT(buf);
WINPR_ASSERT(oid);
WINPR_ASSERT(token);
winpr_Data_Write_UINT16_BE(tok_id_buf, tok_id);
enc = WinPrAsn1Encoder_New(WINPR_ASN1_DER);
if (!enc)
return FALSE;
/* initialContextToken [APPLICATION 0] */
if (!WinPrAsn1EncAppContainer(enc, 0))
goto cleanup;
/* thisMech OID */
if (!WinPrAsn1EncOID(enc, oid))
goto cleanup;
/* TOK_ID */
if (!WinPrAsn1EncRawContent(enc, &mc))
goto cleanup;
/* innerToken */
mc.data = (BYTE*)token->data;
mc.len = token->length;
if (!WinPrAsn1EncRawContent(enc, &mc))
goto cleanup;
if (!WinPrAsn1EncEndContainer(enc))
goto cleanup;
if (!WinPrAsn1EncStreamSize(enc, &len) || len > buf->cbBuffer)
goto cleanup;
Stream_StaticInit(&s, buf->pvBuffer, len);
if (WinPrAsn1EncToStream(enc, &s))
{
buf->cbBuffer = (UINT32)len;
ret = TRUE;
}
cleanup:
WinPrAsn1Encoder_Free(&enc);
return ret;
}
BOOL sspi_gss_unwrap_token(const SecBuffer* buf, WinPrAsn1_OID* oid, uint16_t* tok_id,
sspi_gss_data* token)
{
WinPrAsn1Decoder dec = WinPrAsn1Decoder_init();
WinPrAsn1Decoder dec2 = WinPrAsn1Decoder_init();
WinPrAsn1_tagId tag = 0;
wStream sbuffer = WINPR_C_ARRAY_INIT;
wStream* s = nullptr;
WINPR_ASSERT(buf);
WINPR_ASSERT(oid);
WINPR_ASSERT(token);
WinPrAsn1Decoder_InitMem(&dec, WINPR_ASN1_DER, buf->pvBuffer, buf->cbBuffer);
if (!WinPrAsn1DecReadApp(&dec, &tag, &dec2) || tag != 0)
return FALSE;
if (!WinPrAsn1DecReadOID(&dec2, oid, FALSE))
return FALSE;
sbuffer = WinPrAsn1DecGetStream(&dec2);
s = &sbuffer;
if (Stream_Length(s) < 2)
return FALSE;
if (tok_id)
Stream_Read_UINT16_BE(s, *tok_id);
token->data = Stream_Pointer(s);
token->length = (UINT)Stream_GetRemainingLength(s);
return TRUE;
}

View File

@@ -0,0 +1,85 @@
/**
* FreeRDP: A Remote Desktop Protocol Implementation
* Generic Security Service Application Program Interface (GSSAPI)
*
* Copyright 2015 ANSSI, Author Thomas Calderon
* Copyright 2015 Marc-Andre Moreau <marcandre.moreau@gmail.com>
* Copyright 2017 Dorian Ducournau <dorian.ducournau@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_GSS_PRIVATE_H
#define WINPR_SSPI_GSS_PRIVATE_H
#include <winpr/sspi.h>
#include <winpr/asn1.h>
#ifdef WITH_KRB5_MIT
#include <krb5.h>
typedef krb5_data sspi_gss_data;
#elif defined(WITH_KRB5_HEIMDAL)
#include <krb5.h>
typedef krb5_data sspi_gss_data;
#else
typedef struct
{
int32_t magic;
unsigned int length;
char* data;
} sspi_gss_data;
#endif
#define SSPI_GSS_C_DELEG_FLAG 1
#define SSPI_GSS_C_MUTUAL_FLAG 2
#define SSPI_GSS_C_REPLAY_FLAG 4
#define SSPI_GSS_C_SEQUENCE_FLAG 8
#define SSPI_GSS_C_CONF_FLAG 16
#define SSPI_GSS_C_INTEG_FLAG 32
#define FLAG_SENDER_IS_ACCEPTOR 0x01
#define FLAG_WRAP_CONFIDENTIAL 0x02
#define FLAG_ACCEPTOR_SUBKEY 0x04
#define KG_USAGE_ACCEPTOR_SEAL 22
#define KG_USAGE_ACCEPTOR_SIGN 23
#define KG_USAGE_INITIATOR_SEAL 24
#define KG_USAGE_INITIATOR_SIGN 25
#define TOK_ID_AP_REQ 0x0100
#define TOK_ID_AP_REP 0x0200
#define TOK_ID_ERROR 0x0300
#define TOK_ID_TGT_REQ 0x0400
#define TOK_ID_TGT_REP 0x0401
#define TOK_ID_MIC 0x0404
#define TOK_ID_WRAP 0x0504
#define TOK_ID_MIC_V1 0x0101
#define TOK_ID_WRAP_V1 0x0201
#define GSS_CHECKSUM_TYPE 0x8003
static inline BOOL sspi_gss_oid_compare(const WinPrAsn1_OID* oid1, const WinPrAsn1_OID* oid2)
{
WINPR_ASSERT(oid1);
WINPR_ASSERT(oid2);
return (oid1->len == oid2->len) && (memcmp(oid1->data, oid2->data, oid1->len) == 0);
}
BOOL sspi_gss_wrap_token(SecBuffer* buf, const WinPrAsn1_OID* oid, uint16_t tok_id,
const sspi_gss_data* token);
BOOL sspi_gss_unwrap_token(const SecBuffer* buf, WinPrAsn1_OID* oid, uint16_t* tok_id,
sspi_gss_data* token);
#endif /* WINPR_SSPI_GSS_PRIVATE_H */

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,31 @@
/**
* WinPR: Windows Portable Runtime
* Security Support Provider Interface (SSPI)
*
* Copyright 2012-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_WINPR_H
#define WINPR_SSPI_WINPR_H
#include <winpr/sspi.h>
SecurityFunctionTableW* SEC_ENTRY winpr_InitSecurityInterfaceW(void);
SecurityFunctionTableA* SEC_ENTRY winpr_InitSecurityInterfaceA(void);
void* sspi_ContextBufferAlloc(UINT32 allocatorIndex, size_t size);
void sspi_ContextBufferFree(void* contextBuffer);
#endif /* WINPR_SSPI_WINPR_H */

View File

@@ -0,0 +1,34 @@
set(MODULE_NAME "TestSspi")
set(MODULE_PREFIX "TEST_SSPI")
disable_warnings_for_directory(${CMAKE_CURRENT_BINARY_DIR})
set(${MODULE_PREFIX}_DRIVER ${MODULE_NAME}.c)
set(${MODULE_PREFIX}_TESTS
TestQuerySecurityPackageInfo.c TestEnumerateSecurityPackages.c TestInitializeSecurityContext.c
TestAcquireCredentialsHandle.c TestCredSSP.c
#TestSchannel.c
TestNTLM.c
)
create_test_sourcelist(${MODULE_PREFIX}_SRCS ${${MODULE_PREFIX}_DRIVER} ${${MODULE_PREFIX}_TESTS})
include_directories(SYSTEM ${OPENSSL_INCLUDE_DIR})
add_executable(${MODULE_NAME} ${${MODULE_PREFIX}_SRCS})
if(WIN32)
set(${MODULE_PREFIX}_LIBS ${${MODULE_PREFIX}_LIBS} secur32 crypt32)
endif()
target_link_libraries(${MODULE_NAME} ${${MODULE_PREFIX}_LIBS} winpr)
set_target_properties(${MODULE_NAME} PROPERTIES RUNTIME_OUTPUT_DIRECTORY "${TESTING_OUTPUT_DIRECTORY}")
foreach(test ${${MODULE_PREFIX}_TESTS})
get_filename_component(TestName ${test} NAME_WE)
add_test(${TestName} ${TESTING_OUTPUT_DIRECTORY}/${MODULE_NAME} ${TestName})
endforeach()
set_property(TARGET ${MODULE_NAME} PROPERTY FOLDER "WinPR/Test")

View File

@@ -0,0 +1,61 @@
#include <stdio.h>
#include <winpr/crt.h>
#include <winpr/sspi.h>
#include <winpr/winpr.h>
static const char* test_User = "User";
static const char* test_Domain = "Domain";
static const char* test_Password = "Password";
int TestAcquireCredentialsHandle(int argc, char* argv[])
{
int rc = -1;
SECURITY_STATUS status = 0;
CredHandle credentials = WINPR_C_ARRAY_INIT;
TimeStamp expiration;
SEC_WINNT_AUTH_IDENTITY identity;
SecurityFunctionTable* table = nullptr;
SecPkgCredentials_Names credential_names;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
sspi_GlobalInit();
table = InitSecurityInterfaceEx(0);
identity.User = (UINT16*)_strdup(test_User);
identity.Domain = (UINT16*)_strdup(test_Domain);
identity.Password = (UINT16*)_strdup(test_Password);
if (!identity.User || !identity.Domain || !identity.Password)
goto fail;
identity.UserLength = strlen(test_User);
identity.DomainLength = strlen(test_Domain);
identity.PasswordLength = strlen(test_Password);
identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
status =
table->AcquireCredentialsHandle(nullptr, NTLM_SSP_NAME, SECPKG_CRED_OUTBOUND, nullptr,
&identity, nullptr, nullptr, &credentials, &expiration);
if (status != SEC_E_OK)
goto fail;
status =
table->QueryCredentialsAttributes(&credentials, SECPKG_CRED_ATTR_NAMES, &credential_names);
if (status != SEC_E_OK)
goto fail;
rc = 0;
fail:
if (SecIsValidHandle(&credentials))
table->FreeCredentialsHandle(&credentials);
free(identity.User);
free(identity.Domain);
free(identity.Password);
sspi_GlobalFinish();
return rc;
}

View File

@@ -0,0 +1,8 @@
#include <winpr/crt.h>
#include <winpr/sspi.h>
int TestCredSSP(int argc, char* argv[])
{
return 0;
}

View File

@@ -0,0 +1,40 @@
#include <stdio.h>
#include <winpr/crt.h>
#include <winpr/sspi.h>
#include <winpr/winpr.h>
#include <winpr/tchar.h>
int TestEnumerateSecurityPackages(int argc, char* argv[])
{
ULONG cPackages = 0;
SECURITY_STATUS status = 0;
SecPkgInfo* pPackageInfo = nullptr;
SecurityFunctionTable* table = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
sspi_GlobalInit();
table = InitSecurityInterfaceEx(0);
status = table->EnumerateSecurityPackages(&cPackages, &pPackageInfo);
if (status != SEC_E_OK)
{
sspi_GlobalFinish();
return -1;
}
_tprintf(_T("\nEnumerateSecurityPackages (%") _T(PRIu32) _T("):\n"), cPackages);
for (size_t index = 0; index < cPackages; index++)
{
_tprintf(_T("\"%s\", \"%s\"\n"), pPackageInfo[index].Name, pPackageInfo[index].Comment);
}
table->FreeContextBuffer(pPackageInfo);
sspi_GlobalFinish();
return 0;
}

View File

@@ -0,0 +1,116 @@
#include <stdio.h>
#include <winpr/crt.h>
#include <winpr/sspi.h>
#include <winpr/winpr.h>
static const char* test_User = "User";
static const char* test_Domain = "Domain";
static const char* test_Password = "Password";
int TestInitializeSecurityContext(int argc, char* argv[])
{
int rc = -1;
UINT32 cbMaxLen = 0;
UINT32 fContextReq = 0;
void* output_buffer = nullptr;
CtxtHandle context;
ULONG pfContextAttr = 0;
SECURITY_STATUS status = 0;
CredHandle credentials = WINPR_C_ARRAY_INIT;
TimeStamp expiration;
PSecPkgInfo pPackageInfo = nullptr;
SEC_WINNT_AUTH_IDENTITY identity = WINPR_C_ARRAY_INIT;
SecurityFunctionTable* table = nullptr;
PSecBuffer p_SecBuffer = nullptr;
SecBuffer output_SecBuffer;
SecBufferDesc output_SecBuffer_desc;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
sspi_GlobalInit();
table = InitSecurityInterfaceEx(0);
status = table->QuerySecurityPackageInfo(NTLM_SSP_NAME, &pPackageInfo);
if (status != SEC_E_OK)
{
printf("QuerySecurityPackageInfo status: 0x%08" PRIX32 "\n", status);
goto fail;
}
cbMaxLen = pPackageInfo->cbMaxToken;
identity.User = (UINT16*)_strdup(test_User);
identity.Domain = (UINT16*)_strdup(test_Domain);
identity.Password = (UINT16*)_strdup(test_Password);
if (!identity.User || !identity.Domain || !identity.Password)
goto fail;
identity.UserLength = strlen(test_User);
identity.DomainLength = strlen(test_Domain);
identity.PasswordLength = strlen(test_Password);
identity.Flags = SEC_WINNT_AUTH_IDENTITY_ANSI;
status =
table->AcquireCredentialsHandle(nullptr, NTLM_SSP_NAME, SECPKG_CRED_OUTBOUND, nullptr,
&identity, nullptr, nullptr, &credentials, &expiration);
if (status != SEC_E_OK)
{
printf("AcquireCredentialsHandle status: 0x%08" PRIX32 "\n", status);
goto fail;
}
fContextReq = ISC_REQ_REPLAY_DETECT | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_CONFIDENTIALITY |
ISC_REQ_DELEGATE;
output_buffer = malloc(cbMaxLen);
if (!output_buffer)
{
printf("Memory allocation failed\n");
goto fail;
}
output_SecBuffer_desc.ulVersion = 0;
output_SecBuffer_desc.cBuffers = 1;
output_SecBuffer_desc.pBuffers = &output_SecBuffer;
output_SecBuffer.cbBuffer = cbMaxLen;
output_SecBuffer.BufferType = SECBUFFER_TOKEN;
output_SecBuffer.pvBuffer = output_buffer;
status = table->InitializeSecurityContext(&credentials, nullptr, nullptr, fContextReq, 0, 0,
nullptr, 0, &context, &output_SecBuffer_desc,
&pfContextAttr, &expiration);
if (status != SEC_I_CONTINUE_NEEDED)
{
printf("InitializeSecurityContext status: 0x%08" PRIX32 "\n", status);
goto fail;
}
printf("cBuffers: %" PRIu32 " ulVersion: %" PRIu32 "\n", output_SecBuffer_desc.cBuffers,
output_SecBuffer_desc.ulVersion);
p_SecBuffer = &output_SecBuffer_desc.pBuffers[0];
printf("BufferType: 0x%08" PRIX32 " cbBuffer: %" PRIu32 "\n", p_SecBuffer->BufferType,
p_SecBuffer->cbBuffer);
status = table->DeleteSecurityContext(&context);
if (status != SEC_E_OK)
{
printf("DeleteSecurityContext status: 0x%08" PRIX32 "\n", status);
goto fail;
}
rc = 0;
fail:
free(identity.User);
free(identity.Domain);
free(identity.Password);
free(output_buffer);
if (SecIsValidHandle(&credentials))
table->FreeCredentialsHandle(&credentials);
table->FreeContextBuffer(pPackageInfo);
sspi_GlobalFinish();
return rc;
}

View File

@@ -0,0 +1,792 @@
#include <winpr/crt.h>
#include <winpr/assert.h>
#include <winpr/sspi.h>
#include <winpr/print.h>
#include <winpr/wlog.h>
struct test_input_t
{
const char* user;
const char* domain;
const char* pwd;
const BYTE* ntlm;
const BYTE* ntlmv2;
BOOL dynamic;
BOOL expected;
};
typedef struct
{
CtxtHandle context;
ULONG cbMaxToken;
ULONG fContextReq;
ULONG pfContextAttr;
TimeStamp expiration;
PSecBuffer pBuffer;
SecBuffer inputBuffer[2];
SecBuffer outputBuffer[2];
BOOL haveContext;
BOOL haveInputBuffer;
BOOL UseNtlmV2Hash;
LPTSTR ServicePrincipalName;
SecBufferDesc inputBufferDesc;
SecBufferDesc outputBufferDesc;
CredHandle credentials;
BOOL confidentiality;
SecPkgInfo* pPackageInfo;
SecurityFunctionTable* table;
SEC_WINNT_AUTH_IDENTITY identity;
} TEST_NTLM_SERVER;
static BYTE TEST_NTLM_TIMESTAMP[8] = { 0x33, 0x57, 0xbd, 0xb1, 0x07, 0x8b, 0xcf, 0x01 };
static BYTE TEST_NTLM_CLIENT_CHALLENGE[8] = { 0x20, 0xc0, 0x2b, 0x3d, 0xc0, 0x61, 0xa7, 0x73 };
static BYTE TEST_NTLM_SERVER_CHALLENGE[8] = { 0xa4, 0xf1, 0xba, 0xa6, 0x7c, 0xdc, 0x1a, 0x12 };
static BYTE TEST_NTLM_NEGOTIATE[] =
"\x4e\x54\x4c\x4d\x53\x53\x50\x00\x01\x00\x00\x00\x07\x82\x08\xa2"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x06\x03\x80\x25\x00\x00\x00\x0f";
static BYTE TEST_NTLM_CHALLENGE[] =
"\x4e\x54\x4c\x4d\x53\x53\x50\x00\x02\x00\x00\x00\x00\x00\x00\x00"
"\x38\x00\x00\x00\x07\x82\x88\xa2\xa4\xf1\xba\xa6\x7c\xdc\x1a\x12"
"\x00\x00\x00\x00\x00\x00\x00\x00\x66\x00\x66\x00\x38\x00\x00\x00"
"\x06\x03\x80\x25\x00\x00\x00\x0f\x02\x00\x0e\x00\x4e\x00\x45\x00"
"\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x01\x00\x0e\x00\x4e\x00"
"\x45\x00\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x04\x00\x1c\x00"
"\x6c\x00\x61\x00\x62\x00\x2e\x00\x77\x00\x61\x00\x79\x00\x6b\x00"
"\x2e\x00\x6c\x00\x6f\x00\x63\x00\x61\x00\x6c\x00\x03\x00\x0e\x00"
"\x6e\x00\x65\x00\x77\x00\x79\x00\x65\x00\x61\x00\x72\x00\x07\x00"
"\x08\x00\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x00\x00\x00\x00";
static BYTE TEST_NTLM_AUTHENTICATE[] =
"\x4e\x54\x4c\x4d\x53\x53\x50\x00\x03\x00\x00\x00\x18\x00\x18\x00"
"\x82\x00\x00\x00\x08\x01\x08\x01\x9a\x00\x00\x00\x0c\x00\x0c\x00"
"\x58\x00\x00\x00\x10\x00\x10\x00\x64\x00\x00\x00\x0e\x00\x0e\x00"
"\x74\x00\x00\x00\x00\x00\x00\x00\xa2\x01\x00\x00\x05\x82\x88\xa2"
"\x06\x03\x80\x25\x00\x00\x00\x0f\x12\xe5\x5a\xf5\x80\xee\x3f\x29"
"\xe1\xde\x90\x4d\x73\x77\x06\x25\x44\x00\x6f\x00\x6d\x00\x61\x00"
"\x69\x00\x6e\x00\x55\x00\x73\x00\x65\x00\x72\x00\x6e\x00\x61\x00"
"\x6d\x00\x65\x00\x4e\x00\x45\x00\x57\x00\x59\x00\x45\x00\x41\x00"
"\x52\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x62\x14\x68\xc8\x98\x12"
"\xe7\x39\xd8\x76\x1b\xe9\xf7\x54\xb5\xe3\x01\x01\x00\x00\x00\x00"
"\x00\x00\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x20\xc0\x2b\x3d\xc0\x61"
"\xa7\x73\x00\x00\x00\x00\x02\x00\x0e\x00\x4e\x00\x45\x00\x57\x00"
"\x59\x00\x45\x00\x41\x00\x52\x00\x01\x00\x0e\x00\x4e\x00\x45\x00"
"\x57\x00\x59\x00\x45\x00\x41\x00\x52\x00\x04\x00\x1c\x00\x6c\x00"
"\x61\x00\x62\x00\x2e\x00\x77\x00\x61\x00\x79\x00\x6b\x00\x2e\x00"
"\x6c\x00\x6f\x00\x63\x00\x61\x00\x6c\x00\x03\x00\x0e\x00\x6e\x00"
"\x65\x00\x77\x00\x79\x00\x65\x00\x61\x00\x72\x00\x07\x00\x08\x00"
"\x33\x57\xbd\xb1\x07\x8b\xcf\x01\x06\x00\x04\x00\x02\x00\x00\x00"
"\x08\x00\x30\x00\x30\x00\x00\x00\x00\x00\x00\x00\x01\x00\x00\x00"
"\x00\x20\x00\x00\x1e\x10\xf5\x2c\x54\x2f\x2e\x77\x1c\x13\xbf\xc3"
"\x3f\xe1\x7b\x28\x7e\x0b\x93\x5a\x39\xd2\xce\x12\xd7\xbd\x8c\x4e"
"\x2b\xb5\x0b\xf5\x0a\x00\x10\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00\x00\x00\x00\x00\x00\x00\x09\x00\x1a\x00\x48\x00\x54\x00"
"\x54\x00\x50\x00\x2f\x00\x72\x00\x77\x00\x2e\x00\x6c\x00\x6f\x00"
"\x63\x00\x61\x00\x6c\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00"
"\x00\x00";
#define TEST_SSPI_INTERFACE SSPI_INTERFACE_WINPR
static const char* TEST_NTLM_USER = "Username";
static const char* TEST_NTLM_DOMAIN = "Domain";
static const char* TEST_NTLM_PASSWORD = "P4ss123!";
// static const char* TEST_NTLM_HASH_STRING = "d5922a65c4d5c082ca444af1be0001db";
static const BYTE TEST_NTLM_HASH[16] = { 0xd5, 0x92, 0x2a, 0x65, 0xc4, 0xd5, 0xc0, 0x82,
0xca, 0x44, 0x4a, 0xf1, 0xbe, 0x00, 0x01, 0xdb };
// static const char* TEST_NTLM_HASH_V2_STRING = "4c7f706f7dde05a9d1a0f4e7ffe3bfb8";
static const BYTE TEST_NTLM_V2_HASH[16] = { 0x4c, 0x7f, 0x70, 0x6f, 0x7d, 0xde, 0x05, 0xa9,
0xd1, 0xa0, 0xf4, 0xe7, 0xff, 0xe3, 0xbf, 0xb8 };
static const BYTE TEST_EMPTY_PWD_NTLM_HASH[] = { 0x31, 0xd6, 0xcf, 0xe0, 0xd1, 0x6a, 0xe9, 0x31,
0xb7, 0x3c, 0x59, 0xd7, 0xe0, 0xc0, 0x89, 0xc0 };
static const BYTE TEST_EMPTY_PWD_NTLM_V2_HASH[] = {
0x0b, 0xce, 0x54, 0x87, 0x4e, 0x94, 0x20, 0x9e, 0x34, 0x48, 0x97, 0xc1, 0x60, 0x03, 0x6e, 0x3b
};
#define NTLM_PACKAGE_NAME NTLM_SSP_NAME
typedef struct
{
CtxtHandle context;
ULONG cbMaxToken;
ULONG fContextReq;
ULONG pfContextAttr;
TimeStamp expiration;
PSecBuffer pBuffer;
SecBuffer inputBuffer[2];
SecBuffer outputBuffer[2];
BOOL haveContext;
BOOL haveInputBuffer;
LPTSTR ServicePrincipalName;
SecBufferDesc inputBufferDesc;
SecBufferDesc outputBufferDesc;
CredHandle credentials;
BOOL confidentiality;
SecPkgInfo* pPackageInfo;
SecurityFunctionTable* table;
SEC_WINNT_AUTH_IDENTITY identity;
} TEST_NTLM_CLIENT;
static int test_ntlm_client_init(TEST_NTLM_CLIENT* ntlm, const char* user, const char* domain,
const char* password)
{
SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
WINPR_ASSERT(ntlm);
SecInvalidateHandle(&(ntlm->context));
ntlm->table = InitSecurityInterfaceEx(TEST_SSPI_INTERFACE);
if (!ntlm->table)
return -1;
const int rc = sspi_SetAuthIdentity(&(ntlm->identity), user, domain, password);
if (rc < 0)
return rc;
status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo);
if (status != SEC_E_OK)
{
(void)fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n",
GetSecurityStatusString(status), status);
return -1;
}
ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken;
status = ntlm->table->AcquireCredentialsHandle(nullptr, NTLM_PACKAGE_NAME, SECPKG_CRED_OUTBOUND,
nullptr, &ntlm->identity, nullptr, nullptr,
&ntlm->credentials, &ntlm->expiration);
if (status != SEC_E_OK)
{
(void)fprintf(stderr, "AcquireCredentialsHandle status: %s (0x%08" PRIX32 ")\n",
GetSecurityStatusString(status), status);
return -1;
}
ntlm->haveContext = FALSE;
ntlm->haveInputBuffer = FALSE;
ZeroMemory(&ntlm->inputBuffer, sizeof(SecBuffer));
ZeroMemory(&ntlm->outputBuffer, sizeof(SecBuffer));
ntlm->fContextReq = 0;
/* NLA authentication flags */
ntlm->fContextReq |= ISC_REQ_MUTUAL_AUTH;
ntlm->fContextReq |= ISC_REQ_CONFIDENTIALITY;
ntlm->fContextReq |= ISC_REQ_USE_SESSION_KEY;
return 1;
}
static void test_ntlm_client_uninit(TEST_NTLM_CLIENT* ntlm)
{
if (!ntlm)
return;
if (ntlm->outputBuffer[0].pvBuffer)
{
free(ntlm->outputBuffer[0].pvBuffer);
ntlm->outputBuffer[0].pvBuffer = nullptr;
}
free(ntlm->identity.User);
free(ntlm->identity.Domain);
free(ntlm->identity.Password);
free(ntlm->ServicePrincipalName);
if (ntlm->table)
{
SECURITY_STATUS status = ntlm->table->FreeCredentialsHandle(&ntlm->credentials);
WINPR_ASSERT((status == SEC_E_OK) || (status == SEC_E_SECPKG_NOT_FOUND) ||
(status == SEC_E_UNSUPPORTED_FUNCTION));
status = ntlm->table->FreeContextBuffer(ntlm->pPackageInfo);
WINPR_ASSERT((status == SEC_E_OK) || (status = SEC_E_INVALID_HANDLE));
status = ntlm->table->DeleteSecurityContext(&ntlm->context);
WINPR_ASSERT((status == SEC_E_OK) || (status == SEC_E_SECPKG_NOT_FOUND) ||
(status == SEC_E_UNSUPPORTED_FUNCTION));
}
}
/**
* SSPI Client Ceremony
*
* --------------
* ( Client Begin )
* --------------
* |
* |
* \|/
* -----------+--------------
* | AcquireCredentialsHandle |
* --------------------------
* |
* |
* \|/
* -------------+--------------
* +---------------> / InitializeSecurityContext /
* | ----------------------------
* | |
* | |
* | \|/
* --------------------------- ---------+------------- ----------------------
* / Receive blob from server / < Received security blob? > --Yes-> / Send blob to server /
* -------------+------------- ----------------------- ----------------------
* /|\ | |
* | No |
* Yes \|/ |
* | ------------+----------- |
* +---------------- < Received Continue Needed > <-----------------+
* ------------------------
* |
* No
* \|/
* ------+-------
* ( Client End )
* --------------
*/
static BOOL IsSecurityStatusError(SECURITY_STATUS status)
{
BOOL error = TRUE;
switch (status)
{
case SEC_E_OK:
case SEC_I_CONTINUE_NEEDED:
case SEC_I_COMPLETE_NEEDED:
case SEC_I_COMPLETE_AND_CONTINUE:
case SEC_I_LOCAL_LOGON:
case SEC_I_CONTEXT_EXPIRED:
case SEC_I_INCOMPLETE_CREDENTIALS:
case SEC_I_RENEGOTIATE:
case SEC_I_NO_LSA_CONTEXT:
case SEC_I_SIGNATURE_NEEDED:
case SEC_I_NO_RENEGOTIATION:
error = FALSE;
break;
default:
break;
}
return error;
}
static int test_ntlm_client_authenticate(TEST_NTLM_CLIENT* ntlm)
{
SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
WINPR_ASSERT(ntlm);
if (ntlm->outputBuffer[0].pvBuffer)
{
free(ntlm->outputBuffer[0].pvBuffer);
ntlm->outputBuffer[0].pvBuffer = nullptr;
}
ntlm->outputBufferDesc.ulVersion = SECBUFFER_VERSION;
ntlm->outputBufferDesc.cBuffers = 1;
ntlm->outputBufferDesc.pBuffers = ntlm->outputBuffer;
ntlm->outputBuffer[0].BufferType = SECBUFFER_TOKEN;
ntlm->outputBuffer[0].cbBuffer = ntlm->cbMaxToken;
ntlm->outputBuffer[0].pvBuffer = malloc(ntlm->outputBuffer[0].cbBuffer);
if (!ntlm->outputBuffer[0].pvBuffer)
return -1;
if (ntlm->haveInputBuffer)
{
ntlm->inputBufferDesc.ulVersion = SECBUFFER_VERSION;
ntlm->inputBufferDesc.cBuffers = 1;
ntlm->inputBufferDesc.pBuffers = ntlm->inputBuffer;
ntlm->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
}
if ((!ntlm) || (!ntlm->table))
{
(void)fprintf(stderr, "ntlm_authenticate: invalid ntlm context\n");
return -1;
}
status = ntlm->table->InitializeSecurityContext(
&ntlm->credentials, (ntlm->haveContext) ? &ntlm->context : nullptr,
(ntlm->ServicePrincipalName) ? ntlm->ServicePrincipalName : nullptr, ntlm->fContextReq, 0,
SECURITY_NATIVE_DREP, (ntlm->haveInputBuffer) ? &ntlm->inputBufferDesc : nullptr, 0,
&ntlm->context, &ntlm->outputBufferDesc, &ntlm->pfContextAttr, &ntlm->expiration);
if (IsSecurityStatusError(status))
return -1;
if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED))
{
if (ntlm->table->CompleteAuthToken)
{
SECURITY_STATUS rc =
ntlm->table->CompleteAuthToken(&ntlm->context, &ntlm->outputBufferDesc);
if (rc != SEC_E_OK)
return -1;
}
if (status == SEC_I_COMPLETE_NEEDED)
status = SEC_E_OK;
else if (status == SEC_I_COMPLETE_AND_CONTINUE)
status = SEC_I_CONTINUE_NEEDED;
}
if (ntlm->haveInputBuffer)
{
free(ntlm->inputBuffer[0].pvBuffer);
}
ntlm->haveInputBuffer = TRUE;
ntlm->haveContext = TRUE;
return (status == SEC_I_CONTINUE_NEEDED) ? 1 : 0;
}
static TEST_NTLM_CLIENT* test_ntlm_client_new(void)
{
TEST_NTLM_CLIENT* ntlm = (TEST_NTLM_CLIENT*)calloc(1, sizeof(TEST_NTLM_CLIENT));
if (!ntlm)
return nullptr;
return ntlm;
}
static void test_ntlm_client_free(TEST_NTLM_CLIENT* ntlm)
{
if (!ntlm)
return;
test_ntlm_client_uninit(ntlm);
free(ntlm);
}
static int test_ntlm_server_init(TEST_NTLM_SERVER* ntlm)
{
SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
WINPR_ASSERT(ntlm);
ntlm->UseNtlmV2Hash = TRUE;
SecInvalidateHandle(&(ntlm->context));
ntlm->table = InitSecurityInterfaceEx(TEST_SSPI_INTERFACE);
if (!ntlm->table)
return SEC_E_INTERNAL_ERROR;
status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo);
if (status != SEC_E_OK)
{
(void)fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n",
GetSecurityStatusString(status), status);
return -1;
}
ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken;
status = ntlm->table->AcquireCredentialsHandle(nullptr, NTLM_PACKAGE_NAME, SECPKG_CRED_INBOUND,
nullptr, nullptr, nullptr, nullptr,
&ntlm->credentials, &ntlm->expiration);
if (status != SEC_E_OK)
{
(void)fprintf(stderr, "AcquireCredentialsHandle status: %s (0x%08" PRIX32 ")\n",
GetSecurityStatusString(status), status);
return -1;
}
ntlm->haveContext = FALSE;
ntlm->haveInputBuffer = FALSE;
ZeroMemory(&ntlm->inputBuffer, sizeof(SecBuffer));
ZeroMemory(&ntlm->outputBuffer, sizeof(SecBuffer));
ntlm->fContextReq = 0;
/* NLA authentication flags */
ntlm->fContextReq |= ASC_REQ_MUTUAL_AUTH;
ntlm->fContextReq |= ASC_REQ_CONFIDENTIALITY;
ntlm->fContextReq |= ASC_REQ_CONNECTION;
ntlm->fContextReq |= ASC_REQ_USE_SESSION_KEY;
ntlm->fContextReq |= ASC_REQ_REPLAY_DETECT;
ntlm->fContextReq |= ASC_REQ_SEQUENCE_DETECT;
ntlm->fContextReq |= ASC_REQ_EXTENDED_ERROR;
return 1;
}
static void test_ntlm_server_uninit(TEST_NTLM_SERVER* ntlm)
{
if (!ntlm)
return;
if (ntlm->outputBuffer[0].pvBuffer)
{
free(ntlm->outputBuffer[0].pvBuffer);
ntlm->outputBuffer[0].pvBuffer = nullptr;
}
free(ntlm->identity.User);
free(ntlm->identity.Domain);
free(ntlm->identity.Password);
free(ntlm->ServicePrincipalName);
if (ntlm->table)
{
SECURITY_STATUS status = ntlm->table->FreeCredentialsHandle(&ntlm->credentials);
WINPR_ASSERT(status == SEC_E_OK);
status = ntlm->table->FreeContextBuffer(ntlm->pPackageInfo);
WINPR_ASSERT(status == SEC_E_OK);
status = ntlm->table->DeleteSecurityContext(&ntlm->context);
WINPR_ASSERT(status == SEC_E_OK);
}
}
static int test_ntlm_server_authenticate(const struct test_input_t* targ, TEST_NTLM_SERVER* ntlm)
{
SECURITY_STATUS status = SEC_E_INTERNAL_ERROR;
WINPR_ASSERT(ntlm);
WINPR_ASSERT(targ);
ntlm->inputBufferDesc.ulVersion = SECBUFFER_VERSION;
ntlm->inputBufferDesc.cBuffers = 1;
ntlm->inputBufferDesc.pBuffers = ntlm->inputBuffer;
ntlm->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
ntlm->outputBufferDesc.ulVersion = SECBUFFER_VERSION;
ntlm->outputBufferDesc.cBuffers = 1;
ntlm->outputBufferDesc.pBuffers = &ntlm->outputBuffer[0];
ntlm->outputBuffer[0].BufferType = SECBUFFER_TOKEN;
ntlm->outputBuffer[0].cbBuffer = ntlm->cbMaxToken;
ntlm->outputBuffer[0].pvBuffer = malloc(ntlm->outputBuffer[0].cbBuffer);
if (!ntlm->outputBuffer[0].pvBuffer)
return -1;
status = ntlm->table->AcceptSecurityContext(
&ntlm->credentials, ntlm->haveContext ? &ntlm->context : nullptr, &ntlm->inputBufferDesc,
ntlm->fContextReq, SECURITY_NATIVE_DREP, &ntlm->context, &ntlm->outputBufferDesc,
&ntlm->pfContextAttr, &ntlm->expiration);
if (status == SEC_I_CONTINUE_NEEDED)
{
SecPkgContext_AuthNtlmHash AuthNtlmHash = WINPR_C_ARRAY_INIT;
if (ntlm->UseNtlmV2Hash)
{
AuthNtlmHash.Version = 2;
CopyMemory(AuthNtlmHash.NtlmHash, targ->ntlmv2, 16);
}
else
{
AuthNtlmHash.Version = 1;
CopyMemory(AuthNtlmHash.NtlmHash, targ->ntlm, 16);
}
status =
ntlm->table->SetContextAttributes(&ntlm->context, SECPKG_ATTR_AUTH_NTLM_HASH,
&AuthNtlmHash, sizeof(SecPkgContext_AuthNtlmHash));
}
if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED))
{
(void)fprintf(stderr, "AcceptSecurityContext status: %s (0x%08" PRIX32 ")\n",
GetSecurityStatusString(status), status);
return -1; /* Access Denied */
}
ntlm->haveContext = TRUE;
return (status == SEC_I_CONTINUE_NEEDED) ? 1 : 0;
}
static TEST_NTLM_SERVER* test_ntlm_server_new(void)
{
TEST_NTLM_SERVER* ntlm = (TEST_NTLM_SERVER*)calloc(1, sizeof(TEST_NTLM_SERVER));
if (!ntlm)
return nullptr;
return ntlm;
}
static void test_ntlm_server_free(TEST_NTLM_SERVER* ntlm)
{
if (!ntlm)
return;
test_ntlm_server_uninit(ntlm);
free(ntlm);
}
static BOOL test_default(const struct test_input_t* arg)
{
BOOL rc = FALSE;
PSecBuffer pSecBuffer = nullptr;
WINPR_ASSERT(arg);
printf("testcase {user=%s, domain=%s, password=%s, dynamic=%s}\n", arg->user, arg->domain,
arg->pwd, arg->dynamic ? "TRUE" : "FALSE");
/**
* Client Initialization
*/
TEST_NTLM_CLIENT* client = test_ntlm_client_new();
TEST_NTLM_SERVER* server = test_ntlm_server_new();
if (!client || !server)
{
printf("Memory allocation failed\n");
goto fail;
}
int status = test_ntlm_client_init(client, arg->user, arg->domain, arg->pwd);
if (status < 0)
{
printf("test_ntlm_client_init failure\n");
goto fail;
}
/**
* Server Initialization
*/
status = test_ntlm_server_init(server);
if (status < 0)
{
printf("test_ntlm_server_init failure\n");
goto fail;
}
/**
* Client -> Negotiate Message
*/
status = test_ntlm_client_authenticate(client);
if (status < 0)
{
printf("test_ntlm_client_authenticate failure\n");
goto fail;
}
if (!arg->dynamic)
{
SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp = WINPR_C_ARRAY_INIT;
SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge = WINPR_C_ARRAY_INIT;
SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge = WINPR_C_ARRAY_INIT;
CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8);
AuthNtlmTimestamp.ChallengeOrResponse = TRUE;
SECURITY_STATUS rc = client->table->SetContextAttributes(
&client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, &AuthNtlmTimestamp,
sizeof(SecPkgContext_AuthNtlmTimestamp));
WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
AuthNtlmTimestamp.ChallengeOrResponse = FALSE;
rc = client->table->SetContextAttributes(&client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP,
&AuthNtlmTimestamp,
sizeof(SecPkgContext_AuthNtlmTimestamp));
WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8);
CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8);
rc = client->table->SetContextAttributes(
&client->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge,
sizeof(SecPkgContext_AuthNtlmClientChallenge));
WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
rc = client->table->SetContextAttributes(
&client->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge,
sizeof(SecPkgContext_AuthNtlmServerChallenge));
WINPR_ASSERT((rc == SEC_E_OK) || (rc == SEC_E_SECPKG_NOT_FOUND));
}
pSecBuffer = &(client->outputBuffer[0]);
if (!arg->dynamic)
{
pSecBuffer->cbBuffer = sizeof(TEST_NTLM_NEGOTIATE) - 1;
free(pSecBuffer->pvBuffer);
pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
if (!pSecBuffer->pvBuffer)
{
printf("Memory allocation failed\n");
goto fail;
}
CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_NEGOTIATE, pSecBuffer->cbBuffer);
}
(void)fprintf(stderr, "NTLM_NEGOTIATE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
/**
* Server <- Negotiate Message
* Server -> Challenge Message
*/
server->haveInputBuffer = TRUE;
server->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
server->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
server->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
status = test_ntlm_server_authenticate(arg, server);
if (status < 0)
{
printf("test_ntlm_server_authenticate failure\n");
goto fail;
}
if (!arg->dynamic)
{
SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp = WINPR_C_ARRAY_INIT;
SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge = WINPR_C_ARRAY_INIT;
SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge = WINPR_C_ARRAY_INIT;
CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8);
AuthNtlmTimestamp.ChallengeOrResponse = TRUE;
SECURITY_STATUS rc = client->table->SetContextAttributes(
&server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, &AuthNtlmTimestamp,
sizeof(SecPkgContext_AuthNtlmTimestamp));
WINPR_ASSERT(rc == SEC_E_OK);
AuthNtlmTimestamp.ChallengeOrResponse = FALSE;
rc = client->table->SetContextAttributes(&server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP,
&AuthNtlmTimestamp,
sizeof(SecPkgContext_AuthNtlmTimestamp));
WINPR_ASSERT(rc == SEC_E_OK);
CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8);
CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8);
rc = server->table->SetContextAttributes(
&server->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge,
sizeof(SecPkgContext_AuthNtlmClientChallenge));
WINPR_ASSERT(rc == SEC_E_OK);
rc = server->table->SetContextAttributes(
&server->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge,
sizeof(SecPkgContext_AuthNtlmServerChallenge));
WINPR_ASSERT(rc == SEC_E_OK);
}
pSecBuffer = &(server->outputBuffer[0]);
if (!arg->dynamic)
{
SecPkgContext_AuthNtlmMessage AuthNtlmMessage = WINPR_C_ARRAY_INIT;
pSecBuffer->cbBuffer = sizeof(TEST_NTLM_CHALLENGE) - 1;
free(pSecBuffer->pvBuffer);
pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
if (!pSecBuffer->pvBuffer)
{
printf("Memory allocation failed\n");
goto fail;
}
CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_CHALLENGE, pSecBuffer->cbBuffer);
AuthNtlmMessage.type = 2;
AuthNtlmMessage.length = pSecBuffer->cbBuffer;
AuthNtlmMessage.buffer = (BYTE*)pSecBuffer->pvBuffer;
SECURITY_STATUS rc = server->table->SetContextAttributes(
&server->context, SECPKG_ATTR_AUTH_NTLM_MESSAGE, &AuthNtlmMessage,
sizeof(SecPkgContext_AuthNtlmMessage));
if (rc != SEC_E_OK)
goto fail;
}
(void)fprintf(stderr, "NTLM_CHALLENGE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
/**
* Client <- Challenge Message
* Client -> Authenticate Message
*/
client->haveInputBuffer = TRUE;
client->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
client->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
client->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
status = test_ntlm_client_authenticate(client);
if (status < 0)
{
printf("test_ntlm_client_authenticate failure\n");
goto fail;
}
pSecBuffer = &(client->outputBuffer[0]);
if (!arg->dynamic)
{
pSecBuffer->cbBuffer = sizeof(TEST_NTLM_AUTHENTICATE) - 1;
free(pSecBuffer->pvBuffer);
pSecBuffer->pvBuffer = malloc(pSecBuffer->cbBuffer);
if (!pSecBuffer->pvBuffer)
{
printf("Memory allocation failed\n");
goto fail;
}
CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_AUTHENTICATE, pSecBuffer->cbBuffer);
}
(void)fprintf(stderr, "NTLM_AUTHENTICATE (length = %" PRIu32 "):\n", pSecBuffer->cbBuffer);
winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer, pSecBuffer->cbBuffer);
/**
* Server <- Authenticate Message
*/
server->haveInputBuffer = TRUE;
server->inputBuffer[0].BufferType = SECBUFFER_TOKEN;
server->inputBuffer[0].pvBuffer = pSecBuffer->pvBuffer;
server->inputBuffer[0].cbBuffer = pSecBuffer->cbBuffer;
status = test_ntlm_server_authenticate(arg, server);
if (status < 0)
{
printf("test_ntlm_server_authenticate failure\n");
goto fail;
}
rc = TRUE;
fail:
/**
* Cleanup & Termination
*/
test_ntlm_client_free(client);
test_ntlm_server_free(server);
printf("testcase {user=%s, domain=%s, password=%s, dynamic=%s} returns %d\n", arg->user,
arg->domain, arg->pwd, arg->dynamic ? "TRUE" : "FALSE", rc);
return rc;
}
int TestNTLM(int argc, char* argv[])
{
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
const struct test_input_t inputs[] = {
{ TEST_NTLM_USER, TEST_NTLM_DOMAIN, TEST_NTLM_PASSWORD, TEST_NTLM_HASH, TEST_NTLM_V2_HASH,
TRUE, TRUE },
{ TEST_NTLM_USER, TEST_NTLM_DOMAIN, TEST_NTLM_PASSWORD, TEST_NTLM_HASH, TEST_NTLM_V2_HASH,
FALSE, TRUE },
{ TEST_NTLM_USER, TEST_NTLM_DOMAIN, "", TEST_EMPTY_PWD_NTLM_HASH,
TEST_EMPTY_PWD_NTLM_V2_HASH, TRUE, TRUE },
{ TEST_NTLM_USER, TEST_NTLM_DOMAIN, nullptr, TEST_EMPTY_PWD_NTLM_HASH,
TEST_EMPTY_PWD_NTLM_V2_HASH, TRUE, FALSE }
};
int rc = 0;
for (size_t x = 0; x < ARRAYSIZE(inputs); x++)
{
const struct test_input_t* cur = &inputs[x];
const BOOL res = test_default(cur);
if (res != cur->expected)
rc = -1;
}
return rc;
}

View File

@@ -0,0 +1,34 @@
#include <stdio.h>
#include <winpr/sspi.h>
#include <winpr/winpr.h>
#include <winpr/tchar.h>
int TestQuerySecurityPackageInfo(int argc, char* argv[])
{
int rc = 0;
SECURITY_STATUS status = 0;
SecPkgInfo* pPackageInfo = nullptr;
SecurityFunctionTable* table = nullptr;
WINPR_UNUSED(argc);
WINPR_UNUSED(argv);
sspi_GlobalInit();
table = InitSecurityInterfaceEx(0);
status = table->QuerySecurityPackageInfo(NTLM_SSP_NAME, &pPackageInfo);
if (status != SEC_E_OK)
rc = -1;
else
{
_tprintf(_T("\nQuerySecurityPackageInfo:\n"));
_tprintf(_T("\"%s\", \"%s\"\n"), pPackageInfo->Name, pPackageInfo->Comment);
rc = 0;
}
table->FreeContextBuffer(pPackageInfo);
sspi_GlobalFinish();
return rc;
}

View File

@@ -0,0 +1,854 @@
#include <winpr/crt.h>
#include <winpr/sspi.h>
#include <winpr/file.h>
#include <winpr/pipe.h>
#include <winpr/path.h>
#include <winpr/tchar.h>
#include <winpr/print.h>
#include <winpr/synch.h>
#include <winpr/thread.h>
#include <winpr/crypto.h>
#include <winpr/wlog.h>
#include <winpr/schannel.h>
static BOOL g_ClientWait = FALSE;
static BOOL g_ServerWait = FALSE;
static HANDLE g_ClientReadPipe = nullptr;
static HANDLE g_ClientWritePipe = nullptr;
static HANDLE g_ServerReadPipe = nullptr;
static HANDLE g_ServerWritePipe = nullptr;
static const BYTE test_localhost_crt[1029] = {
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49,
0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, 0x4D, 0x49, 0x49, 0x43,
0x79, 0x6A, 0x43, 0x43, 0x41, 0x62, 0x4B, 0x67, 0x41, 0x77, 0x49, 0x42, 0x41, 0x67, 0x49, 0x45,
0x63, 0x61, 0x64, 0x63, 0x72, 0x7A, 0x41, 0x4E, 0x42, 0x67, 0x6B, 0x71, 0x68, 0x6B, 0x69, 0x47,
0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x55, 0x46, 0x41, 0x44, 0x41, 0x55, 0x4D, 0x52, 0x49, 0x77,
0x45, 0x41, 0x59, 0x44, 0x56, 0x51, 0x51, 0x44, 0x45, 0x77, 0x6C, 0x73, 0x0A, 0x62, 0x32, 0x4E,
0x68, 0x62, 0x47, 0x68, 0x76, 0x63, 0x33, 0x51, 0x77, 0x48, 0x68, 0x63, 0x4E, 0x4D, 0x54, 0x4D,
0x78, 0x4D, 0x44, 0x45, 0x78, 0x4D, 0x44, 0x59, 0x78, 0x4E, 0x7A, 0x55, 0x31, 0x57, 0x68, 0x63,
0x4E, 0x4D, 0x54, 0x51, 0x78, 0x4D, 0x44, 0x45, 0x78, 0x4D, 0x44, 0x59, 0x78, 0x4E, 0x7A, 0x55,
0x31, 0x57, 0x6A, 0x41, 0x55, 0x4D, 0x52, 0x49, 0x77, 0x45, 0x41, 0x59, 0x44, 0x0A, 0x56, 0x51,
0x51, 0x44, 0x45, 0x77, 0x6C, 0x73, 0x62, 0x32, 0x4E, 0x68, 0x62, 0x47, 0x68, 0x76, 0x63, 0x33,
0x51, 0x77, 0x67, 0x67, 0x45, 0x69, 0x4D, 0x41, 0x30, 0x47, 0x43, 0x53, 0x71, 0x47, 0x53, 0x49,
0x62, 0x33, 0x44, 0x51, 0x45, 0x42, 0x41, 0x51, 0x55, 0x41, 0x41, 0x34, 0x49, 0x42, 0x44, 0x77,
0x41, 0x77, 0x67, 0x67, 0x45, 0x4B, 0x41, 0x6F, 0x49, 0x42, 0x41, 0x51, 0x43, 0x33, 0x0A, 0x65,
0x6E, 0x33, 0x68, 0x5A, 0x4F, 0x53, 0x33, 0x6B, 0x51, 0x2F, 0x55, 0x54, 0x30, 0x53, 0x45, 0x6C,
0x30, 0x48, 0x6E, 0x50, 0x79, 0x64, 0x48, 0x75, 0x35, 0x39, 0x61, 0x69, 0x71, 0x64, 0x73, 0x64,
0x53, 0x55, 0x74, 0x6E, 0x43, 0x41, 0x37, 0x46, 0x66, 0x74, 0x30, 0x4F, 0x36, 0x51, 0x79, 0x68,
0x49, 0x71, 0x58, 0x7A, 0x30, 0x47, 0x32, 0x53, 0x76, 0x77, 0x4C, 0x54, 0x62, 0x79, 0x68, 0x0A,
0x59, 0x54, 0x68, 0x31, 0x36, 0x78, 0x31, 0x72, 0x45, 0x48, 0x68, 0x31, 0x57, 0x47, 0x5A, 0x6D,
0x36, 0x77, 0x64, 0x2B, 0x4B, 0x76, 0x38, 0x6B, 0x31, 0x6B, 0x2F, 0x36, 0x6F, 0x41, 0x2F, 0x4F,
0x51, 0x76, 0x65, 0x61, 0x38, 0x6B, 0x63, 0x45, 0x64, 0x53, 0x72, 0x54, 0x64, 0x75, 0x71, 0x4A,
0x33, 0x65, 0x66, 0x74, 0x48, 0x4A, 0x4A, 0x6E, 0x43, 0x4B, 0x30, 0x41, 0x62, 0x68, 0x34, 0x39,
0x0A, 0x41, 0x47, 0x41, 0x50, 0x39, 0x79, 0x58, 0x77, 0x77, 0x59, 0x41, 0x6A, 0x51, 0x49, 0x52,
0x6E, 0x38, 0x2B, 0x4F, 0x63, 0x63, 0x48, 0x74, 0x6F, 0x4E, 0x75, 0x75, 0x79, 0x52, 0x63, 0x6B,
0x49, 0x50, 0x71, 0x75, 0x70, 0x78, 0x79, 0x31, 0x4A, 0x5A, 0x4B, 0x39, 0x64, 0x76, 0x76, 0x62,
0x34, 0x79, 0x53, 0x6B, 0x49, 0x75, 0x7A, 0x62, 0x79, 0x50, 0x6F, 0x54, 0x41, 0x79, 0x61, 0x55,
0x2B, 0x0A, 0x51, 0x72, 0x70, 0x34, 0x78, 0x67, 0x64, 0x4B, 0x46, 0x54, 0x70, 0x6B, 0x50, 0x46,
0x34, 0x33, 0x6A, 0x32, 0x4D, 0x6D, 0x5A, 0x72, 0x46, 0x63, 0x42, 0x76, 0x79, 0x6A, 0x69, 0x35,
0x6A, 0x4F, 0x37, 0x74, 0x66, 0x6F, 0x56, 0x61, 0x6B, 0x59, 0x47, 0x53, 0x2F, 0x4C, 0x63, 0x78,
0x77, 0x47, 0x2B, 0x77, 0x51, 0x77, 0x63, 0x4F, 0x43, 0x54, 0x42, 0x45, 0x78, 0x2F, 0x7A, 0x31,
0x53, 0x30, 0x0A, 0x37, 0x49, 0x2F, 0x6A, 0x62, 0x44, 0x79, 0x53, 0x4E, 0x68, 0x44, 0x35, 0x63,
0x61, 0x63, 0x54, 0x75, 0x4E, 0x36, 0x50, 0x68, 0x33, 0x58, 0x30, 0x71, 0x70, 0x47, 0x73, 0x37,
0x79, 0x50, 0x6B, 0x4E, 0x79, 0x69, 0x4A, 0x33, 0x57, 0x52, 0x69, 0x6C, 0x35, 0x75, 0x57, 0x73,
0x4B, 0x65, 0x79, 0x63, 0x64, 0x71, 0x42, 0x4E, 0x72, 0x34, 0x75, 0x32, 0x62, 0x49, 0x52, 0x6E,
0x63, 0x54, 0x51, 0x0A, 0x46, 0x72, 0x68, 0x73, 0x58, 0x39, 0x69, 0x77, 0x37, 0x35, 0x76, 0x75,
0x53, 0x64, 0x35, 0x46, 0x39, 0x37, 0x56, 0x70, 0x41, 0x67, 0x4D, 0x42, 0x41, 0x41, 0x47, 0x6A,
0x4A, 0x44, 0x41, 0x69, 0x4D, 0x42, 0x4D, 0x47, 0x41, 0x31, 0x55, 0x64, 0x4A, 0x51, 0x51, 0x4D,
0x4D, 0x41, 0x6F, 0x47, 0x43, 0x43, 0x73, 0x47, 0x41, 0x51, 0x55, 0x46, 0x42, 0x77, 0x4D, 0x42,
0x4D, 0x41, 0x73, 0x47, 0x0A, 0x41, 0x31, 0x55, 0x64, 0x44, 0x77, 0x51, 0x45, 0x41, 0x77, 0x49,
0x45, 0x4D, 0x44, 0x41, 0x4E, 0x42, 0x67, 0x6B, 0x71, 0x68, 0x6B, 0x69, 0x47, 0x39, 0x77, 0x30,
0x42, 0x41, 0x51, 0x55, 0x46, 0x41, 0x41, 0x4F, 0x43, 0x41, 0x51, 0x45, 0x41, 0x49, 0x51, 0x66,
0x75, 0x2F, 0x77, 0x39, 0x45, 0x34, 0x4C, 0x6F, 0x67, 0x30, 0x71, 0x35, 0x4B, 0x53, 0x38, 0x71,
0x46, 0x78, 0x62, 0x36, 0x6F, 0x0A, 0x36, 0x31, 0x62, 0x35, 0x37, 0x6F, 0x6D, 0x6E, 0x46, 0x59,
0x52, 0x34, 0x47, 0x43, 0x67, 0x33, 0x6F, 0x6A, 0x4F, 0x4C, 0x54, 0x66, 0x38, 0x7A, 0x6A, 0x4D,
0x43, 0x52, 0x6D, 0x75, 0x59, 0x32, 0x76, 0x30, 0x4E, 0x34, 0x78, 0x66, 0x68, 0x69, 0x35, 0x4B,
0x69, 0x59, 0x67, 0x64, 0x76, 0x4E, 0x4C, 0x4F, 0x33, 0x52, 0x42, 0x6D, 0x4E, 0x50, 0x76, 0x59,
0x58, 0x50, 0x52, 0x46, 0x41, 0x76, 0x0A, 0x66, 0x61, 0x76, 0x66, 0x57, 0x75, 0x6C, 0x44, 0x31,
0x64, 0x50, 0x36, 0x31, 0x69, 0x35, 0x62, 0x36, 0x59, 0x66, 0x56, 0x6C, 0x78, 0x62, 0x31, 0x61,
0x57, 0x46, 0x37, 0x4C, 0x5A, 0x44, 0x32, 0x55, 0x6E, 0x63, 0x41, 0x6A, 0x37, 0x4E, 0x38, 0x78,
0x38, 0x2B, 0x36, 0x58, 0x6B, 0x30, 0x6B, 0x63, 0x70, 0x58, 0x46, 0x38, 0x6C, 0x77, 0x58, 0x48,
0x55, 0x57, 0x57, 0x55, 0x6D, 0x73, 0x2B, 0x0A, 0x4B, 0x56, 0x44, 0x34, 0x34, 0x39, 0x68, 0x6F,
0x4D, 0x2B, 0x77, 0x4E, 0x4A, 0x49, 0x61, 0x4F, 0x52, 0x39, 0x4C, 0x46, 0x2B, 0x6B, 0x6F, 0x32,
0x32, 0x37, 0x7A, 0x74, 0x37, 0x54, 0x41, 0x47, 0x64, 0x56, 0x35, 0x4A, 0x75, 0x7A, 0x71, 0x38,
0x32, 0x2F, 0x6B, 0x75, 0x73, 0x6F, 0x65, 0x32, 0x69, 0x75, 0x57, 0x77, 0x54, 0x65, 0x42, 0x6C,
0x53, 0x5A, 0x6E, 0x6B, 0x42, 0x38, 0x63, 0x64, 0x0A, 0x77, 0x4D, 0x30, 0x5A, 0x42, 0x58, 0x6D,
0x34, 0x35, 0x48, 0x38, 0x6F, 0x79, 0x75, 0x36, 0x4A, 0x71, 0x59, 0x71, 0x45, 0x6D, 0x75, 0x4A,
0x51, 0x64, 0x67, 0x79, 0x52, 0x2B, 0x63, 0x53, 0x53, 0x41, 0x7A, 0x2B, 0x4F, 0x32, 0x6D, 0x61,
0x62, 0x68, 0x50, 0x5A, 0x65, 0x49, 0x76, 0x78, 0x65, 0x67, 0x6A, 0x6A, 0x61, 0x5A, 0x61, 0x46,
0x4F, 0x71, 0x74, 0x73, 0x2B, 0x64, 0x33, 0x72, 0x39, 0x0A, 0x79, 0x71, 0x4A, 0x78, 0x67, 0x75,
0x39, 0x43, 0x38, 0x39, 0x5A, 0x69, 0x33, 0x39, 0x57, 0x34, 0x38, 0x46, 0x66, 0x46, 0x63, 0x49,
0x58, 0x4A, 0x4F, 0x6B, 0x39, 0x43, 0x4E, 0x46, 0x41, 0x2F, 0x69, 0x70, 0x54, 0x57, 0x6A, 0x74,
0x74, 0x4E, 0x2F, 0x6B, 0x4F, 0x6B, 0x5A, 0x42, 0x70, 0x6F, 0x6A, 0x2F, 0x32, 0x6A, 0x4E, 0x45,
0x62, 0x4F, 0x59, 0x7A, 0x7A, 0x6E, 0x4B, 0x77, 0x3D, 0x3D, 0x0A, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D,
0x45, 0x4E, 0x44, 0x20, 0x43, 0x45, 0x52, 0x54, 0x49, 0x46, 0x49, 0x43, 0x41, 0x54, 0x45, 0x2D,
0x2D, 0x2D, 0x2D, 0x2D, 0x0A
};
static const BYTE test_localhost_key[1704] = {
0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x42, 0x45, 0x47, 0x49, 0x4E, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41,
0x54, 0x45, 0x20, 0x4B, 0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A, 0x4D, 0x49, 0x49, 0x45,
0x76, 0x51, 0x49, 0x42, 0x41, 0x44, 0x41, 0x4E, 0x42, 0x67, 0x6B, 0x71, 0x68, 0x6B, 0x69, 0x47,
0x39, 0x77, 0x30, 0x42, 0x41, 0x51, 0x45, 0x46, 0x41, 0x41, 0x53, 0x43, 0x42, 0x4B, 0x63, 0x77,
0x67, 0x67, 0x53, 0x6A, 0x41, 0x67, 0x45, 0x41, 0x41, 0x6F, 0x49, 0x42, 0x41, 0x51, 0x43, 0x33,
0x65, 0x6E, 0x33, 0x68, 0x5A, 0x4F, 0x53, 0x33, 0x6B, 0x51, 0x2F, 0x55, 0x0A, 0x54, 0x30, 0x53,
0x45, 0x6C, 0x30, 0x48, 0x6E, 0x50, 0x79, 0x64, 0x48, 0x75, 0x35, 0x39, 0x61, 0x69, 0x71, 0x64,
0x73, 0x64, 0x53, 0x55, 0x74, 0x6E, 0x43, 0x41, 0x37, 0x46, 0x66, 0x74, 0x30, 0x4F, 0x36, 0x51,
0x79, 0x68, 0x49, 0x71, 0x58, 0x7A, 0x30, 0x47, 0x32, 0x53, 0x76, 0x77, 0x4C, 0x54, 0x62, 0x79,
0x68, 0x59, 0x54, 0x68, 0x31, 0x36, 0x78, 0x31, 0x72, 0x45, 0x48, 0x68, 0x31, 0x0A, 0x57, 0x47,
0x5A, 0x6D, 0x36, 0x77, 0x64, 0x2B, 0x4B, 0x76, 0x38, 0x6B, 0x31, 0x6B, 0x2F, 0x36, 0x6F, 0x41,
0x2F, 0x4F, 0x51, 0x76, 0x65, 0x61, 0x38, 0x6B, 0x63, 0x45, 0x64, 0x53, 0x72, 0x54, 0x64, 0x75,
0x71, 0x4A, 0x33, 0x65, 0x66, 0x74, 0x48, 0x4A, 0x4A, 0x6E, 0x43, 0x4B, 0x30, 0x41, 0x62, 0x68,
0x34, 0x39, 0x41, 0x47, 0x41, 0x50, 0x39, 0x79, 0x58, 0x77, 0x77, 0x59, 0x41, 0x6A, 0x0A, 0x51,
0x49, 0x52, 0x6E, 0x38, 0x2B, 0x4F, 0x63, 0x63, 0x48, 0x74, 0x6F, 0x4E, 0x75, 0x75, 0x79, 0x52,
0x63, 0x6B, 0x49, 0x50, 0x71, 0x75, 0x70, 0x78, 0x79, 0x31, 0x4A, 0x5A, 0x4B, 0x39, 0x64, 0x76,
0x76, 0x62, 0x34, 0x79, 0x53, 0x6B, 0x49, 0x75, 0x7A, 0x62, 0x79, 0x50, 0x6F, 0x54, 0x41, 0x79,
0x61, 0x55, 0x2B, 0x51, 0x72, 0x70, 0x34, 0x78, 0x67, 0x64, 0x4B, 0x46, 0x54, 0x70, 0x6B, 0x0A,
0x50, 0x46, 0x34, 0x33, 0x6A, 0x32, 0x4D, 0x6D, 0x5A, 0x72, 0x46, 0x63, 0x42, 0x76, 0x79, 0x6A,
0x69, 0x35, 0x6A, 0x4F, 0x37, 0x74, 0x66, 0x6F, 0x56, 0x61, 0x6B, 0x59, 0x47, 0x53, 0x2F, 0x4C,
0x63, 0x78, 0x77, 0x47, 0x2B, 0x77, 0x51, 0x77, 0x63, 0x4F, 0x43, 0x54, 0x42, 0x45, 0x78, 0x2F,
0x7A, 0x31, 0x53, 0x30, 0x37, 0x49, 0x2F, 0x6A, 0x62, 0x44, 0x79, 0x53, 0x4E, 0x68, 0x44, 0x35,
0x0A, 0x63, 0x61, 0x63, 0x54, 0x75, 0x4E, 0x36, 0x50, 0x68, 0x33, 0x58, 0x30, 0x71, 0x70, 0x47,
0x73, 0x37, 0x79, 0x50, 0x6B, 0x4E, 0x79, 0x69, 0x4A, 0x33, 0x57, 0x52, 0x69, 0x6C, 0x35, 0x75,
0x57, 0x73, 0x4B, 0x65, 0x79, 0x63, 0x64, 0x71, 0x42, 0x4E, 0x72, 0x34, 0x75, 0x32, 0x62, 0x49,
0x52, 0x6E, 0x63, 0x54, 0x51, 0x46, 0x72, 0x68, 0x73, 0x58, 0x39, 0x69, 0x77, 0x37, 0x35, 0x76,
0x75, 0x0A, 0x53, 0x64, 0x35, 0x46, 0x39, 0x37, 0x56, 0x70, 0x41, 0x67, 0x4D, 0x42, 0x41, 0x41,
0x45, 0x43, 0x67, 0x67, 0x45, 0x41, 0x42, 0x36, 0x6A, 0x6C, 0x65, 0x48, 0x4E, 0x74, 0x32, 0x50,
0x77, 0x46, 0x58, 0x53, 0x65, 0x79, 0x42, 0x4A, 0x63, 0x4C, 0x2B, 0x55, 0x74, 0x35, 0x71, 0x46,
0x54, 0x38, 0x34, 0x68, 0x72, 0x48, 0x77, 0x6F, 0x39, 0x68, 0x62, 0x66, 0x59, 0x47, 0x6F, 0x6E,
0x44, 0x59, 0x0A, 0x66, 0x70, 0x47, 0x2B, 0x32, 0x52, 0x30, 0x50, 0x62, 0x43, 0x63, 0x4B, 0x35,
0x30, 0x46, 0x61, 0x4A, 0x46, 0x36, 0x71, 0x63, 0x56, 0x4A, 0x4E, 0x75, 0x52, 0x36, 0x48, 0x71,
0x2B, 0x43, 0x55, 0x4A, 0x74, 0x48, 0x35, 0x39, 0x48, 0x48, 0x37, 0x62, 0x68, 0x6A, 0x39, 0x62,
0x64, 0x78, 0x45, 0x6D, 0x6F, 0x48, 0x30, 0x4A, 0x76, 0x68, 0x45, 0x76, 0x67, 0x4D, 0x2F, 0x55,
0x38, 0x42, 0x51, 0x0A, 0x65, 0x57, 0x4F, 0x4E, 0x68, 0x78, 0x50, 0x73, 0x69, 0x73, 0x6D, 0x57,
0x6B, 0x78, 0x61, 0x5A, 0x6F, 0x6C, 0x72, 0x32, 0x69, 0x44, 0x56, 0x72, 0x7A, 0x54, 0x37, 0x55,
0x4A, 0x71, 0x6A, 0x74, 0x59, 0x49, 0x74, 0x67, 0x2B, 0x37, 0x59, 0x43, 0x32, 0x70, 0x55, 0x58,
0x6B, 0x64, 0x49, 0x35, 0x4A, 0x4D, 0x67, 0x6C, 0x44, 0x47, 0x4D, 0x52, 0x5A, 0x35, 0x55, 0x5A,
0x48, 0x75, 0x63, 0x7A, 0x0A, 0x41, 0x56, 0x2B, 0x71, 0x77, 0x77, 0x33, 0x65, 0x45, 0x52, 0x74,
0x78, 0x44, 0x50, 0x61, 0x61, 0x61, 0x34, 0x54, 0x39, 0x50, 0x64, 0x33, 0x44, 0x31, 0x6D, 0x62,
0x71, 0x58, 0x66, 0x75, 0x45, 0x68, 0x42, 0x6D, 0x33, 0x51, 0x6F, 0x2B, 0x75, 0x7A, 0x51, 0x32,
0x36, 0x76, 0x73, 0x66, 0x48, 0x75, 0x56, 0x76, 0x61, 0x39, 0x38, 0x32, 0x4F, 0x6A, 0x41, 0x55,
0x6A, 0x6E, 0x64, 0x30, 0x70, 0x0A, 0x77, 0x43, 0x53, 0x6E, 0x42, 0x49, 0x48, 0x67, 0x70, 0x73,
0x30, 0x79, 0x61, 0x45, 0x50, 0x63, 0x37, 0x46, 0x78, 0x39, 0x71, 0x45, 0x63, 0x6D, 0x33, 0x70,
0x7A, 0x41, 0x56, 0x31, 0x69, 0x72, 0x31, 0x4E, 0x4E, 0x63, 0x51, 0x47, 0x55, 0x45, 0x75, 0x45,
0x6C, 0x4A, 0x78, 0x76, 0x2B, 0x69, 0x57, 0x34, 0x6D, 0x35, 0x70, 0x7A, 0x4C, 0x6A, 0x64, 0x53,
0x63, 0x49, 0x30, 0x59, 0x45, 0x73, 0x0A, 0x4D, 0x61, 0x33, 0x78, 0x32, 0x79, 0x48, 0x74, 0x6E,
0x77, 0x79, 0x65, 0x4C, 0x4D, 0x54, 0x4B, 0x6C, 0x72, 0x46, 0x4B, 0x70, 0x55, 0x4E, 0x4A, 0x62,
0x78, 0x73, 0x35, 0x32, 0x62, 0x5A, 0x4B, 0x71, 0x49, 0x56, 0x33, 0x33, 0x4A, 0x53, 0x34, 0x41,
0x51, 0x4B, 0x42, 0x67, 0x51, 0x44, 0x73, 0x4C, 0x54, 0x49, 0x68, 0x35, 0x59, 0x38, 0x4C, 0x2F,
0x48, 0x33, 0x64, 0x74, 0x68, 0x63, 0x62, 0x0A, 0x53, 0x43, 0x45, 0x77, 0x32, 0x64, 0x42, 0x49,
0x76, 0x49, 0x79, 0x54, 0x7A, 0x39, 0x53, 0x72, 0x62, 0x33, 0x58, 0x37, 0x37, 0x41, 0x77, 0x57,
0x45, 0x4C, 0x53, 0x4D, 0x49, 0x57, 0x53, 0x50, 0x55, 0x43, 0x4B, 0x54, 0x49, 0x70, 0x6A, 0x4D,
0x73, 0x6E, 0x7A, 0x6B, 0x46, 0x67, 0x32, 0x32, 0x59, 0x32, 0x53, 0x75, 0x47, 0x38, 0x4C, 0x72,
0x50, 0x6D, 0x76, 0x73, 0x46, 0x4A, 0x34, 0x30, 0x0A, 0x32, 0x67, 0x35, 0x44, 0x55, 0x6C, 0x59,
0x33, 0x59, 0x6D, 0x53, 0x4F, 0x46, 0x61, 0x45, 0x4A, 0x54, 0x70, 0x55, 0x47, 0x44, 0x4D, 0x79,
0x65, 0x33, 0x74, 0x36, 0x4F, 0x30, 0x6C, 0x63, 0x51, 0x41, 0x66, 0x79, 0x6D, 0x58, 0x66, 0x41,
0x38, 0x74, 0x50, 0x42, 0x48, 0x6A, 0x5A, 0x78, 0x56, 0x61, 0x38, 0x78, 0x78, 0x52, 0x5A, 0x6E,
0x56, 0x43, 0x31, 0x41, 0x62, 0x75, 0x49, 0x49, 0x52, 0x0A, 0x6E, 0x77, 0x72, 0x4E, 0x46, 0x2B,
0x42, 0x6F, 0x53, 0x4B, 0x55, 0x41, 0x73, 0x78, 0x2B, 0x46, 0x75, 0x35, 0x5A, 0x4A, 0x4B, 0x4F,
0x66, 0x79, 0x4D, 0x51, 0x4B, 0x42, 0x67, 0x51, 0x44, 0x47, 0x34, 0x50, 0x52, 0x39, 0x2F, 0x58,
0x58, 0x6B, 0x51, 0x54, 0x36, 0x6B, 0x7A, 0x4B, 0x64, 0x34, 0x50, 0x6C, 0x50, 0x4D, 0x63, 0x2B,
0x4B, 0x51, 0x79, 0x4C, 0x45, 0x6C, 0x4B, 0x39, 0x71, 0x47, 0x0A, 0x41, 0x6D, 0x6E, 0x2F, 0x31,
0x68, 0x64, 0x69, 0x57, 0x57, 0x4F, 0x52, 0x57, 0x46, 0x62, 0x32, 0x38, 0x30, 0x4D, 0x77, 0x76,
0x77, 0x41, 0x64, 0x78, 0x72, 0x66, 0x65, 0x4C, 0x57, 0x4D, 0x57, 0x32, 0x66, 0x76, 0x4C, 0x59,
0x4B, 0x66, 0x6C, 0x4F, 0x35, 0x50, 0x51, 0x44, 0x59, 0x67, 0x4B, 0x4A, 0x78, 0x35, 0x79, 0x50,
0x37, 0x52, 0x64, 0x38, 0x2F, 0x64, 0x50, 0x79, 0x5A, 0x59, 0x36, 0x0A, 0x7A, 0x56, 0x37, 0x47,
0x47, 0x6B, 0x51, 0x5A, 0x42, 0x4B, 0x36, 0x79, 0x74, 0x61, 0x66, 0x32, 0x35, 0x44, 0x50, 0x67,
0x50, 0x72, 0x32, 0x77, 0x73, 0x59, 0x4D, 0x43, 0x6C, 0x53, 0x74, 0x6C, 0x56, 0x74, 0x72, 0x6D,
0x4F, 0x78, 0x59, 0x55, 0x56, 0x77, 0x42, 0x59, 0x4F, 0x69, 0x36, 0x45, 0x62, 0x50, 0x69, 0x6B,
0x78, 0x47, 0x48, 0x5A, 0x70, 0x59, 0x6F, 0x5A, 0x5A, 0x70, 0x68, 0x4A, 0x0A, 0x4E, 0x61, 0x38,
0x4F, 0x4C, 0x31, 0x69, 0x77, 0x75, 0x51, 0x4B, 0x42, 0x67, 0x51, 0x44, 0x42, 0x55, 0x55, 0x31,
0x54, 0x79, 0x5A, 0x2B, 0x4A, 0x5A, 0x43, 0x64, 0x79, 0x72, 0x33, 0x58, 0x43, 0x63, 0x77, 0x77,
0x58, 0x2F, 0x48, 0x49, 0x73, 0x31, 0x34, 0x6B, 0x4B, 0x42, 0x48, 0x68, 0x44, 0x79, 0x33, 0x78,
0x37, 0x74, 0x50, 0x38, 0x2F, 0x6F, 0x48, 0x54, 0x6F, 0x72, 0x76, 0x79, 0x74, 0x0A, 0x41, 0x68,
0x38, 0x4B, 0x36, 0x4B, 0x72, 0x43, 0x41, 0x75, 0x65, 0x50, 0x6D, 0x79, 0x32, 0x6D, 0x4F, 0x54,
0x31, 0x54, 0x39, 0x6F, 0x31, 0x61, 0x47, 0x55, 0x49, 0x6C, 0x66, 0x38, 0x72, 0x76, 0x33, 0x2F,
0x30, 0x45, 0x78, 0x67, 0x53, 0x6B, 0x57, 0x50, 0x6D, 0x4F, 0x41, 0x38, 0x35, 0x49, 0x32, 0x2F,
0x58, 0x48, 0x65, 0x66, 0x71, 0x54, 0x6F, 0x45, 0x48, 0x30, 0x44, 0x65, 0x41, 0x4E, 0x0A, 0x7A,
0x6C, 0x4B, 0x4C, 0x71, 0x79, 0x44, 0x56, 0x30, 0x42, 0x56, 0x4E, 0x76, 0x48, 0x42, 0x57, 0x79,
0x32, 0x49, 0x51, 0x35, 0x62, 0x50, 0x42, 0x57, 0x76, 0x30, 0x37, 0x63, 0x34, 0x2B, 0x6A, 0x39,
0x4E, 0x62, 0x57, 0x67, 0x64, 0x44, 0x43, 0x43, 0x35, 0x52, 0x6B, 0x4F, 0x6A, 0x70, 0x33, 0x4D,
0x4E, 0x45, 0x58, 0x47, 0x56, 0x43, 0x69, 0x51, 0x51, 0x4B, 0x42, 0x67, 0x43, 0x7A, 0x4D, 0x0A,
0x77, 0x65, 0x61, 0x62, 0x73, 0x50, 0x48, 0x68, 0x44, 0x4B, 0x5A, 0x38, 0x2F, 0x34, 0x43, 0x6A,
0x73, 0x61, 0x62, 0x4E, 0x75, 0x41, 0x7A, 0x62, 0x57, 0x4B, 0x52, 0x42, 0x38, 0x37, 0x44, 0x61,
0x58, 0x46, 0x78, 0x6F, 0x4D, 0x73, 0x35, 0x52, 0x79, 0x6F, 0x38, 0x55, 0x4D, 0x6B, 0x72, 0x67,
0x30, 0x35, 0x4C, 0x6F, 0x67, 0x37, 0x4D, 0x78, 0x62, 0x33, 0x76, 0x61, 0x42, 0x34, 0x63, 0x2F,
0x0A, 0x52, 0x57, 0x77, 0x7A, 0x38, 0x72, 0x34, 0x39, 0x70, 0x48, 0x64, 0x71, 0x68, 0x4F, 0x6D,
0x63, 0x6C, 0x45, 0x77, 0x79, 0x4D, 0x34, 0x51, 0x79, 0x6A, 0x39, 0x52, 0x6D, 0x57, 0x62, 0x51,
0x58, 0x54, 0x54, 0x45, 0x63, 0x2B, 0x35, 0x67, 0x54, 0x4B, 0x50, 0x4E, 0x53, 0x33, 0x6D, 0x70,
0x4D, 0x54, 0x36, 0x39, 0x46, 0x45, 0x74, 0x2F, 0x35, 0x72, 0x4D, 0x52, 0x70, 0x4B, 0x2B, 0x52,
0x68, 0x0A, 0x49, 0x32, 0x42, 0x58, 0x6B, 0x51, 0x71, 0x31, 0x36, 0x6E, 0x72, 0x31, 0x61, 0x45,
0x4D, 0x6D, 0x64, 0x51, 0x42, 0x51, 0x79, 0x4B, 0x59, 0x4A, 0x6C, 0x30, 0x6C, 0x50, 0x68, 0x69,
0x42, 0x2F, 0x75, 0x6C, 0x5A, 0x63, 0x72, 0x67, 0x4C, 0x70, 0x41, 0x6F, 0x47, 0x41, 0x65, 0x30,
0x65, 0x74, 0x50, 0x4A, 0x77, 0x6D, 0x51, 0x46, 0x6B, 0x6A, 0x4D, 0x70, 0x66, 0x4D, 0x44, 0x61,
0x4E, 0x34, 0x0A, 0x70, 0x7A, 0x71, 0x45, 0x51, 0x72, 0x52, 0x35, 0x4B, 0x35, 0x4D, 0x6E, 0x54,
0x48, 0x76, 0x47, 0x67, 0x2F, 0x70, 0x6A, 0x57, 0x6A, 0x43, 0x57, 0x58, 0x56, 0x48, 0x67, 0x35,
0x76, 0x36, 0x46, 0x6F, 0x5A, 0x48, 0x35, 0x6E, 0x59, 0x2B, 0x56, 0x2F, 0x57, 0x75, 0x57, 0x38,
0x38, 0x6A, 0x6C, 0x4B, 0x53, 0x50, 0x6C, 0x77, 0x6A, 0x50, 0x7A, 0x41, 0x67, 0x7A, 0x47, 0x33,
0x45, 0x41, 0x55, 0x0A, 0x71, 0x57, 0x6B, 0x42, 0x67, 0x30, 0x71, 0x75, 0x50, 0x4D, 0x72, 0x54,
0x6B, 0x73, 0x69, 0x6E, 0x58, 0x50, 0x2B, 0x58, 0x6B, 0x51, 0x65, 0x46, 0x66, 0x58, 0x61, 0x33,
0x38, 0x6A, 0x72, 0x70, 0x62, 0x4B, 0x46, 0x4F, 0x72, 0x7A, 0x49, 0x6F, 0x6A, 0x69, 0x65, 0x6C,
0x4B, 0x55, 0x4D, 0x50, 0x4D, 0x78, 0x2F, 0x78, 0x70, 0x53, 0x6A, 0x63, 0x55, 0x42, 0x68, 0x62,
0x4E, 0x34, 0x45, 0x54, 0x0A, 0x4F, 0x30, 0x66, 0x63, 0x57, 0x47, 0x6F, 0x61, 0x56, 0x50, 0x72,
0x63, 0x6E, 0x38, 0x62, 0x58, 0x4D, 0x54, 0x45, 0x4E, 0x53, 0x31, 0x41, 0x3D, 0x0A, 0x2D, 0x2D,
0x2D, 0x2D, 0x2D, 0x45, 0x4E, 0x44, 0x20, 0x50, 0x52, 0x49, 0x56, 0x41, 0x54, 0x45, 0x20, 0x4B,
0x45, 0x59, 0x2D, 0x2D, 0x2D, 0x2D, 0x2D, 0x0A
};
static const BYTE test_DummyMessage[64] = {
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC, 0xCC,
0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD, 0xDD
};
static const BYTE test_LastDummyMessage[64] = {
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
};
static int schannel_send(PSecurityFunctionTable table, HANDLE hPipe, PCtxtHandle phContext,
BYTE* buffer, UINT32 length)
{
BYTE* ioBuffer;
UINT32 ioBufferLength;
BYTE* pMessageBuffer;
SecBuffer Buffers[4] = WINPR_C_ARRAY_INIT;
SecBufferDesc Message;
SECURITY_STATUS status;
DWORD NumberOfBytesWritten;
SecPkgContext_StreamSizes StreamSizes = WINPR_C_ARRAY_INIT;
status = table->QueryContextAttributes(phContext, SECPKG_ATTR_STREAM_SIZES, &StreamSizes);
ioBufferLength = StreamSizes.cbHeader + StreamSizes.cbMaximumMessage + StreamSizes.cbTrailer;
ioBuffer = (BYTE*)calloc(1, ioBufferLength);
if (!ioBuffer)
return -1;
pMessageBuffer = ioBuffer + StreamSizes.cbHeader;
CopyMemory(pMessageBuffer, buffer, length);
Buffers[0].pvBuffer = ioBuffer;
Buffers[0].cbBuffer = StreamSizes.cbHeader;
Buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
Buffers[1].pvBuffer = pMessageBuffer;
Buffers[1].cbBuffer = length;
Buffers[1].BufferType = SECBUFFER_DATA;
Buffers[2].pvBuffer = pMessageBuffer + length;
Buffers[2].cbBuffer = StreamSizes.cbTrailer;
Buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
Buffers[3].pvBuffer = nullptr;
Buffers[3].cbBuffer = 0;
Buffers[3].BufferType = SECBUFFER_EMPTY;
Message.ulVersion = SECBUFFER_VERSION;
Message.cBuffers = 4;
Message.pBuffers = Buffers;
ioBufferLength =
Message.pBuffers[0].cbBuffer + Message.pBuffers[1].cbBuffer + Message.pBuffers[2].cbBuffer;
status = table->EncryptMessage(phContext, 0, &Message, 0);
printf("EncryptMessage status: 0x%08" PRIX32 "\n", status);
printf("EncryptMessage output: cBuffers: %" PRIu32 " [0]: %" PRIu32 " / %" PRIu32
" [1]: %" PRIu32 " / %" PRIu32 " [2]: %" PRIu32 " / %" PRIu32 " [3]: %" PRIu32
" / %" PRIu32 "\n",
Message.cBuffers, Message.pBuffers[0].cbBuffer, Message.pBuffers[0].BufferType,
Message.pBuffers[1].cbBuffer, Message.pBuffers[1].BufferType,
Message.pBuffers[2].cbBuffer, Message.pBuffers[2].BufferType,
Message.pBuffers[3].cbBuffer, Message.pBuffers[3].BufferType);
if (status != SEC_E_OK)
return -1;
printf("Client > Server (%" PRIu32 ")\n", ioBufferLength);
winpr_HexDump("sspi.test", WLOG_DEBUG, ioBuffer, ioBufferLength);
if (!WriteFile(hPipe, ioBuffer, ioBufferLength, &NumberOfBytesWritten, nullptr))
{
printf("schannel_send: failed to write to pipe\n");
return -1;
}
return 0;
}
static int schannel_recv(PSecurityFunctionTable table, HANDLE hPipe, PCtxtHandle phContext)
{
BYTE* ioBuffer;
UINT32 ioBufferLength;
// BYTE* pMessageBuffer;
SecBuffer Buffers[4] = WINPR_C_ARRAY_INIT;
SecBufferDesc Message;
SECURITY_STATUS status;
DWORD NumberOfBytesRead;
SecPkgContext_StreamSizes StreamSizes = WINPR_C_ARRAY_INIT;
status = table->QueryContextAttributes(phContext, SECPKG_ATTR_STREAM_SIZES, &StreamSizes);
ioBufferLength = StreamSizes.cbHeader + StreamSizes.cbMaximumMessage + StreamSizes.cbTrailer;
ioBuffer = (BYTE*)calloc(1, ioBufferLength);
if (!ioBuffer)
return -1;
if (!ReadFile(hPipe, ioBuffer, ioBufferLength, &NumberOfBytesRead, nullptr))
{
printf("schannel_recv: failed to read from pipe\n");
return -1;
}
Buffers[0].pvBuffer = ioBuffer;
Buffers[0].cbBuffer = NumberOfBytesRead;
Buffers[0].BufferType = SECBUFFER_DATA;
Buffers[1].pvBuffer = nullptr;
Buffers[1].cbBuffer = 0;
Buffers[1].BufferType = SECBUFFER_EMPTY;
Buffers[2].pvBuffer = nullptr;
Buffers[2].cbBuffer = 0;
Buffers[2].BufferType = SECBUFFER_EMPTY;
Buffers[3].pvBuffer = nullptr;
Buffers[3].cbBuffer = 0;
Buffers[3].BufferType = SECBUFFER_EMPTY;
Message.ulVersion = SECBUFFER_VERSION;
Message.cBuffers = 4;
Message.pBuffers = Buffers;
status = table->DecryptMessage(phContext, &Message, 0, nullptr);
printf("DecryptMessage status: 0x%08" PRIX32 "\n", status);
printf("DecryptMessage output: cBuffers: %" PRIu32 " [0]: %" PRIu32 " / %" PRIu32
" [1]: %" PRIu32 " / %" PRIu32 " [2]: %" PRIu32 " / %" PRIu32 " [3]: %" PRIu32
" / %" PRIu32 "\n",
Message.cBuffers, Message.pBuffers[0].cbBuffer, Message.pBuffers[0].BufferType,
Message.pBuffers[1].cbBuffer, Message.pBuffers[1].BufferType,
Message.pBuffers[2].cbBuffer, Message.pBuffers[2].BufferType,
Message.pBuffers[3].cbBuffer, Message.pBuffers[3].BufferType);
if (status != SEC_E_OK)
return -1;
printf("Decrypted Message (%" PRIu32 ")\n", Message.pBuffers[1].cbBuffer);
winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)Message.pBuffers[1].pvBuffer,
Message.pBuffers[1].cbBuffer);
if (memcmp(Message.pBuffers[1].pvBuffer, test_LastDummyMessage,
sizeof(test_LastDummyMessage)) == 0)
return -1;
return 0;
}
static DWORD WINAPI schannel_test_server_thread(LPVOID arg)
{
BOOL extraData;
BYTE* lpTokenIn;
BYTE* lpTokenOut;
TimeStamp expiry;
UINT32 cbMaxToken;
UINT32 fContextReq;
ULONG fContextAttr;
SCHANNEL_CRED cred = WINPR_C_ARRAY_INIT;
CtxtHandle context;
CredHandle credentials;
DWORD cchNameString;
LPTSTR pszNameString;
HCERTSTORE hCertStore;
PCCERT_CONTEXT pCertContext;
PSecBuffer pSecBuffer;
SecBuffer SecBuffer_in[2] = WINPR_C_ARRAY_INIT;
SecBuffer SecBuffer_out[2] = WINPR_C_ARRAY_INIT;
SecBufferDesc SecBufferDesc_in;
SecBufferDesc SecBufferDesc_out;
DWORD NumberOfBytesRead;
SECURITY_STATUS status;
PSecPkgInfo pPackageInfo;
PSecurityFunctionTable table;
DWORD NumberOfBytesWritten;
printf("Starting Server\n");
SecInvalidateHandle(&context);
SecInvalidateHandle(&credentials);
table = InitSecurityInterface();
status = QuerySecurityPackageInfo(SCHANNEL_NAME, &pPackageInfo);
if (status != SEC_E_OK)
{
printf("QuerySecurityPackageInfo failure: 0x%08" PRIX32 "\n", status);
return 0;
}
cbMaxToken = pPackageInfo->cbMaxToken;
hCertStore = CertOpenSystemStore(0, _T("MY"));
if (!hCertStore)
{
printf("Error opening system store\n");
// return nullptr;
}
#ifdef CERT_FIND_HAS_PRIVATE_KEY
pCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0,
CERT_FIND_HAS_PRIVATE_KEY, nullptr, nullptr);
#else
pCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY,
nullptr, nullptr);
#endif
if (!pCertContext)
{
printf("Error finding certificate in store\n");
// return nullptr;
}
cchNameString =
CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr, nullptr, 0);
pszNameString = (LPTSTR)malloc(cchNameString * sizeof(TCHAR));
if (!pszNameString)
{
printf("Memory allocation failed\n");
return 0;
}
cchNameString = CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, nullptr,
pszNameString, cchNameString);
_tprintf(_T("Certificate Name: %s\n"), pszNameString);
cred.dwVersion = SCHANNEL_CRED_VERSION;
cred.cCreds = 1;
cred.paCred = &pCertContext;
cred.cSupportedAlgs = 0;
cred.palgSupportedAlgs = nullptr;
cred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER;
cred.dwFlags = SCH_CRED_NO_SYSTEM_MAPPER;
status = table->AcquireCredentialsHandle(nullptr, SCHANNEL_NAME, SECPKG_CRED_INBOUND, nullptr,
&cred, nullptr, nullptr, &credentials, nullptr);
if (status != SEC_E_OK)
{
printf("AcquireCredentialsHandle failure: 0x%08" PRIX32 "\n", status);
return 0;
}
extraData = FALSE;
g_ServerWait = TRUE;
if (!(lpTokenIn = (BYTE*)malloc(cbMaxToken)))
{
printf("Memory allocation failed\n");
return 0;
}
if (!(lpTokenOut = (BYTE*)malloc(cbMaxToken)))
{
printf("Memory allocation failed\n");
free(lpTokenIn);
return 0;
}
fContextReq = ASC_REQ_STREAM | ASC_REQ_SEQUENCE_DETECT | ASC_REQ_REPLAY_DETECT |
ASC_REQ_CONFIDENTIALITY | ASC_REQ_EXTENDED_ERROR;
do
{
if (!extraData)
{
if (g_ServerWait)
{
if (!ReadFile(g_ServerReadPipe, lpTokenIn, cbMaxToken, &NumberOfBytesRead, nullptr))
{
printf("Failed to read from server pipe\n");
return nullptr;
}
}
else
{
NumberOfBytesRead = 0;
}
}
extraData = FALSE;
g_ServerWait = TRUE;
SecBuffer_in[0].BufferType = SECBUFFER_TOKEN;
SecBuffer_in[0].pvBuffer = lpTokenIn;
SecBuffer_in[0].cbBuffer = NumberOfBytesRead;
SecBuffer_in[1].BufferType = SECBUFFER_EMPTY;
SecBuffer_in[1].pvBuffer = nullptr;
SecBuffer_in[1].cbBuffer = 0;
SecBufferDesc_in.ulVersion = SECBUFFER_VERSION;
SecBufferDesc_in.cBuffers = 2;
SecBufferDesc_in.pBuffers = SecBuffer_in;
SecBuffer_out[0].BufferType = SECBUFFER_TOKEN;
SecBuffer_out[0].pvBuffer = lpTokenOut;
SecBuffer_out[0].cbBuffer = cbMaxToken;
SecBufferDesc_out.ulVersion = SECBUFFER_VERSION;
SecBufferDesc_out.cBuffers = 1;
SecBufferDesc_out.pBuffers = SecBuffer_out;
status = table->AcceptSecurityContext(
&credentials, SecIsValidHandle(&context) ? &context : nullptr, &SecBufferDesc_in,
fContextReq, 0, &context, &SecBufferDesc_out, &fContextAttr, &expiry);
if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED) &&
(status != SEC_E_INCOMPLETE_MESSAGE))
{
printf("AcceptSecurityContext unexpected status: 0x%08" PRIX32 "\n", status);
return nullptr;
}
NumberOfBytesWritten = 0;
if (status == SEC_E_OK)
printf("AcceptSecurityContext status: SEC_E_OK\n");
else if (status == SEC_I_CONTINUE_NEEDED)
printf("AcceptSecurityContext status: SEC_I_CONTINUE_NEEDED\n");
else if (status == SEC_E_INCOMPLETE_MESSAGE)
printf("AcceptSecurityContext status: SEC_E_INCOMPLETE_MESSAGE\n");
printf("Server cBuffers: %" PRIu32 " pBuffers[0]: %" PRIu32 " type: %" PRIu32 "\n",
SecBufferDesc_out.cBuffers, SecBufferDesc_out.pBuffers[0].cbBuffer,
SecBufferDesc_out.pBuffers[0].BufferType);
printf("Server Input cBuffers: %" PRIu32 " pBuffers[0]: %" PRIu32 " type: %" PRIu32
" pBuffers[1]: %" PRIu32 " type: %" PRIu32 "\n",
SecBufferDesc_in.cBuffers, SecBufferDesc_in.pBuffers[0].cbBuffer,
SecBufferDesc_in.pBuffers[0].BufferType, SecBufferDesc_in.pBuffers[1].cbBuffer,
SecBufferDesc_in.pBuffers[1].BufferType);
if (SecBufferDesc_in.pBuffers[1].BufferType == SECBUFFER_EXTRA)
{
printf("AcceptSecurityContext SECBUFFER_EXTRA\n");
pSecBuffer = &SecBufferDesc_in.pBuffers[1];
CopyMemory(lpTokenIn, &lpTokenIn[NumberOfBytesRead - pSecBuffer->cbBuffer],
pSecBuffer->cbBuffer);
NumberOfBytesRead = pSecBuffer->cbBuffer;
continue;
}
if (status != SEC_E_INCOMPLETE_MESSAGE)
{
pSecBuffer = &SecBufferDesc_out.pBuffers[0];
if (pSecBuffer->cbBuffer > 0)
{
printf("Server > Client (%" PRIu32 ")\n", pSecBuffer->cbBuffer);
winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer,
pSecBuffer->cbBuffer);
if (!WriteFile(g_ClientWritePipe, pSecBuffer->pvBuffer, pSecBuffer->cbBuffer,
&NumberOfBytesWritten, nullptr))
{
printf("failed to write to client pipe\n");
return nullptr;
}
}
}
if (status == SEC_E_OK)
{
printf("Server Handshake Complete\n");
break;
}
} while (1);
do
{
if (schannel_recv(table, g_ServerReadPipe, &context) < 0)
break;
} while (1);
return 0;
}
static int dump_test_certificate_files(void)
{
FILE* fp;
char* fullpath = nullptr;
int ret = -1;
/*
* Output Certificate File
*/
fullpath = GetCombinedPath("/tmp", "localhost.crt");
if (!fullpath)
return -1;
fp = winpr_fopen(fullpath, "w+");
if (fp)
{
if (fwrite((void*)test_localhost_crt, sizeof(test_localhost_crt), 1, fp) != 1)
goto out_fail;
fclose(fp);
fp = nullptr;
}
free(fullpath);
/*
* Output Private Key File
*/
fullpath = GetCombinedPath("/tmp", "localhost.key");
if (!fullpath)
return -1;
fp = winpr_fopen(fullpath, "w+");
if (fp && fwrite((void*)test_localhost_key, sizeof(test_localhost_key), 1, fp) != 1)
goto out_fail;
ret = 1;
out_fail:
free(fullpath);
if (fp)
fclose(fp);
return ret;
}
int TestSchannel(int argc, char* argv[])
{
int count;
ALG_ID algId;
HANDLE thread;
BYTE* lpTokenIn;
BYTE* lpTokenOut;
TimeStamp expiry;
UINT32 cbMaxToken;
SCHANNEL_CRED cred = WINPR_C_ARRAY_INIT;
UINT32 fContextReq;
ULONG fContextAttr;
CtxtHandle context;
CredHandle credentials;
SECURITY_STATUS status;
PSecPkgInfo pPackageInfo;
PSecBuffer pSecBuffer;
PSecurityFunctionTable table;
DWORD NumberOfBytesRead;
DWORD NumberOfBytesWritten;
SecPkgCred_SupportedAlgs SupportedAlgs = WINPR_C_ARRAY_INIT;
SecPkgCred_CipherStrengths CipherStrengths = WINPR_C_ARRAY_INIT;
SecPkgCred_SupportedProtocols SupportedProtocols = WINPR_C_ARRAY_INIT;
return 0; /* disable by default - causes crash */
sspi_GlobalInit();
dump_test_certificate_files();
SecInvalidateHandle(&context);
SecInvalidateHandle(&credentials);
if (!CreatePipe(&g_ClientReadPipe, &g_ClientWritePipe, nullptr, 0))
{
printf("Failed to create client pipe\n");
return -1;
}
if (!CreatePipe(&g_ServerReadPipe, &g_ServerWritePipe, nullptr, 0))
{
printf("Failed to create server pipe\n");
return -1;
}
if (!(thread = CreateThread(nullptr, 0, schannel_test_server_thread, nullptr, 0, nullptr)))
{
printf("Failed to create server thread\n");
return -1;
}
table = InitSecurityInterface();
status = QuerySecurityPackageInfo(SCHANNEL_NAME, &pPackageInfo);
if (status != SEC_E_OK)
{
printf("QuerySecurityPackageInfo failure: 0x%08" PRIX32 "\n", status);
return -1;
}
cbMaxToken = pPackageInfo->cbMaxToken;
cred.dwVersion = SCHANNEL_CRED_VERSION;
cred.cCreds = 0;
cred.paCred = nullptr;
cred.cSupportedAlgs = 0;
cred.palgSupportedAlgs = nullptr;
cred.grbitEnabledProtocols = SP_PROT_SSL3TLS1_CLIENTS;
cred.dwFlags = SCH_CRED_NO_DEFAULT_CREDS;
cred.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
cred.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
status = table->AcquireCredentialsHandle(nullptr, SCHANNEL_NAME, SECPKG_CRED_OUTBOUND, nullptr,
&cred, nullptr, nullptr, &credentials, nullptr);
if (status != SEC_E_OK)
{
printf("AcquireCredentialsHandle failure: 0x%08" PRIX32 "\n", status);
return -1;
}
status =
table->QueryCredentialsAttributes(&credentials, SECPKG_ATTR_SUPPORTED_ALGS, &SupportedAlgs);
if (status != SEC_E_OK)
{
printf("QueryCredentialsAttributes SECPKG_ATTR_SUPPORTED_ALGS failure: 0x%08" PRIX32 "\n",
status);
return -1;
}
/**
* SupportedAlgs: 15
* 0x660E 0x6610 0x6801 0x6603 0x6601 0x8003 0x8004
* 0x800C 0x800D 0x800E 0x2400 0xAA02 0xAE06 0x2200 0x2203
*/
printf("SupportedAlgs: %" PRIu32 "\n", SupportedAlgs.cSupportedAlgs);
for (DWORD index = 0; index < SupportedAlgs.cSupportedAlgs; index++)
{
algId = SupportedAlgs.palgSupportedAlgs[index];
printf("\t0x%08" PRIX32 " CLASS: %" PRIu32 " TYPE: %" PRIu32 " SID: %" PRIu32 "\n", algId,
((GET_ALG_CLASS(algId)) >> 13), ((GET_ALG_TYPE(algId)) >> 9), GET_ALG_SID(algId));
}
printf("\n");
status = table->QueryCredentialsAttributes(&credentials, SECPKG_ATTR_CIPHER_STRENGTHS,
&CipherStrengths);
if (status != SEC_E_OK)
{
printf("QueryCredentialsAttributes SECPKG_ATTR_CIPHER_STRENGTHS failure: 0x%08" PRIX32 "\n",
status);
return -1;
}
/* CipherStrengths: Minimum: 40 Maximum: 256 */
printf("CipherStrengths: Minimum: %" PRIu32 " Maximum: %" PRIu32 "\n",
CipherStrengths.dwMinimumCipherStrength, CipherStrengths.dwMaximumCipherStrength);
status = table->QueryCredentialsAttributes(&credentials, SECPKG_ATTR_SUPPORTED_PROTOCOLS,
&SupportedProtocols);
if (status != SEC_E_OK)
{
printf("QueryCredentialsAttributes SECPKG_ATTR_SUPPORTED_PROTOCOLS failure: 0x%08" PRIX32
"\n",
status);
return -1;
}
/* SupportedProtocols: 0x208A0 */
printf("SupportedProtocols: 0x%08" PRIX32 "\n", SupportedProtocols.grbitProtocol);
fContextReq = ISC_REQ_STREAM | ISC_REQ_SEQUENCE_DETECT | ISC_REQ_REPLAY_DETECT |
ISC_REQ_CONFIDENTIALITY | ISC_RET_EXTENDED_ERROR |
ISC_REQ_MANUAL_CRED_VALIDATION | ISC_REQ_INTEGRITY;
if (!(lpTokenIn = (BYTE*)malloc(cbMaxToken)))
{
printf("Memory allocation failed\n");
return -1;
}
if (!(lpTokenOut = (BYTE*)malloc(cbMaxToken)))
{
printf("Memory allocation failed\n");
return -1;
}
g_ClientWait = FALSE;
do
{
SecBuffer SecBuffer_in[2] = WINPR_C_ARRAY_INIT;
SecBuffer SecBuffer_out[1] = WINPR_C_ARRAY_INIT;
SecBufferDesc SecBufferDesc_in = WINPR_C_ARRAY_INIT;
SecBufferDesc SecBufferDesc_out = WINPR_C_ARRAY_INIT;
if (g_ClientWait)
{
if (!ReadFile(g_ClientReadPipe, lpTokenIn, cbMaxToken, &NumberOfBytesRead, nullptr))
{
printf("failed to read from server pipe\n");
return -1;
}
}
else
{
NumberOfBytesRead = 0;
}
g_ClientWait = TRUE;
printf("NumberOfBytesRead: %" PRIu32 "\n", NumberOfBytesRead);
SecBuffer_in[0].BufferType = SECBUFFER_TOKEN;
SecBuffer_in[0].pvBuffer = lpTokenIn;
SecBuffer_in[0].cbBuffer = NumberOfBytesRead;
SecBuffer_in[1].pvBuffer = nullptr;
SecBuffer_in[1].cbBuffer = 0;
SecBuffer_in[1].BufferType = SECBUFFER_EMPTY;
SecBufferDesc_in.ulVersion = SECBUFFER_VERSION;
SecBufferDesc_in.cBuffers = 2;
SecBufferDesc_in.pBuffers = SecBuffer_in;
SecBuffer_out[0].BufferType = SECBUFFER_TOKEN;
SecBuffer_out[0].pvBuffer = lpTokenOut;
SecBuffer_out[0].cbBuffer = cbMaxToken;
SecBufferDesc_out.ulVersion = SECBUFFER_VERSION;
SecBufferDesc_out.cBuffers = 1;
SecBufferDesc_out.pBuffers = SecBuffer_out;
status = table->InitializeSecurityContext(
&credentials, SecIsValidHandle(&context) ? &context : nullptr, _T("localhost"),
fContextReq, 0, 0, &SecBufferDesc_in, 0, &context, &SecBufferDesc_out, &fContextAttr,
&expiry);
if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED) &&
(status != SEC_E_INCOMPLETE_MESSAGE))
{
printf("InitializeSecurityContext unexpected status: 0x%08" PRIX32 "\n", status);
return -1;
}
NumberOfBytesWritten = 0;
if (status == SEC_E_OK)
printf("InitializeSecurityContext status: SEC_E_OK\n");
else if (status == SEC_I_CONTINUE_NEEDED)
printf("InitializeSecurityContext status: SEC_I_CONTINUE_NEEDED\n");
else if (status == SEC_E_INCOMPLETE_MESSAGE)
printf("InitializeSecurityContext status: SEC_E_INCOMPLETE_MESSAGE\n");
printf("Client Output cBuffers: %" PRIu32 " pBuffers[0]: %" PRIu32 " type: %" PRIu32 "\n",
SecBufferDesc_out.cBuffers, SecBufferDesc_out.pBuffers[0].cbBuffer,
SecBufferDesc_out.pBuffers[0].BufferType);
printf("Client Input cBuffers: %" PRIu32 " pBuffers[0]: %" PRIu32 " type: %" PRIu32
" pBuffers[1]: %" PRIu32 " type: %" PRIu32 "\n",
SecBufferDesc_in.cBuffers, SecBufferDesc_in.pBuffers[0].cbBuffer,
SecBufferDesc_in.pBuffers[0].BufferType, SecBufferDesc_in.pBuffers[1].cbBuffer,
SecBufferDesc_in.pBuffers[1].BufferType);
if (status != SEC_E_INCOMPLETE_MESSAGE)
{
pSecBuffer = &SecBufferDesc_out.pBuffers[0];
if (pSecBuffer->cbBuffer > 0)
{
printf("Client > Server (%" PRIu32 ")\n", pSecBuffer->cbBuffer);
winpr_HexDump("sspi.test", WLOG_DEBUG, (BYTE*)pSecBuffer->pvBuffer,
pSecBuffer->cbBuffer);
if (!WriteFile(g_ServerWritePipe, pSecBuffer->pvBuffer, pSecBuffer->cbBuffer,
&NumberOfBytesWritten, nullptr))
{
printf("failed to write to server pipe\n");
return -1;
}
}
}
if (status == SEC_E_OK)
{
printf("Client Handshake Complete\n");
break;
}
} while (1);
count = 0;
do
{
if (schannel_send(table, g_ServerWritePipe, &context, test_DummyMessage,
sizeof(test_DummyMessage)) < 0)
break;
for (DWORD index = 0; index < sizeof(test_DummyMessage); index++)
{
BYTE b, ln, hn;
b = test_DummyMessage[index];
ln = (b & 0x0F);
hn = ((b & 0xF0) >> 4);
ln = (ln + 1) % 0xF;
hn = (ln + 1) % 0xF;
b = (ln | (hn << 4));
test_DummyMessage[index] = b;
}
Sleep(100);
count++;
} while (count < 3);
schannel_send(table, g_ServerWritePipe, &context, test_LastDummyMessage,
sizeof(test_LastDummyMessage));
(void)WaitForSingleObject(thread, INFINITE);
sspi_GlobalFinish();
return 0;
}