adapters/tlsio_schannel.c (1,372 lines of code) (raw):
// Copyright (c) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
#define SECURITY_WIN32
#define SEC_TCHAR SEC_CHAR
#include <stdlib.h>
#include <stdio.h>
#include <stdbool.h>
#include "azure_macro_utils/macro_utils.h"
#include "azure_c_shared_utility/tlsio.h"
#include "azure_c_shared_utility/tlsio_schannel.h"
#include "azure_c_shared_utility/socketio.h"
#include "windows.h"
#include "sspi.h"
#include "schannel.h"
#include "azure_c_shared_utility/optimize_size.h"
#include "azure_c_shared_utility/xlogging.h"
#include "azure_c_shared_utility/x509_schannel.h"
#include "azure_c_shared_utility/crt_abstractions.h"
#include "azure_c_shared_utility/singlylinkedlist.h"
#include "azure_c_shared_utility/shared_util_options.h"
#include "azure_c_shared_utility/gballoc.h"
#include "azure_c_shared_utility/safe_math.h"
#define TLSIO_STATE_VALUES \
TLSIO_STATE_NOT_OPEN, \
TLSIO_STATE_OPENING_UNDERLYING_IO, \
TLSIO_STATE_HANDSHAKE_CLIENT_HELLO_SENT, \
TLSIO_STATE_HANDSHAKE_SERVER_HELLO_RECEIVED, \
TLSIO_STATE_RENEGOTIATE, \
TLSIO_STATE_OPEN, \
TLSIO_STATE_CLOSING, \
TLSIO_STATE_ERROR
MU_DEFINE_ENUM(TLSIO_STATE, TLSIO_STATE_VALUES);
MU_DEFINE_ENUM_STRINGS(TLSIO_STATE, TLSIO_STATE_VALUES);
typedef struct PENDING_SEND_TAG
{
unsigned char* bytes;
size_t length;
ON_SEND_COMPLETE on_send_complete;
void* on_send_complete_context;
} PENDING_SEND;
typedef struct TLS_IO_INSTANCE_TAG
{
XIO_HANDLE socket_io;
ON_IO_OPEN_COMPLETE on_io_open_complete;
ON_IO_CLOSE_COMPLETE on_io_close_complete;
ON_BYTES_RECEIVED on_bytes_received;
ON_IO_ERROR on_io_error;
void* on_io_open_complete_context;
void* on_io_close_complete_context;
void* on_bytes_received_context;
void* on_io_error_context;
CtxtHandle security_context;
TLSIO_STATE tlsio_state;
SEC_TCHAR* host_name;
CredHandle credential_handle;
bool credential_handle_allocated;
bool ignore_server_name_check;
unsigned char* received_bytes;
size_t received_byte_count;
size_t buffer_size;
size_t needed_bytes;
char* x509certificate;
char* x509privatekey;
X509_SCHANNEL_HANDLE x509_schannel_handle;
SINGLYLINKEDLIST_HANDLE pending_io_list;
// Certificate to check server certificate chains to, overriding built-in Windows certificate store certificates.
char* trustedCertificate;
} TLS_IO_INSTANCE;
/*this function will clone an option given by name and value*/
static void* tlsio_schannel_CloneOption(const char* name, const void* value)
{
void* result;
if (
(name == NULL) || (value == NULL)
)
{
LogError("invalid parameter detected: const char* name = %p, const void* value = %p", name, value);
result = NULL;
}
else
{
if (strcmp(name, SU_OPTION_X509_CERT) == 0 || strcmp(name, OPTION_X509_ECC_CERT) == 0)
{
if (mallocAndStrcpy_s((char**)&result, (const char *) value) != 0)
{
LogError("unable to mallocAndStrcpy_s x509certificate value");
result = NULL;
}
else
{
/*return as is*/
}
}
else if (strcmp(name, SU_OPTION_X509_PRIVATE_KEY) == 0 || strcmp(name, OPTION_X509_ECC_KEY) == 0)
{
if (mallocAndStrcpy_s((char**)&result, (const char *) value) != 0)
{
LogError("unable to mallocAndStrcpy_s x509privatekey value");
result = NULL;
}
else
{
/*return as is*/
}
}
else if (strcmp(name, OPTION_TRUSTED_CERT) == 0)
{
if (mallocAndStrcpy_s((char**)&result, (const char *) value) != 0)
{
LogError("unable to mallocAndStrcpy_s TrustedCerts value");
result = NULL;
}
else
{
/*return as is*/
}
}
else
{
LogError("not handled option : %s", name);
result = NULL;
}
}
return result;
}
/*this function destroys an option previously created*/
static void tlsio_schannel_DestroyOption(const char* name, const void* value)
{
/*since all options for this layer are actually string copies., disposing of one is just calling free*/
if (
(name == NULL) || (value == NULL)
)
{
LogError("invalid parameter detected: const char* name = %p, const void* value = %p", name, value);
}
else
{
if (
(strcmp(name, SU_OPTION_X509_CERT) == 0) ||
(strcmp(name, SU_OPTION_X509_PRIVATE_KEY) == 0) ||
(strcmp(name, OPTION_X509_ECC_CERT) == 0) ||
(strcmp(name, OPTION_X509_ECC_KEY) == 0) ||
(strcmp(name, OPTION_TRUSTED_CERT) == 0)
)
{
free((void*)value);
}
else
{
LogError("not handled option : %s", name);
}
}
}
static OPTIONHANDLER_HANDLE tlsio_schannel_retrieveoptions(CONCRETE_IO_HANDLE handle)
{
OPTIONHANDLER_HANDLE result;
if (handle == NULL)
{
LogError("invalid parameter detected: CONCRETE_IO_HANDLE handle = %p", handle);
result = NULL;
}
else
{
result = OptionHandler_Create(tlsio_schannel_CloneOption, tlsio_schannel_DestroyOption, tlsio_schannel_setoption);
if (result == NULL)
{
LogError("unable to OptionHandler_Create");
/*return as is*/
}
else
{
/*this layer cares about the certificates and the x509 credentials*/
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)handle;
if (
(tls_io_instance->x509certificate != NULL) &&
(OptionHandler_AddOption(result, "x509certificate", tls_io_instance->x509certificate) != OPTIONHANDLER_OK)
)
{
LogError("unable to save x509certificate option");
OptionHandler_Destroy(result);
result = NULL;
}
else if (
(tls_io_instance->trustedCertificate != NULL) &&
(OptionHandler_AddOption(result, OPTION_TRUSTED_CERT, tls_io_instance->trustedCertificate) != OPTIONHANDLER_OK)
)
{
LogError("unable to save TrustedCerts option");
OptionHandler_Destroy(result);
result = NULL;
}
else if (
(tls_io_instance->x509privatekey != NULL) &&
(OptionHandler_AddOption(result, "x509privatekey", tls_io_instance->x509privatekey) != OPTIONHANDLER_OK)
)
{
LogError("unable to save x509privatekey option");
OptionHandler_Destroy(result);
result = NULL;
}
else
{
/*all is fine, all interesting options have been saved*/
/*return as is*/
}
}
}
return result;
}
static const IO_INTERFACE_DESCRIPTION tlsio_schannel_interface_description =
{
tlsio_schannel_retrieveoptions,
tlsio_schannel_create,
tlsio_schannel_destroy,
tlsio_schannel_open,
tlsio_schannel_close,
tlsio_schannel_send,
tlsio_schannel_dowork,
tlsio_schannel_setoption
};
static void indicate_error(TLS_IO_INSTANCE* tls_io_instance)
{
if (tls_io_instance->on_io_error != NULL)
{
tls_io_instance->on_io_error(tls_io_instance->on_io_error_context);
}
}
static int resize_receive_buffer(TLS_IO_INSTANCE* tls_io_instance, size_t needed_buffer_size)
{
int result;
if (needed_buffer_size > tls_io_instance->buffer_size)
{
unsigned char* new_buffer = (unsigned char*) realloc(tls_io_instance->received_bytes, needed_buffer_size);
if (new_buffer == NULL)
{
LogError("realloc failed");
result = MU_FAILURE;
}
else
{
tls_io_instance->received_bytes = new_buffer;
tls_io_instance->buffer_size = needed_buffer_size;
result = 0;
}
}
else
{
result = 0;
}
return result;
}
static void on_underlying_io_close_complete(void* context)
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
if (tls_io_instance->tlsio_state == TLSIO_STATE_CLOSING)
{
tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN;
if (tls_io_instance->on_io_close_complete != NULL)
{
tls_io_instance->on_io_close_complete(tls_io_instance->on_io_close_complete_context);
}
/* Free security context resources corresponding to creation with open */
DeleteSecurityContext(&tls_io_instance->security_context);
if (tls_io_instance->credential_handle_allocated)
{
(void)FreeCredentialHandle(&tls_io_instance->credential_handle);
tls_io_instance->credential_handle_allocated = false;
}
}
}
// This callback usage needs to be either verified and commented or integrated into
// the state machine.
static void unchecked_on_send_complete(void* context, IO_SEND_RESULT send_result)
{
(void)context;
(void)send_result;
}
static void send_client_hello(TLS_IO_INSTANCE* tls_io_instance)
{
SecBuffer init_security_buffers[2];
ULONG context_attributes;
SECURITY_STATUS status;
SCHANNEL_CRED auth_data;
PCCERT_CONTEXT certContext;
auth_data.dwVersion = SCHANNEL_CRED_VERSION;
if (tls_io_instance->x509_schannel_handle != NULL)
{
certContext = x509_schannel_get_certificate_context(tls_io_instance->x509_schannel_handle);
auth_data.cCreds = 1;
auth_data.paCred = &certContext;
}
else
{
auth_data.cCreds = 0;
auth_data.paCred = NULL;
}
auth_data.hRootStore = NULL;
auth_data.cSupportedAlgs = 0;
auth_data.palgSupportedAlgs = NULL;
auth_data.grbitEnabledProtocols = 0;
auth_data.dwMinimumCipherStrength = 0;
auth_data.dwMaximumCipherStrength = 0;
auth_data.dwSessionLifespan = 0;
#if defined(SCH_USE_STRONG_CRYPTO)
auth_data.dwFlags = SCH_USE_STRONG_CRYPTO | SCH_CRED_NO_DEFAULT_CREDS;
#else
auth_data.dwFlags = SCH_CRED_NO_DEFAULT_CREDS;
#endif
if (tls_io_instance->ignore_server_name_check)
{
auth_data.dwFlags |= SCH_CRED_NO_SERVERNAME_CHECK;
}
auth_data.dwCredFormat = 0;
if (tls_io_instance->trustedCertificate != NULL)
{
// SCH_CRED_MANUAL_CRED_VALIDATION flag signals to schannel to NOT use
// the Windows certificate store, but instead have application verify
// certificate.
auth_data.dwFlags |= SCH_CRED_MANUAL_CRED_VALIDATION;
}
status = AcquireCredentialsHandle(NULL, UNISP_NAME, SECPKG_CRED_OUTBOUND, NULL,
&auth_data, NULL, NULL, &tls_io_instance->credential_handle, NULL);
if (status != SEC_E_OK)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
else
{
SecBufferDesc security_buffers_desc;
tls_io_instance->credential_handle_allocated = true;
init_security_buffers[0].cbBuffer = 0;
init_security_buffers[0].BufferType = SECBUFFER_TOKEN;
init_security_buffers[0].pvBuffer = NULL;
init_security_buffers[1].cbBuffer = 0;
init_security_buffers[1].BufferType = SECBUFFER_EMPTY;
init_security_buffers[1].pvBuffer = 0;
security_buffers_desc.cBuffers = 2;
security_buffers_desc.pBuffers = init_security_buffers;
security_buffers_desc.ulVersion = SECBUFFER_VERSION;
status = InitializeSecurityContext(&tls_io_instance->credential_handle,
NULL, tls_io_instance->host_name, ISC_REQ_EXTENDED_ERROR | ISC_REQ_STREAM | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_USE_SUPPLIED_CREDS, 0, 0, NULL, 0,
&tls_io_instance->security_context, &security_buffers_desc,
&context_attributes, NULL);
if ((status == SEC_I_COMPLETE_NEEDED) || (status == SEC_I_CONTINUE_NEEDED) || (status == SEC_I_COMPLETE_AND_CONTINUE))
{
if (xio_send(tls_io_instance->socket_io, init_security_buffers[0].pvBuffer, init_security_buffers[0].cbBuffer, unchecked_on_send_complete, NULL) != 0)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
else
{
/* set the needed bytes to 1, to get on the next byte how many we actually need */
tls_io_instance->needed_bytes = 1;
if (resize_receive_buffer(tls_io_instance, tls_io_instance->needed_bytes + tls_io_instance->received_byte_count) != 0)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
else
{
tls_io_instance->tlsio_state = TLSIO_STATE_HANDSHAKE_CLIENT_HELLO_SENT;
}
}
}
if (init_security_buffers[0].pvBuffer != NULL)
{
FreeContextBuffer(init_security_buffers[0].pvBuffer);
}
if (init_security_buffers[1].pvBuffer != NULL)
{
FreeContextBuffer(init_security_buffers[1].pvBuffer);
}
}
}
static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT io_open_result)
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
if (tls_io_instance->tlsio_state != TLSIO_STATE_OPENING_UNDERLYING_IO)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
else
{
if (io_open_result != IO_OPEN_OK)
{
tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN;
if (tls_io_instance->on_io_open_complete != NULL)
{
tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, IO_OPEN_ERROR);
}
}
else
{
send_client_hello(tls_io_instance);
}
}
}
static int verify_custom_certificate_if_needed(TLS_IO_INSTANCE* tls_io_instance)
{
int result;
if (tls_io_instance->trustedCertificate == NULL)
{
// If there is no trusted certificate set by API caller, then we'll rely on implicit Windows certificate store to verify the certificate.
result = 0;
}
else
{
PCERT_CONTEXT serverCertificateToVerify = NULL;
SECURITY_STATUS status = QueryContextAttributes(&tls_io_instance->security_context, SECPKG_ATTR_REMOTE_CERT_CONTEXT, &serverCertificateToVerify);
if (status != SEC_E_OK)
{
LogError("QueryContextAttributes failed: %x", status);
result = MU_FAILURE;
}
else if (x509_verify_certificate_in_chain(tls_io_instance->trustedCertificate, serverCertificateToVerify) != 0)
{
LogError("Failed to verify trusted certificate in chain");
result = MU_FAILURE;
}
else
{
result = 0;
}
if (serverCertificateToVerify)
{
CertFreeCertificateContext(serverCertificateToVerify);
}
}
return result;
}
static int set_receive_buffer(TLS_IO_INSTANCE* tls_io_instance, size_t buffer_size)
{
int result;
unsigned char* new_buffer = (unsigned char*) realloc(tls_io_instance->received_bytes, buffer_size);
if (new_buffer == NULL)
{
LogError("realloc failed");
result = MU_FAILURE;
}
else
{
tls_io_instance->received_bytes = new_buffer;
tls_io_instance->buffer_size = buffer_size;
result = 0;
}
return result;
}
static int send_chunk(CONCRETE_IO_HANDLE tls_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context)
{
int result;
if ((tls_io == NULL) ||
(buffer == NULL) ||
(size == 0))
{
LogError("invalid argument detected: CONCRETE_IO_HANDLE tls_io = %p, const void* buffer = %p, size_t size = %lu", tls_io, buffer, (unsigned long)size);
result = MU_FAILURE;
}
else
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
if (tls_io_instance->tlsio_state != TLSIO_STATE_OPEN)
{
LogError("invalid tls_io_instance->tlsio_state: %" PRI_MU_ENUM "", MU_ENUM_VALUE(TLSIO_STATE, tls_io_instance->tlsio_state));
result = MU_FAILURE;
}
else
{
SecPkgContext_StreamSizes sizes;
SECURITY_STATUS status = QueryContextAttributes(&tls_io_instance->security_context, SECPKG_ATTR_STREAM_SIZES, &sizes);
if (status != SEC_E_OK)
{
LogError("QueryContextAttributes failed: %x", status);
result = MU_FAILURE;
}
else
{
unsigned char* out_buffer;
SecBuffer security_buffers[4];
SecBufferDesc security_buffers_desc;
size_t needed_buffer = safe_add_size_t(sizes.cbHeader, size);
needed_buffer = safe_add_size_t(needed_buffer, sizes.cbTrailer);
if (needed_buffer == SIZE_MAX ||
(out_buffer = (unsigned char*)malloc(needed_buffer)) == NULL)
{
LogError("malloc failed, size:%zu", needed_buffer);
result = MU_FAILURE;
}
else
{
(void)memcpy(out_buffer + sizes.cbHeader, buffer, size);
security_buffers[0].BufferType = SECBUFFER_STREAM_HEADER;
security_buffers[0].cbBuffer = sizes.cbHeader;
security_buffers[0].pvBuffer = out_buffer;
security_buffers[1].BufferType = SECBUFFER_DATA;
security_buffers[1].cbBuffer = (unsigned long)size;
security_buffers[1].pvBuffer = out_buffer + sizes.cbHeader;
security_buffers[2].BufferType = SECBUFFER_STREAM_TRAILER;
security_buffers[2].cbBuffer = sizes.cbTrailer;
security_buffers[2].pvBuffer = out_buffer + sizes.cbHeader + size;
security_buffers[3].cbBuffer = 0;
security_buffers[3].BufferType = SECBUFFER_EMPTY;
security_buffers[3].pvBuffer = 0;
security_buffers_desc.cBuffers = sizeof(security_buffers) / sizeof(security_buffers[0]);
security_buffers_desc.pBuffers = security_buffers;
security_buffers_desc.ulVersion = SECBUFFER_VERSION;
status = EncryptMessage(&tls_io_instance->security_context, 0, &security_buffers_desc, 0);
if (FAILED(status))
{
LogError("EncryptMessage failed: %x", status);
result = MU_FAILURE;
}
else
{
if (xio_send(tls_io_instance->socket_io, out_buffer, (size_t)security_buffers[0].cbBuffer + (size_t)security_buffers[1].cbBuffer + (size_t)security_buffers[2].cbBuffer, on_send_complete, callback_context) != 0)
{
LogError("xio_send failed");
result = MU_FAILURE;
}
else
{
result = 0;
}
}
free(out_buffer);
}
}
}
}
return result;
}
static int internal_send(TLS_IO_INSTANCE* tls_io_instance, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context)
{
int result;
while (size > 0)
{
size_t to_send = 16 * 1024;
if (to_send > size)
{
to_send = size;
}
if (send_chunk(tls_io_instance, buffer, to_send, (to_send == size) ? on_send_complete : NULL, callback_context) != 0)
{
break;
}
size -= to_send;
buffer = ((const unsigned char*)buffer) + to_send;
}
if (size > 0)
{
LogError("send_chunk failed");
result = MU_FAILURE;
}
else
{
result = 0;
}
return result;
}
static void on_underlying_io_bytes_received(void* context, const unsigned char* buffer, size_t size)
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
size_t consumed_bytes;
if (resize_receive_buffer(tls_io_instance, tls_io_instance->received_byte_count + size) == 0)
{
(void)memcpy(tls_io_instance->received_bytes + tls_io_instance->received_byte_count, buffer, size);
tls_io_instance->received_byte_count += size;
if (size > tls_io_instance->needed_bytes)
{
tls_io_instance->needed_bytes = 0;
}
else
{
tls_io_instance->needed_bytes -= size;
}
/* Drain what we received */
while (tls_io_instance->needed_bytes == 0)
{
if ((tls_io_instance->tlsio_state == TLSIO_STATE_HANDSHAKE_CLIENT_HELLO_SENT) ||
(tls_io_instance->tlsio_state == TLSIO_STATE_RENEGOTIATE))
{
SecBuffer input_buffers[2];
SecBuffer output_buffers[2];
SecBufferDesc input_buffers_desc;
SecBufferDesc output_buffers_desc;
SECURITY_STATUS status;
unsigned long flags;
ULONG context_attributes;
/* we need to try and perform the second (next) step of the init */
input_buffers[0].cbBuffer = (unsigned long)tls_io_instance->received_byte_count;
input_buffers[0].BufferType = SECBUFFER_TOKEN;
input_buffers[0].pvBuffer = (void*)tls_io_instance->received_bytes;
input_buffers[1].cbBuffer = 0;
input_buffers[1].BufferType = SECBUFFER_EMPTY;
input_buffers[1].pvBuffer = 0;
input_buffers_desc.cBuffers = 2;
input_buffers_desc.pBuffers = input_buffers;
input_buffers_desc.ulVersion = SECBUFFER_VERSION;
output_buffers[0].cbBuffer = 0;
output_buffers[0].BufferType = SECBUFFER_TOKEN;
output_buffers[0].pvBuffer = NULL;
output_buffers[1].cbBuffer = 0;
output_buffers[1].BufferType = SECBUFFER_EMPTY;
output_buffers[1].pvBuffer = 0;
output_buffers_desc.cBuffers = 2;
output_buffers_desc.pBuffers = output_buffers;
output_buffers_desc.ulVersion = SECBUFFER_VERSION;
flags = ISC_REQ_EXTENDED_ERROR | ISC_REQ_STREAM | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_USE_SUPPLIED_CREDS;
status = InitializeSecurityContext(&tls_io_instance->credential_handle,
&tls_io_instance->security_context, tls_io_instance->host_name, flags, 0, 0, &input_buffers_desc, 0,
&tls_io_instance->security_context, &output_buffers_desc,
&context_attributes, NULL);
switch (status)
{
case SEC_E_INCOMPLETE_MESSAGE:
if (input_buffers[1].BufferType != SECBUFFER_MISSING)
{
//If SECBUFFER_MISSING not sent, try to read byte by byte.
tls_io_instance->needed_bytes = 1;
}
else
{
tls_io_instance->needed_bytes = input_buffers[1].cbBuffer;
}
if (resize_receive_buffer(tls_io_instance, tls_io_instance->received_byte_count + tls_io_instance->needed_bytes) != 0)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
if (tls_io_instance->on_io_open_complete != NULL)
{
tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, IO_OPEN_ERROR);
}
}
break;
case SEC_E_OK:
consumed_bytes = tls_io_instance->received_byte_count;
/* Any extra bytes left over or did we fully consume the receive buffer? */
if (input_buffers[1].BufferType == SECBUFFER_EXTRA)
{
consumed_bytes -= input_buffers[1].cbBuffer;
(void)memmove(tls_io_instance->received_bytes, tls_io_instance->received_bytes + consumed_bytes, tls_io_instance->received_byte_count - consumed_bytes);
}
tls_io_instance->received_byte_count -= consumed_bytes;
/* if nothing more to consume, set the needed bytes to 1, to get on the next byte how many we actually need */
tls_io_instance->needed_bytes = tls_io_instance->received_byte_count == 0 ? 1 : 0;
if (verify_custom_certificate_if_needed(tls_io_instance) != 0)
{
LogError("Unable to verify server certificate against custom server trusted certificate");
if (tls_io_instance->on_io_open_complete != NULL)
{
tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, IO_OPEN_ERROR);
}
}
else if (set_receive_buffer(tls_io_instance, tls_io_instance->needed_bytes + tls_io_instance->received_byte_count) != 0)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
if (tls_io_instance->on_io_open_complete != NULL)
{
tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, IO_OPEN_ERROR);
}
}
else
{
if (tls_io_instance->tlsio_state == TLSIO_STATE_HANDSHAKE_CLIENT_HELLO_SENT)
{
tls_io_instance->tlsio_state = TLSIO_STATE_OPEN;
if (tls_io_instance->on_io_open_complete != NULL)
{
tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, IO_OPEN_OK);
}
}
else
{
LIST_ITEM_HANDLE first_pending_io;
tls_io_instance->tlsio_state = TLSIO_STATE_OPEN;
first_pending_io = singlylinkedlist_get_head_item(tls_io_instance->pending_io_list);
while (first_pending_io != NULL)
{
PENDING_SEND* pending_send = (PENDING_SEND*)singlylinkedlist_item_get_value(first_pending_io);
if (pending_send == NULL)
{
LogError("Failure: retrieving pending IO from list");
indicate_error(tls_io_instance);
break;
}
else
{
if (internal_send(tls_io_instance, pending_send->bytes, pending_send->length, pending_send->on_send_complete, pending_send->on_send_complete_context) != 0)
{
LogError("send failed");
indicate_error(tls_io_instance);
}
else
{
if (singlylinkedlist_remove(tls_io_instance->pending_io_list, first_pending_io) != 0)
{
LogError("Failure: removing pending IO from list");
indicate_error(tls_io_instance);
}
}
first_pending_io = singlylinkedlist_get_head_item(tls_io_instance->pending_io_list);
}
}
}
}
break;
case SEC_I_COMPLETE_NEEDED:
case SEC_I_CONTINUE_NEEDED:
case SEC_I_COMPLETE_AND_CONTINUE:
if ((output_buffers[0].cbBuffer > 0) && xio_send(tls_io_instance->socket_io, output_buffers[0].pvBuffer, output_buffers[0].cbBuffer, unchecked_on_send_complete, NULL) != 0)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
if (tls_io_instance->on_io_open_complete != NULL)
{
tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, IO_OPEN_ERROR);
}
}
else
{
consumed_bytes = tls_io_instance->received_byte_count;
/* Any extra bytes left over or did we fully consume the receive buffer? */
if (input_buffers[1].BufferType == SECBUFFER_EXTRA)
{
consumed_bytes -= input_buffers[1].cbBuffer;
(void)memmove(tls_io_instance->received_bytes, tls_io_instance->received_bytes + consumed_bytes, tls_io_instance->received_byte_count - consumed_bytes);
}
tls_io_instance->received_byte_count -= consumed_bytes;
/* if nothing more to consume, set the needed bytes to 1, to get on the next byte how many we actually need */
tls_io_instance->needed_bytes = tls_io_instance->received_byte_count == 0 ? 1 : 0;
if (set_receive_buffer(tls_io_instance, tls_io_instance->needed_bytes + tls_io_instance->received_byte_count) != 0)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
if (tls_io_instance->on_io_open_complete != NULL)
{
tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, IO_OPEN_ERROR);
}
}
else
{
if (tls_io_instance->tlsio_state != TLSIO_STATE_RENEGOTIATE)
{
tls_io_instance->tlsio_state = TLSIO_STATE_HANDSHAKE_CLIENT_HELLO_SENT;
}
}
}
break;
case SEC_E_UNTRUSTED_ROOT:
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
if (tls_io_instance->on_io_open_complete != NULL)
{
tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, IO_OPEN_ERROR);
}
break;
default:
{
LPVOID srcText = NULL;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&srcText, 0, NULL) > 0)
{
LogError("[%#x] %s", status, (LPTSTR)srcText);
LocalFree(srcText);
}
else
{
LogError("[%#x]", status);
}
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
break;
}
if (output_buffers[0].pvBuffer != NULL)
{
FreeContextBuffer(output_buffers[0].pvBuffer);
output_buffers[0].pvBuffer = NULL;
}
if (output_buffers[1].pvBuffer != NULL)
{
FreeContextBuffer(output_buffers[1].pvBuffer);
output_buffers[1].pvBuffer = NULL;
}
}
else if (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN)
{
SecBuffer security_buffers[4];
SecBufferDesc security_buffers_desc;
SECURITY_STATUS status;
security_buffers[0].BufferType = SECBUFFER_DATA;
security_buffers[0].pvBuffer = tls_io_instance->received_bytes;
security_buffers[0].cbBuffer = (unsigned long)tls_io_instance->received_byte_count;
security_buffers[1].BufferType = SECBUFFER_EMPTY;
security_buffers[2].BufferType = SECBUFFER_EMPTY;
security_buffers[3].BufferType = SECBUFFER_EMPTY;
security_buffers_desc.cBuffers = sizeof(security_buffers) / sizeof(security_buffers[0]);
security_buffers_desc.pBuffers = security_buffers;
security_buffers_desc.ulVersion = SECBUFFER_VERSION;
status = DecryptMessage(&tls_io_instance->security_context, &security_buffers_desc, 0, NULL);
switch (status)
{
case SEC_E_INCOMPLETE_MESSAGE:
if (security_buffers[1].BufferType != SECBUFFER_MISSING)
{
//If SECBUFFER_MISSING not sent, try to read byte by byte.
tls_io_instance->needed_bytes = 1;
}
else
{
tls_io_instance->needed_bytes = security_buffers[1].cbBuffer;
}
if (resize_receive_buffer(tls_io_instance, tls_io_instance->received_byte_count + tls_io_instance->needed_bytes) != 0)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
break;
case SEC_E_OK:
if (security_buffers[1].BufferType != SECBUFFER_DATA)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
else
{
size_t i;
/* notify of the received data */
if (tls_io_instance->on_bytes_received != NULL)
{
tls_io_instance->on_bytes_received(tls_io_instance->on_bytes_received_context, (const unsigned char *) security_buffers[1].pvBuffer, security_buffers[1].cbBuffer);
}
consumed_bytes = tls_io_instance->received_byte_count;
for (i = 0; i < sizeof(security_buffers) / sizeof(security_buffers[0]); i++)
{
/* Any extra bytes left over or did we fully consume the receive buffer? */
if (security_buffers[i].BufferType == SECBUFFER_EXTRA)
{
consumed_bytes -= security_buffers[i].cbBuffer;
(void)memmove(tls_io_instance->received_bytes, tls_io_instance->received_bytes + consumed_bytes, tls_io_instance->received_byte_count - consumed_bytes);
break;
}
}
tls_io_instance->received_byte_count -= consumed_bytes;
/* if nothing more to consume, set the needed bytes to 1, to get on the next byte how many we actually need */
tls_io_instance->needed_bytes = tls_io_instance->received_byte_count == 0 ? 1 : 0;
if (set_receive_buffer(tls_io_instance, tls_io_instance->needed_bytes + tls_io_instance->received_byte_count) != 0)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
}
break;
case SEC_I_RENEGOTIATE:
{
SecBuffer input_buffers[2];
SecBuffer output_buffers[2];
SecBufferDesc input_buffers_desc;
SecBufferDesc output_buffers_desc;
unsigned long flags;
ULONG context_attributes;
/* we need to try and perform the second (next) step of the init */
input_buffers[0].cbBuffer = (unsigned long)tls_io_instance->received_byte_count;
input_buffers[0].BufferType = SECBUFFER_TOKEN;
input_buffers[0].pvBuffer = (void*)tls_io_instance->received_bytes;
input_buffers[1].cbBuffer = 0;
input_buffers[1].BufferType = SECBUFFER_EMPTY;
input_buffers[1].pvBuffer = 0;
input_buffers_desc.cBuffers = 2;
input_buffers_desc.pBuffers = input_buffers;
input_buffers_desc.ulVersion = SECBUFFER_VERSION;
output_buffers[0].cbBuffer = 0;
output_buffers[0].BufferType = SECBUFFER_TOKEN;
output_buffers[0].pvBuffer = NULL;
output_buffers[1].cbBuffer = 0;
output_buffers[1].BufferType = SECBUFFER_EMPTY;
output_buffers[1].pvBuffer = 0;
output_buffers_desc.cBuffers = 2;
output_buffers_desc.pBuffers = output_buffers;
output_buffers_desc.ulVersion = SECBUFFER_VERSION;
flags = ISC_REQ_EXTENDED_ERROR | ISC_REQ_STREAM | ISC_REQ_ALLOCATE_MEMORY | ISC_REQ_USE_SUPPLIED_CREDS;
status = InitializeSecurityContext(&tls_io_instance->credential_handle,
&tls_io_instance->security_context, tls_io_instance->host_name, flags, 0, 0, &input_buffers_desc, 0,
&tls_io_instance->security_context, &output_buffers_desc,
&context_attributes, NULL);
if ((status == SEC_I_COMPLETE_NEEDED) || (status == SEC_I_CONTINUE_NEEDED) || (status == SEC_I_COMPLETE_AND_CONTINUE))
{
/* This needs to account for EXTRA */
tls_io_instance->received_byte_count = 0;
if (xio_send(tls_io_instance->socket_io, output_buffers[0].pvBuffer, output_buffers[0].cbBuffer, unchecked_on_send_complete, NULL) != 0)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
else
{
tls_io_instance->needed_bytes = 1;
if (resize_receive_buffer(tls_io_instance, tls_io_instance->needed_bytes + tls_io_instance->received_byte_count) != 0)
{
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
else
{
tls_io_instance->tlsio_state = TLSIO_STATE_RENEGOTIATE;
}
}
}
if (output_buffers[0].pvBuffer != NULL)
{
FreeContextBuffer(output_buffers[0].pvBuffer);
output_buffers[0].pvBuffer = NULL;
}
if (output_buffers[1].pvBuffer != NULL)
{
FreeContextBuffer(output_buffers[1].pvBuffer);
output_buffers[1].pvBuffer = NULL;
}
break;
}
default:
{
LPVOID srcText = NULL;
if (FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL,
status, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPTSTR)&srcText, 0, NULL) > 0)
{
LogError("[%#x] %s", status, (LPTSTR)srcText);
LocalFree(srcText);
}
else
{
LogError("[%#x]", status);
}
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
}
break;
}
}
else
{
/* Received data in error or other state */
break;
}
}
}
}
static void on_underlying_io_error(void* context)
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
switch (tls_io_instance->tlsio_state)
{
default:
case TLSIO_STATE_NOT_OPEN:
case TLSIO_STATE_ERROR:
break;
case TLSIO_STATE_OPENING_UNDERLYING_IO:
case TLSIO_STATE_HANDSHAKE_CLIENT_HELLO_SENT:
case TLSIO_STATE_HANDSHAKE_SERVER_HELLO_RECEIVED:
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
if (tls_io_instance->on_io_open_complete != NULL)
{
tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, IO_OPEN_ERROR);
}
break;
case TLSIO_STATE_CLOSING:
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
if (tls_io_instance->on_io_close_complete != NULL)
{
tls_io_instance->on_io_close_complete(tls_io_instance->on_io_close_complete_context);
}
break;
case TLSIO_STATE_OPEN:
tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
indicate_error(tls_io_instance);
break;
}
}
CONCRETE_IO_HANDLE tlsio_schannel_create(void* io_create_parameters)
{
TLSIO_CONFIG* tls_io_config = (TLSIO_CONFIG *) io_create_parameters;
TLS_IO_INSTANCE* result;
if (tls_io_config == NULL)
{
LogError("invalid argument detected: void* io_create_parameters = %p", tls_io_config);
result = NULL;
}
else
{
result = (TLS_IO_INSTANCE*)malloc(sizeof(TLS_IO_INSTANCE));
if (result == NULL)
{
LogError("malloc failed");
}
else
{
(void)memset(result, 0, sizeof(TLS_IO_INSTANCE));
size_t malloc_size = safe_add_size_t(strlen(tls_io_config->hostname), 1);
malloc_size = safe_multiply_size_t(malloc_size, sizeof(SEC_TCHAR));
if (malloc_size == SIZE_MAX ||
(result->host_name = (SEC_TCHAR*)malloc(malloc_size)) == NULL)
{
LogError("malloc failed, size:%zu", malloc_size);
free(result);
result = NULL;
}
else
{
SOCKETIO_CONFIG socketio_config;
const IO_INTERFACE_DESCRIPTION* underlying_io_interface;
void* io_interface_parameters;
(void)strcpy(result->host_name, tls_io_config->hostname);
if (tls_io_config->underlying_io_interface != NULL)
{
underlying_io_interface = tls_io_config->underlying_io_interface;
io_interface_parameters = tls_io_config->underlying_io_parameters;
}
else
{
socketio_config.hostname = tls_io_config->hostname;
socketio_config.port = tls_io_config->port;
socketio_config.accepted_socket = NULL;
underlying_io_interface = socketio_get_interface_description();
io_interface_parameters = &socketio_config;
}
if (underlying_io_interface == NULL)
{
LogError("socketio_get_interface_description failed");
free(result->host_name);
free(result);
result = NULL;
}
else
{
result->socket_io = xio_create(underlying_io_interface, io_interface_parameters);
if (result->socket_io == NULL)
{
LogError("xio_create failed");
free(result->host_name);
free(result);
result = NULL;
}
else
{
result->pending_io_list = singlylinkedlist_create();
if (result->pending_io_list == NULL)
{
LogError("Failed creating pending IO list.");
xio_destroy(result->socket_io);
free(result->host_name);
free(result);
result = NULL;
}
else
{
result->received_bytes = NULL;
result->received_byte_count = 0;
result->buffer_size = 0;
result->tlsio_state = TLSIO_STATE_NOT_OPEN;
result->x509certificate = NULL;
result->x509privatekey = NULL;
result->x509_schannel_handle = NULL;
}
}
}
}
}
}
return result;
}
void tlsio_schannel_destroy(CONCRETE_IO_HANDLE tls_io)
{
if (tls_io != NULL)
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
LIST_ITEM_HANDLE first_pending_io;
if (tls_io_instance->credential_handle_allocated)
{
(void)FreeCredentialHandle(&tls_io_instance->credential_handle);
tls_io_instance->credential_handle_allocated = false;
}
if (tls_io_instance->received_bytes != NULL)
{
free(tls_io_instance->received_bytes);
tls_io_instance->received_bytes = NULL;
}
if (tls_io_instance->trustedCertificate != NULL)
{
free(tls_io_instance->trustedCertificate);
tls_io_instance->trustedCertificate = NULL;
}
if (tls_io_instance->x509_schannel_handle != NULL)
{
x509_schannel_destroy(tls_io_instance->x509_schannel_handle);
tls_io_instance->x509_schannel_handle = NULL;
}
if (tls_io_instance->x509certificate != NULL)
{
free(tls_io_instance->x509certificate);
tls_io_instance->x509certificate = NULL;
}
if (tls_io_instance->x509privatekey != NULL)
{
free(tls_io_instance->x509privatekey);
tls_io_instance->x509privatekey = NULL;
}
xio_destroy(tls_io_instance->socket_io);
tls_io_instance->socket_io = NULL;
free(tls_io_instance->host_name);
tls_io_instance->host_name = NULL;
first_pending_io = singlylinkedlist_get_head_item(tls_io_instance->pending_io_list);
while (first_pending_io != NULL)
{
PENDING_SEND* pending_send = (PENDING_SEND*)singlylinkedlist_item_get_value(first_pending_io);
if (pending_send == NULL)
{
LogError("Failure: retrieving socket from list");
indicate_error(tls_io_instance);
break;
}
else
{
if (pending_send->on_send_complete != NULL)
{
pending_send->on_send_complete(pending_send->on_send_complete_context, IO_SEND_CANCELLED);
}
if (singlylinkedlist_remove(tls_io_instance->pending_io_list, first_pending_io) != 0)
{
LogError("Failure: removing pending IO from list");
indicate_error(tls_io_instance);
break;
}
first_pending_io = singlylinkedlist_get_head_item(tls_io_instance->pending_io_list);
}
}
singlylinkedlist_destroy(tls_io_instance->pending_io_list);
free(tls_io);
}
}
int tlsio_schannel_open(CONCRETE_IO_HANDLE tls_io, ON_IO_OPEN_COMPLETE on_io_open_complete, void* on_io_open_complete_context, ON_BYTES_RECEIVED on_bytes_received, void* on_bytes_received_context, ON_IO_ERROR on_io_error, void* on_io_error_context)
{
int result;
if (tls_io == NULL)
{
LogError("invalid argument detected: CONCRETE_IO_HANDLE tls_io = %p", tls_io);
result = MU_FAILURE;
}
else
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
if (tls_io_instance->tlsio_state != TLSIO_STATE_NOT_OPEN)
{
LogError("invalid tls_io_instance->tlsio_state = %" PRI_MU_ENUM "", MU_ENUM_VALUE(TLSIO_STATE, tls_io_instance->tlsio_state));
result = MU_FAILURE;
}
else
{
tls_io_instance->on_io_open_complete = on_io_open_complete;
tls_io_instance->on_io_open_complete_context = on_io_open_complete_context;
tls_io_instance->on_bytes_received = on_bytes_received;
tls_io_instance->on_bytes_received_context = on_bytes_received_context;
tls_io_instance->on_io_error = on_io_error;
tls_io_instance->on_io_error_context = on_io_error_context;
tls_io_instance->tlsio_state = TLSIO_STATE_OPENING_UNDERLYING_IO;
if (xio_open(tls_io_instance->socket_io, on_underlying_io_open_complete, tls_io_instance, on_underlying_io_bytes_received, tls_io_instance, on_underlying_io_error, tls_io_instance) != 0)
{
LogError("xio_open failed");
tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN;
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
return result;
}
int tlsio_schannel_close(CONCRETE_IO_HANDLE tls_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context)
{
int result = 0;
if (tls_io == NULL)
{
LogError("invalid argument detected: tls_io = %p", tls_io);
result = MU_FAILURE;
}
else
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
if ((tls_io_instance->tlsio_state == TLSIO_STATE_NOT_OPEN) ||
(tls_io_instance->tlsio_state == TLSIO_STATE_CLOSING))
{
LogError("invalid tls_io_instance->tlsio_state = %" PRI_MU_ENUM "", MU_ENUM_VALUE(TLSIO_STATE, tls_io_instance->tlsio_state));
result = MU_FAILURE;
}
else
{
tls_io_instance->tlsio_state = TLSIO_STATE_CLOSING;
tls_io_instance->on_io_close_complete = on_io_close_complete;
tls_io_instance->on_io_close_complete_context = callback_context;
if (xio_close(tls_io_instance->socket_io, on_underlying_io_close_complete, tls_io_instance) != 0)
{
LogError("xio_close failed");
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
return result;
}
int tlsio_schannel_send(CONCRETE_IO_HANDLE tls_io, const void* buffer, size_t size, ON_SEND_COMPLETE on_send_complete, void* callback_context)
{
int result;
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
if (tls_io_instance->tlsio_state == TLSIO_STATE_RENEGOTIATE)
{
/* add to pending list */
PENDING_SEND* new_pending_send = (PENDING_SEND*)calloc(1, sizeof(PENDING_SEND));
if (new_pending_send == NULL)
{
LogError("Cannot allocate memory for pending IO");
result = MU_FAILURE;
}
else
{
new_pending_send->bytes = (unsigned char*)malloc(size);
if (new_pending_send->bytes == NULL)
{
LogError("Cannot allocate memory for pending IO payload");
result = MU_FAILURE;
}
else
{
(void)memcpy(new_pending_send->bytes, buffer, size);
new_pending_send->length = size;
new_pending_send->on_send_complete = on_send_complete;
new_pending_send->on_send_complete_context = callback_context;
if (singlylinkedlist_add(tls_io_instance->pending_io_list, new_pending_send) == NULL)
{
LogError("Cannot add pending IO to list");
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
}
else
{
if (internal_send((TLS_IO_INSTANCE*)tls_io, buffer, size, on_send_complete, callback_context) != 0)
{
LogError("send failed");
result = MU_FAILURE;
}
else
{
result = 0;
}
}
return result;
}
void tlsio_schannel_dowork(CONCRETE_IO_HANDLE tls_io)
{
if (tls_io != NULL)
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
xio_dowork(tls_io_instance->socket_io);
}
}
int tlsio_schannel_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value)
{
int result;
if (tls_io == NULL || optionName == NULL)
{
LogError("invalid argument detected: CONCRETE_IO_HANDLE tls_io = %p, const char* optionName = %p", tls_io, optionName);
result = MU_FAILURE;
}
else
{
TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io;
if (strcmp(SU_OPTION_X509_CERT, optionName) == 0 || strcmp(OPTION_X509_ECC_CERT, optionName) == 0)
{
if (tls_io_instance->x509certificate != NULL)
{
LogError("x509certificate has already been specified");
result = MU_FAILURE;
}
else
{
tls_io_instance->x509certificate = (char *)tlsio_schannel_CloneOption(optionName, value);
if (tls_io_instance->x509certificate == NULL)
{
LogError("tlsio_schannel_CloneOption failed");
result = MU_FAILURE;
}
else
{
if (tls_io_instance->x509privatekey != NULL)
{
tls_io_instance->x509_schannel_handle = x509_schannel_create(tls_io_instance->x509certificate, tls_io_instance->x509privatekey);
if (tls_io_instance->x509_schannel_handle == NULL)
{
LogError("x509_schannel_create failed");
result = MU_FAILURE;
}
else
{
/*all is fine, the x509 shall be used later*/
result = 0;
}
}
else
{
result = 0; /*all is fine, maybe x509 privatekey will come and then x509 is set*/
}
}
}
}
else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0 || strcmp(OPTION_X509_ECC_KEY, optionName) == 0)
{
if (tls_io_instance->x509privatekey != NULL)
{
LogError("x509privatekey has already been specified");
result = MU_FAILURE;
}
else
{
tls_io_instance->x509privatekey = (char *)tlsio_schannel_CloneOption(optionName, value);
if (tls_io_instance->x509privatekey == NULL)
{
LogError("tlsio_schannel_CloneOption failed");
result = MU_FAILURE;
}
else
{
if (tls_io_instance->x509certificate != NULL)
{
tls_io_instance->x509_schannel_handle = x509_schannel_create(tls_io_instance->x509certificate, tls_io_instance->x509privatekey);
if (tls_io_instance->x509_schannel_handle == NULL)
{
LogError("x509_schannel_create failed");
result = MU_FAILURE;
}
else
{
/*all is fine, the x509 shall be used later*/
result = 0;
}
}
else
{
result = 0; /*all is fine, maybe x509 cert will come and then x509 is set*/
}
}
}
}
else if (strcmp(OPTION_TRUSTED_CERT, optionName) == 0)
{
if (value == NULL)
{
LogError("Invalid paramater: OPTION_TRUSTED_CERT value=NULL");
result = MU_FAILURE;
}
else
{
if (tls_io_instance->trustedCertificate != NULL)
{
free(tls_io_instance->trustedCertificate);
tls_io_instance->trustedCertificate = NULL;
}
if (mallocAndStrcpy_s((char**)&tls_io_instance->trustedCertificate, value) != 0)
{
LogError("unable to mallocAndStrcpy_s %s", optionName);
result = MU_FAILURE;
}
else
{
result = 0;
}
}
}
else if (strcmp("ignore_server_name_check", optionName) == 0)
{
bool* server_name_check = (bool*)value;
tls_io_instance->ignore_server_name_check = *server_name_check;
result = 0;
}
else if (tls_io_instance->socket_io == NULL)
{
LogError("tls_io_instance->socket_io is not set");
result = MU_FAILURE;
}
else if (strcmp(optionName, OPTION_SET_TLS_RENEGOTIATION) == 0)
{
// No need to do anything for Schannel
result = 0;
}
else
{
result = xio_setoption(tls_io_instance->socket_io, optionName, value);
}
}
return result;
}
const IO_INTERFACE_DESCRIPTION* tlsio_schannel_get_interface_description(void)
{
return &tlsio_schannel_interface_description;
}