Azure IoT common library

Fork of azure_c_shared_utility by Azure IoT

Revision:
15:956c6d205aa7
Parent:
10:1be0bc9a9deb
Child:
19:2e0811512ceb
--- a/tlsio_wolfssl.c	Fri Oct 21 22:11:02 2016 +0000
+++ b/tlsio_wolfssl.c	Wed Nov 16 21:38:39 2016 -0800
@@ -16,6 +16,8 @@
 #include "azure_c_shared_utility/socketio.h"
 #include "azure_c_shared_utility/crt_abstractions.h"
 #include "azure_c_shared_utility/xlogging.h"
+#include "azure_c_shared_utility/shared_util_options.h"
+
 
 typedef enum TLSIO_STATE_ENUM_TAG
 {
@@ -46,6 +48,8 @@
     ON_SEND_COMPLETE on_send_complete;
     void* on_send_complete_callback_context;
     char* certificate;
+    char* x509certificate;
+    char* x509privatekey;
     char* hostname;
     int port;
 } TLS_IO_INSTANCE;
@@ -54,10 +58,9 @@
 static void* tlsio_wolfssl_CloneOption(const char* name, const void* value)
 {
     void* result;
-    if (
-        (name == NULL) || (value == NULL)
-        )
+    if ((name == NULL) || (value == NULL))
     {
+        LogError("invalid parameter detected: const char* name=%p, const void* value=%p", name, value);
         result = NULL;
     }
     else
@@ -66,6 +69,31 @@
         {
             if (mallocAndStrcpy_s((char**)&result, value) != 0)
             {
+                LogError("unable to mallocAndStrcpy_s TrustedCerts value");
+                result = NULL;
+            }
+            else
+            {
+                /*return as is*/
+            }
+        }
+        else if (strcmp(name, SU_OPTION_X509_CERT) == 0)
+        {
+            if (mallocAndStrcpy_s((char**)&result, 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)
+        {
+            if (mallocAndStrcpy_s((char**)&result, value) != 0)
+            {
+                LogError("unable to mallocAndStrcpy_s x509privatekey value");
                 result = NULL;
             }
             else
@@ -75,7 +103,7 @@
         }
         else
         {
-            /*option is not handled*/
+            LogError("not handled option : %s", name);
             result = NULL;
         }
     }
@@ -86,15 +114,21 @@
 static void tlsio_wolfssl_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))
+    if ((name == NULL) || (value == NULL))
     {
-        if (strcmp(name, "TrustedCerts") == 0)
+        LogError("invalid parameter detected: const char* name=%p, const void* value=%p", name, value);
+    }
+    else
+    {
+        if ((strcmp(name, "TrustedCerts") == 0) ||
+            (strcmp(name, SU_OPTION_X509_CERT) == 0) ||
+            (strcmp(name, SU_OPTION_X509_PRIVATE_KEY) == 0))
         {
             free((void*)value);
         }
         else
         {
-            /*option is not handled*/
+            LogError("not handled option : %s", name);
         }
     }
 }
@@ -212,21 +246,21 @@
 
     switch (tls_io_instance->tlsio_state)
     {
-        default:
-        case TLSIO_STATE_NOT_OPEN:
-        case TLSIO_STATE_ERROR:
-            break;
+    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_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;
+    case TLSIO_STATE_OPEN:
+        tls_io_instance->tlsio_state = TLSIO_STATE_ERROR;
+        indicate_error(tls_io_instance);
+        break;
     }
 }
 
@@ -250,6 +284,7 @@
     TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
     unsigned char* new_socket_io_read_bytes;
 
+    (void)ssl;
     while (tls_io_instance->socket_io_read_byte_count == 0)
     {
         xio_dowork(tls_io_instance->socket_io);
@@ -302,6 +337,7 @@
     int result;
     TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
 
+    (void)ssl;
     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;
@@ -319,6 +355,7 @@
 static int on_handshake_done(WOLFSSL* ssl, void* context)
 {
     TLS_IO_INSTANCE* tls_io_instance = (TLS_IO_INSTANCE*)context;
+    (void)ssl;
     if (tls_io_instance->tlsio_state == TLSIO_STATE_IN_HANDSHAKE)
     {
         tls_io_instance->tlsio_state = TLSIO_STATE_OPEN;
@@ -333,7 +370,7 @@
     int result;
     if (tls_io_instance->certificate != NULL)
     {
-        int res = wolfSSL_CTX_load_verify_buffer(tls_io_instance->ssl_context, (const unsigned char*)tls_io_instance->certificate, strlen(tls_io_instance->certificate)+1, SSL_FILETYPE_PEM);
+        int res = wolfSSL_CTX_load_verify_buffer(tls_io_instance->ssl_context, (const unsigned char*)tls_io_instance->certificate, strlen(tls_io_instance->certificate) + 1, SSL_FILETYPE_PEM);
         if (res != SSL_SUCCESS)
         {
             result = __LINE__;
@@ -350,6 +387,38 @@
     return result;
 }
 
+static int x509_wolfssl_add_credentials(WOLFSSL* ssl, char* x509certificate, char* x509privatekey) {
+
+    int result;
+
+    if (wolfSSL_use_certificate_buffer(ssl, (unsigned char*)x509certificate, strlen(x509certificate) + 1, SSL_FILETYPE_PEM) != SSL_SUCCESS)
+    {
+        LogError("unable to load x509 client certificate");
+        result = __LINE__;
+    }
+    else if (wolfSSL_use_PrivateKey_buffer(ssl, (unsigned char*)x509privatekey, strlen(x509privatekey) + 1, SSL_FILETYPE_PEM) != SSL_SUCCESS)
+    {
+        LogError("unable to load x509 client private key");
+        result = __LINE__;
+    }
+#ifdef HAVE_SECURE_RENEGOTIATION
+    else if (wolfSSL_UseSecureRenegotiation(ssl) != SSL_SUCCESS) {
+        LogError("unable to enable secure renegotiation");
+        result = __LINE__;
+    }
+#endif 
+    else
+    {
+        result = 0;
+    }
+    return result;
+}
+
+static void destroy_wolfssl_instance(TLS_IO_INSTANCE* tls_io_instance)
+{
+    wolfSSL_free(tls_io_instance->ssl);
+}
+
 static int create_wolfssl_instance(TLS_IO_INSTANCE* tls_io_instance)
 {
     int result;
@@ -367,6 +436,19 @@
             wolfSSL_CTX_free(tls_io_instance->ssl_context);
             result = __LINE__;
         }
+        /*x509 authentication can only be build before underlying connection is realized*/
+        else if ((tls_io_instance->x509certificate != NULL) &&
+            (tls_io_instance->x509privatekey != NULL) &&
+            (x509_wolfssl_add_credentials(tls_io_instance->ssl, tls_io_instance->x509certificate, tls_io_instance->x509privatekey) != 0))
+        {
+            destroy_wolfssl_instance(tls_io_instance);
+            tls_io_instance->ssl = NULL;
+            wolfSSL_CTX_free(tls_io_instance->ssl_context);
+            tls_io_instance->ssl_context = NULL;
+            LogError("unable to use x509 authentication");
+            result = __LINE__;
+        }
+
         else
         {
             tls_io_instance->socket_io_read_bytes = NULL;
@@ -388,11 +470,6 @@
     return result;
 }
 
-static void destroy_wolfssl_instance(TLS_IO_INSTANCE* tls_io_instance)
-{
-    wolfSSL_free(tls_io_instance->ssl);
-}
-
 int tlsio_wolfssl_init(void)
 {
     (void)wolfSSL_library_init();
@@ -430,6 +507,8 @@
             result->ssl = NULL;
             result->ssl_context = NULL;
             result->certificate = NULL;
+            result->x509certificate = NULL;
+            result->x509privatekey = NULL;
 
             result->on_bytes_received = NULL;
             result->on_bytes_received_context = NULL;
@@ -500,6 +579,16 @@
             free(tls_io_instance->certificate);
             tls_io_instance->certificate = 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;
+        }
         wolfSSL_CTX_free(tls_io_instance->ssl_context);
         xio_destroy(tls_io_instance->socket_io);
         free(tls_io);
@@ -549,26 +638,14 @@
             else
             {
                 // The state can get changed in the on_underlying_io_open_complete
-                if (tls_io_instance->tlsio_state == TLSIO_STATE_ERROR)
+                if (tls_io_instance->tlsio_state != TLSIO_STATE_OPEN)
                 {
                     LogError("Failed to connect to server.  The certificates may not be correct.");
                     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)
-                    {
-                        LogError("Failed to connect to server.");
-                        tls_io_instance->tlsio_state = TLSIO_STATE_NOT_OPEN;
-                        result = __LINE__;
-                    }
-                    else
-                    {
-                        result = 0;
-                    }
+                    result = 0;
                 }
             }
         }
@@ -637,7 +714,7 @@
             tls_io_instance->on_send_complete_callback_context = callback_context;
 
             int res = wolfSSL_write(tls_io_instance->ssl, buffer, size);
-            if (res != size)
+            if ((res < 0) || ((size_t)res != size)) // Best way I can think of to safely compare an int to a size_t
             {
                 result = __LINE__;
             }
@@ -671,6 +748,26 @@
     return &tlsio_wolfssl_interface_description;
 }
 
+static int process_option(char** destination, const char* name, const char* value)
+{
+    int result;
+    if (*destination != NULL)
+    {
+        free(*destination);
+        *destination = NULL;
+    }
+    if (mallocAndStrcpy_s(destination, value) != 0)
+    {
+        LogError("unable to process option %s",name);
+        result = __LINE__;
+    }
+    else
+    {
+        result = 0;
+    }
+    return result;
+
+}
 int tlsio_wolfssl_setoption(CONCRETE_IO_HANDLE tls_io, const char* optionName, const void* value)
 {
     int result;
@@ -685,34 +782,27 @@
 
         if (strcmp("TrustedCerts", optionName) == 0)
         {
-            const char* cert = (const char*)value;
-            if (tls_io_instance->certificate != NULL)
-            {
-                // Free the memory if it has been previously allocated
-                free(tls_io_instance->certificate);
-            }
-
-            // Store the certificate
-            size_t len = strlen(cert);
-            tls_io_instance->certificate = (const char*)malloc(len+1);
-            if (tls_io_instance->certificate == NULL)
+            result  = process_option(&tls_io_instance->certificate, optionName, value);
+        }
+        else if (strcmp(SU_OPTION_X509_CERT, optionName) == 0)
+        {
+            result = process_option(&tls_io_instance->x509certificate, optionName, value);
+        }
+        else if (strcmp(SU_OPTION_X509_PRIVATE_KEY, optionName) == 0)
+        {
+            result = process_option(&tls_io_instance->x509privatekey, optionName, value);
+        }
+        else
+        {
+            if (tls_io_instance->socket_io == NULL)
             {
                 result = __LINE__;
             }
             else
             {
-                (void)strcpy(tls_io_instance->certificate, cert);
-                result = 0;
+                result = xio_setoption(tls_io_instance->socket_io, optionName, value);
             }
         }
-        else if (tls_io_instance->socket_io == NULL)
-        {
-            result = __LINE__;
-        }
-        else
-        {
-            result = xio_setoption(tls_io_instance->socket_io, optionName, value);
-        }
     }
 
     return result;