From a9bcc81f821d7c66f623779fa5147e728eb3c388 Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Sat, 4 May 2024 03:24:41 +0200 Subject: Adding upstream version 3.3.0+dfsg1. Signed-off-by: Daniel Baumann --- winpr/libwinpr/sspi/test/CMakeLists.txt | 38 + .../sspi/test/TestAcquireCredentialsHandle.c | 60 ++ winpr/libwinpr/sspi/test/TestCredSSP.c | 8 + .../sspi/test/TestEnumerateSecurityPackages.c | 40 + .../sspi/test/TestInitializeSecurityContext.c | 115 +++ winpr/libwinpr/sspi/test/TestNTLM.c | 694 +++++++++++++++++ .../sspi/test/TestQuerySecurityPackageInfo.c | 34 + winpr/libwinpr/sspi/test/TestSchannel.c | 854 +++++++++++++++++++++ 8 files changed, 1843 insertions(+) create mode 100644 winpr/libwinpr/sspi/test/CMakeLists.txt create mode 100644 winpr/libwinpr/sspi/test/TestAcquireCredentialsHandle.c create mode 100644 winpr/libwinpr/sspi/test/TestCredSSP.c create mode 100644 winpr/libwinpr/sspi/test/TestEnumerateSecurityPackages.c create mode 100644 winpr/libwinpr/sspi/test/TestInitializeSecurityContext.c create mode 100644 winpr/libwinpr/sspi/test/TestNTLM.c create mode 100644 winpr/libwinpr/sspi/test/TestQuerySecurityPackageInfo.c create mode 100644 winpr/libwinpr/sspi/test/TestSchannel.c (limited to 'winpr/libwinpr/sspi/test') diff --git a/winpr/libwinpr/sspi/test/CMakeLists.txt b/winpr/libwinpr/sspi/test/CMakeLists.txt new file mode 100644 index 0000000..5e0f15d --- /dev/null +++ b/winpr/libwinpr/sspi/test/CMakeLists.txt @@ -0,0 +1,38 @@ + +set(MODULE_NAME "TestSspi") +set(MODULE_PREFIX "TEST_SSPI") + +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(${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") + diff --git a/winpr/libwinpr/sspi/test/TestAcquireCredentialsHandle.c b/winpr/libwinpr/sspi/test/TestAcquireCredentialsHandle.c new file mode 100644 index 0000000..05466c8 --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestAcquireCredentialsHandle.c @@ -0,0 +1,60 @@ + +#include +#include +#include +#include + +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 = { 0 }; + TimeStamp expiration; + SEC_WINNT_AUTH_IDENTITY identity; + SecurityFunctionTable* table = NULL; + 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(NULL, NTLM_SSP_NAME, SECPKG_CRED_OUTBOUND, NULL, + &identity, NULL, NULL, &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; +} diff --git a/winpr/libwinpr/sspi/test/TestCredSSP.c b/winpr/libwinpr/sspi/test/TestCredSSP.c new file mode 100644 index 0000000..b56d9e2 --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestCredSSP.c @@ -0,0 +1,8 @@ + +#include +#include + +int TestCredSSP(int argc, char* argv[]) +{ + return 0; +} diff --git a/winpr/libwinpr/sspi/test/TestEnumerateSecurityPackages.c b/winpr/libwinpr/sspi/test/TestEnumerateSecurityPackages.c new file mode 100644 index 0000000..9de23c0 --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestEnumerateSecurityPackages.c @@ -0,0 +1,40 @@ + +#include +#include +#include +#include +#include + +int TestEnumerateSecurityPackages(int argc, char* argv[]) +{ + ULONG cPackages = 0; + SECURITY_STATUS status = 0; + SecPkgInfo* pPackageInfo = NULL; + SecurityFunctionTable* table = NULL; + + 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; +} diff --git a/winpr/libwinpr/sspi/test/TestInitializeSecurityContext.c b/winpr/libwinpr/sspi/test/TestInitializeSecurityContext.c new file mode 100644 index 0000000..88f5a7c --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestInitializeSecurityContext.c @@ -0,0 +1,115 @@ + +#include +#include +#include +#include + +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 = NULL; + CtxtHandle context; + ULONG pfContextAttr = 0; + SECURITY_STATUS status = 0; + CredHandle credentials = { 0 }; + TimeStamp expiration; + PSecPkgInfo pPackageInfo = NULL; + SEC_WINNT_AUTH_IDENTITY identity = { 0 }; + SecurityFunctionTable* table = NULL; + PSecBuffer p_SecBuffer = NULL; + 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(NULL, NTLM_SSP_NAME, SECPKG_CRED_OUTBOUND, NULL, + &identity, NULL, NULL, &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, NULL, NULL, fContextReq, 0, 0, NULL, 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; +} diff --git a/winpr/libwinpr/sspi/test/TestNTLM.c b/winpr/libwinpr/sspi/test/TestNTLM.c new file mode 100644 index 0000000..2ab3373 --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestNTLM.c @@ -0,0 +1,694 @@ + +#include +#include +#include +#include +#include + +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 }; + +#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); + sspi_SetAuthIdentity(&(ntlm->identity), user, domain, password); + status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo); + + if (status != SEC_E_OK) + { + fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n", + GetSecurityStatusString(status), status); + return -1; + } + + ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken; + status = ntlm->table->AcquireCredentialsHandle(NULL, NTLM_PACKAGE_NAME, SECPKG_CRED_OUTBOUND, + NULL, &ntlm->identity, NULL, NULL, + &ntlm->credentials, &ntlm->expiration); + + if (status != SEC_E_OK) + { + 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; +#if 0 + /* HTTP authentication flags */ + ntlm->fContextReq |= ISC_REQ_CONFIDENTIALITY; +#endif + /* 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 = NULL; + } + + free(ntlm->identity.User); + free(ntlm->identity.Domain); + free(ntlm->identity.Password); + free(ntlm->ServicePrincipalName); + + if (ntlm->table) + { + ntlm->table->FreeCredentialsHandle(&ntlm->credentials); + ntlm->table->FreeContextBuffer(ntlm->pPackageInfo); + ntlm->table->DeleteSecurityContext(&ntlm->context); + } +} + +/** + * 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 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 = NULL; + } + + 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)) + { + fprintf(stderr, "ntlm_authenticate: invalid ntlm context\n"); + return -1; + } + + status = ntlm->table->InitializeSecurityContext( + &ntlm->credentials, (ntlm->haveContext) ? &ntlm->context : NULL, + (ntlm->ServicePrincipalName) ? ntlm->ServicePrincipalName : NULL, ntlm->fContextReq, 0, + SECURITY_NATIVE_DREP, (ntlm->haveInputBuffer) ? &ntlm->inputBufferDesc : NULL, 0, + &ntlm->context, &ntlm->outputBufferDesc, &ntlm->pfContextAttr, &ntlm->expiration); + + if ((status == SEC_I_COMPLETE_AND_CONTINUE) || (status == SEC_I_COMPLETE_NEEDED)) + { + if (ntlm->table->CompleteAuthToken) + ntlm->table->CompleteAuthToken(&ntlm->context, &ntlm->outputBufferDesc); + + 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 NULL; + + return ntlm; +} + +static void test_ntlm_client_free(TEST_NTLM_CLIENT* ntlm) +{ + if (!ntlm) + return; + + test_ntlm_client_uninit(ntlm); + free(ntlm); +} + +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 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); + status = ntlm->table->QuerySecurityPackageInfo(NTLM_PACKAGE_NAME, &ntlm->pPackageInfo); + + if (status != SEC_E_OK) + { + fprintf(stderr, "QuerySecurityPackageInfo status: %s (0x%08" PRIX32 ")\n", + GetSecurityStatusString(status), status); + return -1; + } + + ntlm->cbMaxToken = ntlm->pPackageInfo->cbMaxToken; + status = ntlm->table->AcquireCredentialsHandle(NULL, NTLM_PACKAGE_NAME, SECPKG_CRED_INBOUND, + NULL, NULL, NULL, NULL, &ntlm->credentials, + &ntlm->expiration); + + if (status != SEC_E_OK) + { + 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 = NULL; + } + + free(ntlm->identity.User); + free(ntlm->identity.Domain); + free(ntlm->identity.Password); + free(ntlm->ServicePrincipalName); + + if (ntlm->table) + { + ntlm->table->FreeCredentialsHandle(&ntlm->credentials); + ntlm->table->FreeContextBuffer(ntlm->pPackageInfo); + ntlm->table->DeleteSecurityContext(&ntlm->context); + } +} + +static int test_ntlm_server_authenticate(TEST_NTLM_SERVER* ntlm) +{ + SECURITY_STATUS status = SEC_E_INTERNAL_ERROR; + + WINPR_ASSERT(ntlm); + + 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); + BOOL hash_set = FALSE; + + if (!ntlm->outputBuffer[0].pvBuffer) + return -1; + + status = ntlm->table->AcceptSecurityContext( + &ntlm->credentials, ntlm->haveContext ? &ntlm->context : NULL, &ntlm->inputBufferDesc, + ntlm->fContextReq, SECURITY_NATIVE_DREP, &ntlm->context, &ntlm->outputBufferDesc, + &ntlm->pfContextAttr, &ntlm->expiration); + + if (!hash_set && status == SEC_I_CONTINUE_NEEDED) + { + SecPkgContext_AuthNtlmHash AuthNtlmHash = { 0 }; + + if (ntlm->UseNtlmV2Hash) + { + AuthNtlmHash.Version = 2; + CopyMemory(AuthNtlmHash.NtlmHash, TEST_NTLM_V2_HASH, 16); + } + else + { + AuthNtlmHash.Version = 1; + CopyMemory(AuthNtlmHash.NtlmHash, TEST_NTLM_HASH, 16); + } + + status = + ntlm->table->SetContextAttributes(&ntlm->context, SECPKG_ATTR_AUTH_NTLM_HASH, + &AuthNtlmHash, sizeof(SecPkgContext_AuthNtlmHash)); + + hash_set = TRUE; + } + + if ((status != SEC_E_OK) && (status != SEC_I_CONTINUE_NEEDED)) + { + 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 NULL; + + 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(void) +{ + int status = 0; + BOOL rc = FALSE; + PSecBuffer pSecBuffer = NULL; + TEST_NTLM_CLIENT* client = NULL; + TEST_NTLM_SERVER* server = NULL; + BOOL DynamicTest = TRUE; + + /** + * Client Initialization + */ + client = test_ntlm_client_new(); + + if (!client) + { + printf("Memory allocation failed"); + goto fail; + } + + status = test_ntlm_client_init(client, TEST_NTLM_USER, TEST_NTLM_DOMAIN, TEST_NTLM_PASSWORD); + + if (status < 0) + { + printf("test_ntlm_client_init failure\n"); + goto fail; + } + + /** + * Server Initialization + */ + server = test_ntlm_server_new(); + + if (!server) + { + printf("Memory allocation failed\n"); + goto fail; + } + + 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 (!DynamicTest) + { + SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp; + SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge; + SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge; + CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8); + AuthNtlmTimestamp.ChallengeOrResponse = TRUE; + client->table->SetContextAttributes(&client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, + &AuthNtlmTimestamp, + sizeof(SecPkgContext_AuthNtlmTimestamp)); + AuthNtlmTimestamp.ChallengeOrResponse = FALSE; + client->table->SetContextAttributes(&client->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, + &AuthNtlmTimestamp, + sizeof(SecPkgContext_AuthNtlmTimestamp)); + CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8); + CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8); + client->table->SetContextAttributes( + &client->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge, + sizeof(SecPkgContext_AuthNtlmClientChallenge)); + client->table->SetContextAttributes( + &client->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge, + sizeof(SecPkgContext_AuthNtlmServerChallenge)); + } + + pSecBuffer = &(client->outputBuffer[0]); + + if (!DynamicTest) + { + pSecBuffer->cbBuffer = sizeof(TEST_NTLM_NEGOTIATE) - 1; + pSecBuffer->pvBuffer = (void*)malloc(pSecBuffer->cbBuffer); + + if (!pSecBuffer->pvBuffer) + { + printf("Memory allocation failed\n"); + goto fail; + } + + CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_NEGOTIATE, pSecBuffer->cbBuffer); + } + + 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(server); + + if (status < 0) + { + printf("test_ntlm_server_authenticate failure\n"); + goto fail; + } + + if (!DynamicTest) + { + SecPkgContext_AuthNtlmTimestamp AuthNtlmTimestamp; + SecPkgContext_AuthNtlmClientChallenge AuthNtlmClientChallenge; + SecPkgContext_AuthNtlmServerChallenge AuthNtlmServerChallenge; + CopyMemory(AuthNtlmTimestamp.Timestamp, TEST_NTLM_TIMESTAMP, 8); + AuthNtlmTimestamp.ChallengeOrResponse = TRUE; + client->table->SetContextAttributes(&server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, + &AuthNtlmTimestamp, + sizeof(SecPkgContext_AuthNtlmTimestamp)); + AuthNtlmTimestamp.ChallengeOrResponse = FALSE; + client->table->SetContextAttributes(&server->context, SECPKG_ATTR_AUTH_NTLM_TIMESTAMP, + &AuthNtlmTimestamp, + sizeof(SecPkgContext_AuthNtlmTimestamp)); + CopyMemory(AuthNtlmClientChallenge.ClientChallenge, TEST_NTLM_CLIENT_CHALLENGE, 8); + CopyMemory(AuthNtlmServerChallenge.ServerChallenge, TEST_NTLM_SERVER_CHALLENGE, 8); + server->table->SetContextAttributes( + &server->context, SECPKG_ATTR_AUTH_NTLM_CLIENT_CHALLENGE, &AuthNtlmClientChallenge, + sizeof(SecPkgContext_AuthNtlmClientChallenge)); + server->table->SetContextAttributes( + &server->context, SECPKG_ATTR_AUTH_NTLM_SERVER_CHALLENGE, &AuthNtlmServerChallenge, + sizeof(SecPkgContext_AuthNtlmServerChallenge)); + } + + pSecBuffer = &(server->outputBuffer[0]); + + if (!DynamicTest) + { + SecPkgContext_AuthNtlmMessage AuthNtlmMessage = { 0 }; + pSecBuffer->cbBuffer = sizeof(TEST_NTLM_CHALLENGE) - 1; + pSecBuffer->pvBuffer = (void*)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; + server->table->SetContextAttributes(&server->context, SECPKG_ATTR_AUTH_NTLM_MESSAGE, + &AuthNtlmMessage, + sizeof(SecPkgContext_AuthNtlmMessage)); + } + + 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 (!DynamicTest) + { + pSecBuffer->cbBuffer = sizeof(TEST_NTLM_AUTHENTICATE) - 1; + pSecBuffer->pvBuffer = (void*)malloc(pSecBuffer->cbBuffer); + + if (!pSecBuffer->pvBuffer) + { + printf("Memory allocation failed\n"); + goto fail; + } + + CopyMemory(pSecBuffer->pvBuffer, TEST_NTLM_AUTHENTICATE, pSecBuffer->cbBuffer); + } + + 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(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); + return rc; +} + +int TestNTLM(int argc, char* argv[]) +{ + WINPR_UNUSED(argc); + WINPR_UNUSED(argv); + + if (!test_default()) + return -1; + return 0; +} diff --git a/winpr/libwinpr/sspi/test/TestQuerySecurityPackageInfo.c b/winpr/libwinpr/sspi/test/TestQuerySecurityPackageInfo.c new file mode 100644 index 0000000..5d1ca00 --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestQuerySecurityPackageInfo.c @@ -0,0 +1,34 @@ + +#include +#include +#include +#include + +int TestQuerySecurityPackageInfo(int argc, char* argv[]) +{ + int rc = 0; + SECURITY_STATUS status = 0; + SecPkgInfo* pPackageInfo = NULL; + SecurityFunctionTable* table = NULL; + + 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; +} diff --git a/winpr/libwinpr/sspi/test/TestSchannel.c b/winpr/libwinpr/sspi/test/TestSchannel.c new file mode 100644 index 0000000..3f9751a --- /dev/null +++ b/winpr/libwinpr/sspi/test/TestSchannel.c @@ -0,0 +1,854 @@ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static BOOL g_ClientWait = FALSE; +static BOOL g_ServerWait = FALSE; + +static HANDLE g_ClientReadPipe = NULL; +static HANDLE g_ClientWritePipe = NULL; +static HANDLE g_ServerReadPipe = NULL; +static HANDLE g_ServerWritePipe = NULL; + +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] = { 0 }; + SecBufferDesc Message; + SECURITY_STATUS status; + DWORD NumberOfBytesWritten; + SecPkgContext_StreamSizes StreamSizes = { 0 }; + + 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 = NULL; + 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, NULL)) + { + 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] = { 0 }; + SecBufferDesc Message; + SECURITY_STATUS status; + DWORD NumberOfBytesRead; + SecPkgContext_StreamSizes StreamSizes = { 0 }; + + 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, NULL)) + { + 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 = NULL; + Buffers[1].cbBuffer = 0; + Buffers[1].BufferType = SECBUFFER_EMPTY; + Buffers[2].pvBuffer = NULL; + Buffers[2].cbBuffer = 0; + Buffers[2].BufferType = SECBUFFER_EMPTY; + Buffers[3].pvBuffer = NULL; + 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, NULL); + 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 = { 0 }; + CtxtHandle context; + CredHandle credentials; + DWORD cchNameString; + LPTSTR pszNameString; + HCERTSTORE hCertStore; + PCCERT_CONTEXT pCertContext; + PSecBuffer pSecBuffer; + SecBuffer SecBuffer_in[2] = { 0 }; + SecBuffer SecBuffer_out[2] = { 0 }; + 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 NULL; + } + +#ifdef CERT_FIND_HAS_PRIVATE_KEY + pCertContext = CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, + CERT_FIND_HAS_PRIVATE_KEY, NULL, NULL); +#else + pCertContext = + CertFindCertificateInStore(hCertStore, X509_ASN_ENCODING, 0, CERT_FIND_ANY, NULL, NULL); +#endif + + if (!pCertContext) + { + printf("Error finding certificate in store\n"); + // return NULL; + } + + cchNameString = + CertGetNameString(pCertContext, CERT_NAME_SIMPLE_DISPLAY_TYPE, 0, NULL, NULL, 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, NULL, + 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 = NULL; + cred.grbitEnabledProtocols = SP_PROT_TLS1_SERVER; + cred.dwFlags = SCH_CRED_NO_SYSTEM_MAPPER; + status = table->AcquireCredentialsHandle(NULL, SCHANNEL_NAME, SECPKG_CRED_INBOUND, NULL, &cred, + NULL, NULL, &credentials, NULL); + + 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, NULL)) + { + printf("Failed to read from server pipe\n"); + return NULL; + } + } + 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 = NULL; + 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 : NULL, &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 NULL; + } + + 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, NULL)) + { + printf("failed to write to client pipe\n"); + return NULL; + } + } + } + + 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 = NULL; + 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 = NULL; + } + 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 = { 0 }; + UINT32 fContextReq; + ULONG fContextAttr; + CtxtHandle context; + CredHandle credentials; + SECURITY_STATUS status; + PSecPkgInfo pPackageInfo; + PSecBuffer pSecBuffer; + PSecurityFunctionTable table; + DWORD NumberOfBytesRead; + DWORD NumberOfBytesWritten; + SecPkgCred_SupportedAlgs SupportedAlgs = { 0 }; + SecPkgCred_CipherStrengths CipherStrengths = { 0 }; + SecPkgCred_SupportedProtocols SupportedProtocols = { 0 }; + return 0; /* disable by default - causes crash */ + sspi_GlobalInit(); + dump_test_certificate_files(); + SecInvalidateHandle(&context); + SecInvalidateHandle(&credentials); + + if (!CreatePipe(&g_ClientReadPipe, &g_ClientWritePipe, NULL, 0)) + { + printf("Failed to create client pipe\n"); + return -1; + } + + if (!CreatePipe(&g_ServerReadPipe, &g_ServerWritePipe, NULL, 0)) + { + printf("Failed to create server pipe\n"); + return -1; + } + + if (!(thread = CreateThread(NULL, 0, schannel_test_server_thread, NULL, 0, NULL))) + { + 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 = NULL; + cred.cSupportedAlgs = 0; + cred.palgSupportedAlgs = NULL; + 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(NULL, SCHANNEL_NAME, SECPKG_CRED_OUTBOUND, NULL, &cred, + NULL, NULL, &credentials, NULL); + + 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] = { 0 }; + SecBuffer SecBuffer_out[1] = { 0 }; + SecBufferDesc SecBufferDesc_in = { 0 }; + SecBufferDesc SecBufferDesc_out = { 0 }; + if (g_ClientWait) + { + if (!ReadFile(g_ClientReadPipe, lpTokenIn, cbMaxToken, &NumberOfBytesRead, NULL)) + { + 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 = NULL; + 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 : NULL, _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, NULL)) + { + 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)); + WaitForSingleObject(thread, INFINITE); + sspi_GlobalFinish(); + return 0; +} -- cgit v1.2.3