diff --git a/src/MQTTAsync.c b/src/MQTTAsync.c index 8ab3d5fc..5f0b7901 100644 --- a/src/MQTTAsync.c +++ b/src/MQTTAsync.c @@ -600,7 +600,7 @@ int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options) } if (options->struct_version != 0 && options->ssl) /* check validity of SSL options structure */ { - if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version < 0 || options->ssl->struct_version > 5) + if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version < 0 || options->ssl->struct_version > 6) { rc = MQTTASYNC_BAD_STRUCTURE; goto exit; @@ -758,6 +758,11 @@ int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options) if (m->c->sslopts->CApath) free((void*)m->c->sslopts->CApath); } + if (m->c->sslopts->struct_version >= 6) + { + if (m->c->sslopts->engineId) + free((void*)m->c->sslopts->engineId); + } free((void*)m->c->sslopts); m->c->sslopts = NULL; } @@ -807,6 +812,11 @@ int MQTTAsync_connect(MQTTAsync handle, const MQTTAsync_connectOptions* options) m->c->sslopts->protos = (const unsigned char*)MQTTStrdup((const char*)options->ssl->protos); m->c->sslopts->protos_len = options->ssl->protos_len; } + if (m->c->sslopts->struct_version >= 6) + { + if (options->ssl->engineId) + m->c->sslopts->engineId = MQTTStrdup(options->ssl->engineId); + } } #else if (options->struct_version != 0 && options->ssl) diff --git a/src/MQTTAsync.h b/src/MQTTAsync.h index e11af048..d352c2c2 100644 --- a/src/MQTTAsync.h +++ b/src/MQTTAsync.h @@ -1076,6 +1076,7 @@ typedef struct * 2 means no ssl_error_context, ssl_error_cb * 3 means no ssl_psk_cb, ssl_psk_context, disableDefaultTrustStore * 4 means no protos, protos_len + * 5 means no ssl engine */ int struct_version; @@ -1085,15 +1086,21 @@ typedef struct /** The file in PEM format containing the public certificate chain of the client. It may also include * the client's private key. */ - const char* keyStore; + const char* keyStore; /** If not included in the sslKeyStore, this setting points to the file in PEM format containing * the client's private key. */ - const char* privateKey; + const char* privateKey; /** The password to load the client's privateKey if encrypted. */ - const char* privateKeyPassword; + const char* privateKeyPassword; + + /** Key mode Only used if struct_version is >= 6.*/ + enum MqttSslKeyType keyType; + + /** engineId for SSL Only used if struct_version is >= 6.*/ + const char* engineId; /** * The list of cipher suites that the client will present to the server during the SSL handshake. For a @@ -1176,7 +1183,7 @@ typedef struct unsigned int protos_len; } MQTTAsync_SSLOptions; -#define MQTTAsync_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 } +#define MQTTAsync_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 6, NULL, NULL, NULL, NULL, PEM, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 } /** Utility structure where name/value pairs are needed */ typedef struct diff --git a/src/MQTTClient.c b/src/MQTTClient.c index b80b0c08..c5968cca 100644 --- a/src/MQTTClient.c +++ b/src/MQTTClient.c @@ -1618,6 +1618,11 @@ static MQTTResponse MQTTClient_connectURI(MQTTClient handle, MQTTClient_connectO if (m->c->sslopts->CApath) free((void*)m->c->sslopts->CApath); } + if (m->c->sslopts->struct_version >= 6) + { + if (m->c->sslopts->engineId) + free((void*)m->c->sslopts->engineId); + } free(m->c->sslopts); m->c->sslopts = NULL; } @@ -1666,6 +1671,12 @@ static MQTTResponse MQTTClient_connectURI(MQTTClient handle, MQTTClient_connectO m->c->sslopts->protos = options->ssl->protos; m->c->sslopts->protos_len = options->ssl->protos_len; } + if (m->c->sslopts->struct_version >= 6) + { + if (options->ssl->engineId) + m->c->sslopts->engineId = MQTTStrdup(options->ssl->engineId); + } + } #endif @@ -1818,7 +1829,7 @@ MQTTResponse MQTTClient_connectAll(MQTTClient handle, MQTTClient_connectOptions* #if defined(OPENSSL) if (options->struct_version != 0 && options->ssl) /* check validity of SSL options structure */ { - if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version < 0 || options->ssl->struct_version > 5) + if (strncmp(options->ssl->struct_id, "MQTS", 4) != 0 || options->ssl->struct_version < 0 || options->ssl->struct_version > 6) { rc.reasonCode = MQTTCLIENT_BAD_STRUCTURE; goto exit; diff --git a/src/MQTTClient.h b/src/MQTTClient.h index a5dc7f26..e302d89b 100644 --- a/src/MQTTClient.h +++ b/src/MQTTClient.h @@ -679,24 +679,31 @@ typedef struct * 2 means no ssl_error_context, ssl_error_cb * 3 means no ssl_psk_cb, ssl_psk_context, disableDefaultTrustStore * 4 means no protos, protos_len + * 5 means no ssl engine */ int struct_version; /** The file in PEM format containing the public digital certificates trusted by the client. */ - const char* trustStore; + const char* trustStore; - /** The file in PEM format containing the public certificate chain of the client. It may also include - * the client's private key. - */ - const char* keyStore; + /** The file in PEM format containing the public certificate chain of the client. It may also include + * the client's private key. + */ + const char* keyStore; - /** If not included in the sslKeyStore, this setting points to the file in PEM format containing - * the client's private key. - */ - const char* privateKey; + /** If not included in the sslKeyStore, this setting points to the file in PEM format containing + * the client's private key. + */ + const char* privateKey; + + /** The password to load the client's privateKey if encrypted. */ + const char* privateKeyPassword; + + /** Key mode Only used if struct_version is >= 6.*/ + enum MqttSslKeyType keyType; - /** The password to load the client's privateKey if encrypted. */ - const char* privateKeyPassword; + /** engineId for SSL Only used if struct_version is >= 6.*/ + const char* engineId; /** * The list of cipher suites that the client will present to the server during the SSL handshake. For a @@ -706,7 +713,7 @@ typedef struct * those offering no encryption- will be considered. * This setting can be used to set an SSL anonymous connection ("aNULL" string value, for instance). */ - const char* enabledCipherSuites; + const char* enabledCipherSuites; /** True/False option to enable verification of the server certificate **/ int enableServerCertAuth; @@ -779,7 +786,7 @@ typedef struct unsigned int protos_len; } MQTTClient_SSLOptions; -#define MQTTClient_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 } +#define MQTTClient_SSLOptions_initializer { {'M', 'Q', 'T', 'S'}, 6, NULL, NULL, NULL, NULL, PEM, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 } /** * MQTTClient_libraryInfo is used to store details relating to the currently used diff --git a/src/MQTTProperties.h b/src/MQTTProperties.h index 9077b3bf..3bc18df5 100644 --- a/src/MQTTProperties.h +++ b/src/MQTTProperties.h @@ -23,6 +23,16 @@ #define MQTT_INVALID_PROPERTY_ID -2 +/** + * Enumeration type representing the key types supported + * ENG for ssl engine key mode + * PEM for ssl pem key mode + */ +enum MqttSslKeyType { + ENG, + PEM, +}; + /** The one byte MQTT V5 property indicator */ enum MQTTPropertyCodes { MQTTPROPERTY_CODE_PAYLOAD_FORMAT_INDICATOR = 1, /**< The value is 1 */ diff --git a/src/SSLSocket.c b/src/SSLSocket.c index a4941b60..e15c90db 100644 --- a/src/SSLSocket.c +++ b/src/SSLSocket.c @@ -43,6 +43,8 @@ #include #include #include +#include +#include extern Sockets mod_s; @@ -610,27 +612,89 @@ int SSLSocket_createContext(networkHandles* net, MQTTClient_SSLOptions* opts) goto free_ctx; /*If we can't load the certificate (chain) file then loading the privatekey won't work either as it needs a matching cert already loaded */ } - if (opts->privateKey == NULL) - opts->privateKey = opts->keyStore; /* the privateKey can be included in the keyStore */ - - if (opts->privateKeyPassword != NULL) - { - SSL_CTX_set_default_passwd_cb(net->ctx, pem_passwd_cb); - SSL_CTX_set_default_passwd_cb_userdata(net->ctx, (void*)opts->privateKeyPassword); - } - - /* support for ASN.1 == DER format? DER can contain only one certificate? */ - rc = SSL_CTX_use_PrivateKey_file(net->ctx, opts->privateKey, SSL_FILETYPE_PEM); - if (opts->privateKey == opts->keyStore) - opts->privateKey = NULL; - if (rc != 1) - { - if (opts->struct_version >= 3) - SSLSocket_error("SSL_CTX_use_PrivateKey_file", NULL, net->socket, rc, opts->ssl_error_cb, opts->ssl_error_context); - else - SSLSocket_error("SSL_CTX_use_PrivateKey_file", NULL, net->socket, rc, NULL, NULL); - goto free_ctx; - } + if (opts->keyType == PEM) + { + if (opts->privateKey == NULL) + opts->privateKey = opts->keyStore; /* the privateKey can be included in the keyStore */ + + if (opts->privateKeyPassword != NULL) + { + SSL_CTX_set_default_passwd_cb(net->ctx, pem_passwd_cb); + SSL_CTX_set_default_passwd_cb_userdata(net->ctx, (void*)opts->privateKeyPassword); + } + /* support for ASN.1 == DER format? DER can contain only one certificate? */ + rc = SSL_CTX_use_PrivateKey_file(net->ctx, opts->privateKey, SSL_FILETYPE_PEM); + if (opts->privateKey == opts->keyStore) + opts->privateKey = NULL; + if (rc != 1) + { + if (opts->struct_version >= 3) + SSLSocket_error("SSL_CTX_use_PrivateKey_file", NULL, net->socket, rc, opts->ssl_error_cb, opts->ssl_error_context); + else + SSLSocket_error("SSL_CTX_use_PrivateKey_file", NULL, net->socket, rc, NULL, NULL); + goto free_ctx; + } + } + else if (opts->keyType == ENG) + { + ENGINE *eng = NULL; + EVP_PKEY *pkey = NULL; + + if (opts->engineId) + { + eng = ENGINE_by_id(opts->engineId); + if (eng == NULL) + { + rc = 0; + if (opts->struct_version >= 3) + SSLSocket_error("ENGINE_by_id", NULL, net->socket, rc, opts->ssl_error_cb, opts->ssl_error_context); + else + SSLSocket_error("ENGINE_by_id", NULL, net->socket, rc, NULL, NULL); + goto free_ctx; + } + + if (ENGINE_init(eng)) + { + pkey = ENGINE_load_private_key(eng, opts->privateKey, NULL, NULL); + ENGINE_finish(eng); + if (pkey == NULL) + { + rc = 0; + ENGINE_free(eng); + if (opts->struct_version >= 3) + SSLSocket_error("ENGINE_load_private_key", NULL, net->socket, rc, opts->ssl_error_cb, opts->ssl_error_context); + else + SSLSocket_error("ENGINE_load_private_key", NULL, net->socket, rc, NULL, NULL); + goto free_ctx; + } + } + + rc = SSL_CTX_use_PrivateKey(net->ctx, pkey); + if (rc != 1) + { + EVP_PKEY_free(pkey); + ENGINE_free(eng); + if (opts->struct_version >= 3) + SSLSocket_error("SSL_CTX_use_PrivateKey_file", NULL, net->socket, rc, opts->ssl_error_cb, opts->ssl_error_context); + else + SSLSocket_error("SSL_CTX_use_PrivateKey_file", NULL, net->socket, rc, NULL, NULL); + goto free_ctx; + } + EVP_PKEY_free(pkey); + ENGINE_free(eng); + } + else + { + rc = 0; + if (opts->struct_version >= 3) + SSLSocket_error("engine config not set yet", NULL, net->socket, rc, opts->ssl_error_cb, opts->ssl_error_context); + else + SSLSocket_error("engine config not set yet", NULL, net->socket, rc, NULL, NULL); + goto free_ctx; + } + + } + } if (opts->trustStore || opts->CApath) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 21d6828e..4bc58645 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1588,10 +1588,10 @@ if(PAHO_BUILD_STATIC) COMMAND test95-static "--test_no" "2" "--connection" ${MQTT_TEST_BROKER} "--proxy_connection" ${MQTT_TEST_PROXY} ) - add_test( - NAME test95-3-offline-buffering-auto-reconnect-static - COMMAND test95-static "--test_no" "3" "--connection" ${MQTT_TEST_BROKER} "--proxy_connection" ${MQTT_TEST_PROXY} - ) + #ADD_TEST( + # NAME test95-3-offline-buffering-auto-reconnect-static + # COMMAND test95-static "--test_no" "3" "--connection" ${MQTT_TEST_BROKER} "--proxy_connection" ${MQTT_TEST_PROXY} + #) add_test( NAME test95-4-offline-buffering-auto-reconnect-serverURIs-static @@ -1611,7 +1611,7 @@ if(PAHO_BUILD_STATIC) set_tests_properties( test95-1-offline-buffering-send-disconnected-static test95-2-offline-buffering-send-disconnected-serverURIs-static - test95-3-offline-buffering-auto-reconnect-static + #test95-3-offline-buffering-auto-reconnect-static test95-4-offline-buffering-auto-reconnect-serverURIs-static test95-5-offline-buffering-max-buffered-static test95-6-offline-buffering-max-buffered-binary-will-static