Azure IoT common library
Dependents: STM32F746_iothub_client_sample_mqtt f767zi_mqtt iothub_client_sample_amqp iothub_client_sample_http ... more
Diff: tlsio_wolfssl.c
- Revision:
- 0:fa2de1b79154
- Child:
- 1:9190c0f4d23a
diff -r 000000000000 -r fa2de1b79154 tlsio_wolfssl.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tlsio_wolfssl.c Fri Apr 08 12:01:36 2016 -0700 @@ -0,0 +1,555 @@ +// Copyright (c) Microsoft. All rights reserved. +// Licensed under the MIT license. See LICENSE file in the project root for full license information. + +#include <stdlib.h> +#ifdef _CRTDBG_MAP_ALLOC +#include <crtdbg.h> +#endif + +#include "wolfssl/ssl.h" +#include "wolfssl/error-ssl.h" +#include <stdio.h> +#include <stdbool.h> +#include <string.h> +#include "azure_c_shared_utility/tlsio.h" +#include "azure_c_shared_utility/tlsio_wolfssl.h" +#include "azure_c_shared_utility/socketio.h" + +typedef enum TLSIO_STATE_ENUM_TAG +{ + TLSIO_STATE_NOT_OPEN, + TLSIO_STATE_OPENING_UNDERLYING_IO, + TLSIO_STATE_IN_HANDSHAKE, + TLSIO_STATE_OPEN, + TLSIO_STATE_CLOSING, + TLSIO_STATE_ERROR +} TLSIO_STATE_ENUM; + +typedef struct TLS_IO_INSTANCE_TAG +{ + XIO_HANDLE socket_io; + ON_BYTES_RECEIVED on_bytes_received; + ON_IO_OPEN_COMPLETE on_io_open_complete; + ON_IO_CLOSE_COMPLETE on_io_close_complete; + ON_IO_ERROR on_io_error; + void* on_bytes_received_context; + void* on_io_open_complete_context; + void* on_io_close_complete_context; + void* on_io_error_context; + LOGGER_LOG logger_log; + WOLFSSL* ssl; + WOLFSSL_CTX* ssl_context; + TLSIO_STATE_ENUM tlsio_state; + unsigned char* socket_io_read_bytes; + size_t socket_io_read_byte_count; + ON_SEND_COMPLETE on_send_complete; + void* on_send_complete_callback_context; +} TLS_IO_INSTANCE; + +static const IO_INTERFACE_DESCRIPTION tlsio_wolfssl_interface_description = +{ + tlsio_wolfssl_create, + tlsio_wolfssl_destroy, + tlsio_wolfssl_open, + tlsio_wolfssl_close, + tlsio_wolfssl_send, + tlsio_wolfssl_dowork, + tlsio_wolfssl_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 void indicate_open_complete(TLS_IO_INSTANCE* tls_io_instance, IO_OPEN_RESULT open_result) +{ + if (tls_io_instance->on_io_open_complete != NULL) + { + tls_io_instance->on_io_open_complete(tls_io_instance->on_io_open_complete_context, open_result); + } +} + +static int decode_ssl_received_bytes(TLS_IO_INSTANCE* tls_io_instance) +{ + int result = 0; + unsigned char buffer[64]; + + int rcv_bytes = 1; + while (rcv_bytes > 0) + { + rcv_bytes = wolfSSL_read(tls_io_instance->ssl, buffer, sizeof(buffer)); + if (rcv_bytes > 0) + { + if (tls_io_instance->on_bytes_received != NULL) + { + tls_io_instance->on_bytes_received(tls_io_instance->on_bytes_received_context, buffer, rcv_bytes); + } + } + } + + return result; +} + +static void on_underlying_io_open_complete(void* context, IO_OPEN_RESULT open_result) +{ + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context; + + if (open_result != IO_OPEN_OK) + { + tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; + indicate_open_complete(tls_io_instance, IO_OPEN_ERROR); + } + else + { + int res; + tls_io_instance->tlsio_state = TLSIO_STATE_IN_HANDSHAKE; + + res = wolfSSL_connect(tls_io_instance->ssl); + if (res != SSL_SUCCESS) + { + indicate_open_complete(tls_io_instance, IO_OPEN_ERROR); + } + } +} + +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; + + unsigned char* new_socket_io_read_bytes = (unsigned char*)realloc(tls_io_instance->socket_io_read_bytes, tls_io_instance->socket_io_read_byte_count + size); + if (new_socket_io_read_bytes == NULL) + { + tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; + indicate_error(tls_io_instance); + } + else + { + tls_io_instance->socket_io_read_bytes = new_socket_io_read_bytes; + (void)memcpy(tls_io_instance->socket_io_read_bytes + tls_io_instance->socket_io_read_byte_count, buffer, size); + tls_io_instance->socket_io_read_byte_count += size; + } +} + +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_IN_HANDSHAKE: + tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; + indicate_open_complete(tls_io_instance, IO_OPEN_ERROR); + break; + + case TLSIO_STATE_OPEN: + tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; + indicate_error(tls_io_instance); + break; + } +} + +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) + { + if (tls_io_instance->on_io_close_complete != NULL) + { + tls_io_instance->on_io_close_complete(tls_io_instance->on_io_close_complete_context); + } + } +} + +static int on_io_recv(WOLFSSL *ssl, char *buf, int sz, void *context) +{ + int result; + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context; + unsigned char* new_socket_io_read_bytes; + + while (tls_io_instance->socket_io_read_byte_count == 0) + { + xio_dowork(tls_io_instance->socket_io); + if (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN) + { + break; + } + } + + result = tls_io_instance->socket_io_read_byte_count; + if (result > sz) + { + result = sz; + } + + if (result > 0) + { + (void)memcpy(buf, tls_io_instance->socket_io_read_bytes, result); + (void)memmove(tls_io_instance->socket_io_read_bytes, tls_io_instance->socket_io_read_bytes + result, tls_io_instance->socket_io_read_byte_count - result); + tls_io_instance->socket_io_read_byte_count -= result; + if (tls_io_instance->socket_io_read_byte_count > 0) + { + new_socket_io_read_bytes = (unsigned char*)realloc(tls_io_instance->socket_io_read_bytes, tls_io_instance->socket_io_read_byte_count); + if (new_socket_io_read_bytes != NULL) + { + tls_io_instance->socket_io_read_bytes = new_socket_io_read_bytes; + } + } + else + { + free(tls_io_instance->socket_io_read_bytes); + tls_io_instance->socket_io_read_bytes = NULL; + } + } + + if ((result == 0) && (tls_io_instance->tlsio_state == TLSIO_STATE_OPEN)) + { + result = WOLFSSL_CBIO_ERR_WANT_READ; + } + + return result; +} + +static int on_io_send(WOLFSSL *ssl, char *buf, int sz, void *context) +{ + int result; + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context; + + if (xio_send(tls_io_instance->socket_io, buf, sz, tls_io_instance->on_send_complete, tls_io_instance->on_send_complete_callback_context) != 0) + { + tls_io_instance->tlsio_state = TLSIO_STATE_ERROR; + indicate_error(tls_io_instance); + result = 0; + } + else + { + result = sz; + } + + return result; +} + +static int on_handshake_done(WOLFSSL* ssl, void* context) +{ + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context; + if (tls_io_instance->tlsio_state == TLSIO_STATE_IN_HANDSHAKE) + { + tls_io_instance->tlsio_state = TLSIO_STATE_OPEN; + indicate_open_complete(tls_io_instance, IO_OPEN_OK); + } + + return 0; +} + +int tlsio_wolfssl_init(void) +{ + (void)wolfSSL_library_init(); + wolfSSL_load_error_strings(); + + return 0; +} + +void tlsio_wolfssl_deinit(void) +{ +} + +CONCRETE_IO_HANDLE tlsio_wolfssl_create(void* io_create_parameters, LOGGER_LOG logger_log) +{ + TLSIO_CONFIG* tls_io_config = io_create_parameters; + TLS_IO_INSTANCE* result; + + if (tls_io_config == NULL) + { + result = NULL; + } + else + { + result = malloc(sizeof(TLS_IO_INSTANCE)); + if (result != NULL) + { + SOCKETIO_CONFIG socketio_config; + + socketio_config.hostname = tls_io_config->hostname; + socketio_config.port = tls_io_config->port; + socketio_config.accepted_socket = NULL; + + result->on_bytes_received = NULL; + result->on_bytes_received_context = NULL; + + result->on_io_open_complete = NULL; + result->on_io_open_complete_context = NULL; + + result->on_io_close_complete = NULL; + result->on_io_close_complete_context = NULL; + + result->on_io_error = NULL; + result->on_io_error_context = NULL; + + result->logger_log = logger_log; + + result->ssl_context = wolfSSL_CTX_new(wolfTLSv1_client_method()); + if (result->ssl_context == NULL) + { + free(result); + result = NULL; + } + else + { + const IO_INTERFACE_DESCRIPTION* socket_io_interface = socketio_get_interface_description(); + if (socket_io_interface == NULL) + { + wolfSSL_CTX_free(result->ssl_context); + free(result); + result = NULL; + } + else + { + result->socket_io = xio_create(socket_io_interface, &socketio_config, logger_log); + if (result->socket_io == NULL) + { + wolfSSL_CTX_free(result->ssl_context); + free(result); + result = NULL; + } + else + { + result->ssl = wolfSSL_new(result->ssl_context); + if (result->ssl == NULL) + { + wolfSSL_CTX_free(result->ssl_context); + free(result); + result = NULL; + } + else + { + result->socket_io_read_bytes = NULL; + result->socket_io_read_byte_count = 0; + result->on_send_complete = NULL; + result->on_send_complete_callback_context = NULL; + + wolfSSL_set_using_nonblock(result->ssl, 1); + wolfSSL_SetIOSend(result->ssl_context, on_io_send); + wolfSSL_SetIORecv(result->ssl_context, on_io_recv); + wolfSSL_SetHsDoneCb(result->ssl, on_handshake_done, result); + wolfSSL_SetIOWriteCtx(result->ssl, result); + wolfSSL_SetIOReadCtx(result->ssl, result); + + result->tlsio_state = TLSIO_STATE_NOT_OPEN; + } + } + } + } + } + } + + return result; +} + +void tlsio_wolfssl_destroy(CONCRETE_IO_HANDLE tls_io) +{ + if (tls_io != NULL) + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + wolfSSL_free(tls_io_instance->ssl); + wolfSSL_CTX_free(tls_io_instance->ssl_context); + + if (tls_io_instance->socket_io_read_bytes != NULL) + { + free(tls_io_instance->socket_io_read_bytes); + } + + xio_destroy(tls_io_instance->socket_io); + free(tls_io); + } +} + +int tlsio_wolfssl_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) + { + result = __LINE__; + } + else + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + if (tls_io_instance->tlsio_state != TLSIO_STATE_NOT_OPEN) + { + result = __LINE__; + } + else + { + 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_open_complete = on_io_open_complete; + tls_io_instance->on_io_open_complete_context = on_io_open_complete_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) + { + tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN; + result = __LINE__; + } + else + { + int res; + tls_io_instance->tlsio_state = TLSIO_STATE_IN_HANDSHAKE; + + res = wolfSSL_connect(tls_io_instance->ssl); + if (res != SSL_SUCCESS) + { + result = __LINE__; + } + else + { + result = 0; + } + } + } + } + + return result; +} + +int tlsio_wolfssl_close(CONCRETE_IO_HANDLE tls_io, ON_IO_CLOSE_COMPLETE on_io_close_complete, void* callback_context) +{ + int result = 0; + + if (tls_io == NULL) + { + result = __LINE__; + } + 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)) + { + result = __LINE__; + } + 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) + { + result = __LINE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +int tlsio_wolfssl_send(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) + { + result = __LINE__; + } + else + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + if (tls_io_instance->tlsio_state != TLSIO_STATE_OPEN) + { + result = __LINE__; + } + else + { + tls_io_instance->on_send_complete = on_send_complete; + tls_io_instance->on_send_complete_callback_context = callback_context; + + int res = wolfSSL_write(tls_io_instance->ssl, buffer, size); + if (res != size) + { + result = __LINE__; + } + else + { + result = 0; + } + } + } + + return result; +} + +void tlsio_wolfssl_dowork(CONCRETE_IO_HANDLE tls_io) +{ + if (tls_io != NULL) + { + 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_ERROR)) + { + decode_ssl_received_bytes(tls_io_instance); + xio_dowork(tls_io_instance->socket_io); + } + } +} + +const IO_INTERFACE_DESCRIPTION* tlsio_wolfssl_get_interface_description(void) +{ + return &tlsio_wolfssl_interface_description; +} + +int tlsio_wolfssl_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value) +{ + int result; + + if (tls_io == NULL || optionName == NULL) + { + result = __LINE__; + } + else + { + TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)tls_io; + + if (strcmp("TrustedCerts", optionName) == 0) + { + int res = wolfSSL_CTX_load_verify_buffer(tls_io_instance->ssl_context, (const unsigned char*)value, strlen(value) + 1, SSL_FILETYPE_PEM); + if (res != SSL_SUCCESS) + { + result = __LINE__; + } + else + { + result = 0; + } + } + else + { + result = xio_setoption(tls_io_instance->socket_io, optionName, value); + } + } + + return result; +} \ No newline at end of file