summaryrefslogtreecommitdiffstats
path: root/winpr/libwinpr/sspi/test
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--winpr/libwinpr/sspi/test/CMakeLists.txt38
-rw-r--r--winpr/libwinpr/sspi/test/TestAcquireCredentialsHandle.c60
-rw-r--r--winpr/libwinpr/sspi/test/TestCredSSP.c8
-rw-r--r--winpr/libwinpr/sspi/test/TestEnumerateSecurityPackages.c40
-rw-r--r--winpr/libwinpr/sspi/test/TestInitializeSecurityContext.c115
-rw-r--r--winpr/libwinpr/sspi/test/TestNTLM.c694
-rw-r--r--winpr/libwinpr/sspi/test/TestQuerySecurityPackageInfo.c34
-rw-r--r--winpr/libwinpr/sspi/test/TestSchannel.c854
8 files changed, 1843 insertions, 0 deletions
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 <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 = { 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 <winpr/crt.h>
+#include <winpr/sspi.h>
+
+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 <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 = 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 <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 = 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 <winpr/crt.h>
+#include <winpr/assert.h>
+#include <winpr/sspi.h>
+#include <winpr/print.h>
+#include <winpr/wlog.h>
+
+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 <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 = 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 <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 = 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;
+}