Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implemented OpenSSL providers support #1528

Open
wants to merge 1 commit into
base: develop
Choose a base branch
from
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -17,7 +17,7 @@

cmake_minimum_required(VERSION 3.5)

project("Eclipse Paho C"
project("Eclipse Paho C"
VERSION 1.3.13
LANGUAGES C
)
@@ -29,7 +29,7 @@ set(CMAKE_SCRIPTS "${CMAKE_SOURCE_DIR}/cmake")
set(CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake/modules")

## Project Version
## Previously we read in the version from these files, but now we use the
## Previously we read in the version from these files, but now we use the
## CMake project setting. We just make sure the files and CMake match.
file(READ version.major PAHO_VERSION_MAJOR)
file(READ version.minor PAHO_VERSION_MINOR)
@@ -64,6 +64,7 @@ option(PAHO_ENABLE_TESTING "Build tests and run" TRUE)
option(PAHO_ENABLE_CPACK "Enable CPack" TRUE)
option(PAHO_HIGH_PERFORMANCE "Disable tracing and heap tracking" FALSE)
option(PAHO_USE_SELECT "Revert to select system call instead of poll" FALSE)
option(PAHO_SSL_PROVIDERS "Enable provider option for OpenSSL" FALSE)

if(PAHO_HIGH_PERFORMANCE)
add_definitions(-DHIGH_PERFORMANCE=1)
@@ -73,6 +74,10 @@ if(PAHO_USE_SELECT)
add_definitions(-DUSE_SELECT=1)
endif()

if(PAHO_SSL_PROVIDERS)
add_definitions(-DOPENSSL_PROVIDERS=1)
endif()

if(PAHO_WITH_LIBUUID)
add_definitions(-DUSE_LIBUUID=1)
endif()
18 changes: 17 additions & 1 deletion src/MQTTAsync.c
Original file line number Diff line number Diff line change
@@ -599,7 +599,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;
@@ -757,6 +757,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->providerName)
free((void*)m->c->sslopts->providerName);
}
free((void*)m->c->sslopts);
m->c->sslopts = NULL;
}
@@ -806,6 +811,17 @@ 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->providerName) {
# if OPENSSL_PROVIDERS
m->c->sslopts->providerName = MQTTStrdup(options->ssl->providerName);
# else // OPENSSL_PROVIDERS
rc = MQTTASYNC_SSL_NOT_SUPPORTED;
goto exit;
# endif // OPENSSL_PROVIDERS
}
}
}
#else
if (options->struct_version != 0 && options->ssl)
11 changes: 8 additions & 3 deletions src/MQTTAsync.h
Original file line number Diff line number Diff line change
@@ -491,7 +491,7 @@ typedef struct
#define MQTTAsync_connectData_initializer {{'M', 'Q', 'C', 'D'}, 0, NULL, {0, NULL}}

/**
* This is a callback function which will allow the client application to update the
* This is a callback function which will allow the client application to update the
* connection data.
* @param data The connection data which can be modified by the application.
* @return Return a non-zero value to update the connect data, zero to keep the same data.
@@ -1073,12 +1073,13 @@ typedef struct
/** The eyecatcher for this structure. Must be MQTS */
char struct_id[4];

/** The version number of this structure. Must be 0, 1, 2, 3, 4 or 5.
/** The version number of this structure. Must be [0-6].
* 0 means no sslVersion
* 1 means no verify, CApath
* 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 providerName
*/
int struct_version;

@@ -1177,9 +1178,13 @@ typedef struct
* Exists only if struct_version >= 5
*/
unsigned int protos_len;

/** OpenSSL provider to used, NULL if disabled. */
const char* providerName;

} 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, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0, NULL }

/** Utility structure where name/value pairs are needed */
typedef struct
20 changes: 18 additions & 2 deletions src/MQTTClient.c
Original file line number Diff line number Diff line change
@@ -1285,7 +1285,7 @@ static MQTTResponse MQTTClient_connectURIVersion(MQTTClient handle, MQTTClient_c
setSocketForSSLrc = SSLSocket_setSocketForSSL(&m->c->net, m->c->sslopts,
serverURI, hostname_len);

if (setSocketForSSLrc != MQTTCLIENT_SUCCESS)
if (1 == setSocketForSSLrc)
{
if (m->c->session != NULL)
if ((rc = SSL_set_session(m->c->net.ssl, m->c->session)) != 1)
@@ -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->providerName)
free((void*)m->c->sslopts->providerName);
}
free(m->c->sslopts);
m->c->sslopts = NULL;
}
@@ -1666,6 +1671,17 @@ 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->providerName) {
# if OPENSSL_PROVIDERS
m->c->sslopts->providerName = MQTTStrdup(options->ssl->providerName);
# else // OPENSSL_PROVIDERS
rc.reasonCode = MQTTCLIENT_SSL_NOT_SUPPORTED;
goto exit;
# endif // OPENSSL_PROVIDERS
}
}
}
#endif

@@ -1818,7 +1834,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;
9 changes: 7 additions & 2 deletions src/MQTTClient.h
Original file line number Diff line number Diff line change
@@ -675,12 +675,13 @@ typedef struct
/** The eyecatcher for this structure. Must be MQTS */
char struct_id[4];

/** The version number of this structure. Must be 0, 1, 2, 3, 4 or 5.
/** The version number of this structure. Must be [0-6].
* 0 means no sslVersion
* 1 means no verify, CApath
* 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 providerName
*/
int struct_version;

@@ -779,9 +780,13 @@ typedef struct
* Exists only if struct_version >= 5
*/
unsigned int protos_len;

/** OpenSSL provider to used, NULL if disabled. */
const char* providerName;

} 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'}, 5, NULL, NULL, NULL, NULL, NULL, 1, MQTT_SSL_VERSION_DEFAULT, 0, NULL, NULL, NULL, NULL, NULL, 0, NULL, 0 , NULL}

/**
* MQTTClient_libraryInfo is used to store details relating to the currently used
9 changes: 7 additions & 2 deletions src/MQTTProtocolClient.c
Original file line number Diff line number Diff line change
@@ -345,7 +345,7 @@ int MQTTProtocol_handlePublishes(void* pack, SOCKET sock)
if (publish->header.bits.qos == 1)
{
Protocol_processPublication(publish, client, 1);

if (socketHasPendingWrites)
rc = MQTTProtocol_queueAck(client, PUBACK, publish->msgId);
else
@@ -981,8 +981,13 @@ void MQTTProtocol_freeClient(Clients* client)
if (client->sslopts->protos)
free((void*)client->sslopts->protos);
}
if (client->sslopts->struct_version >= 6)
{
if(client->sslopts->providerName)
free((void*)client->sslopts->providerName);
}
free(client->sslopts);
client->sslopts = NULL;
client->sslopts = NULL;
}
#endif
/* don't free the client structure itself... this is done elsewhere */
120 changes: 111 additions & 9 deletions src/SSLSocket.c
Original file line number Diff line number Diff line change
@@ -44,6 +44,16 @@
#include <openssl/crypto.h>
#include <openssl/x509v3.h>

#if OPENSSL_PROVIDERS
# include <openssl/store.h>
# include <openssl/provider.h>

# if (OPENSSL_VERSION_NUMBER < 0x030000000lu)
# error "OpenSSL providers are only usabe with OpenSSL version 3.x.x."
# endif // (OPENSSL_VERSION_NUMBER < 0x030000000lu)

#endif // OPENSSL_PROVIDERS

extern Sockets mod_s;

static int SSLSocket_error(char* aString, SSL* ssl, SOCKET sock, int rc, int (*cb)(const char *str, size_t len, void *u), void* u);
@@ -619,16 +629,108 @@ int SSLSocket_createContext(networkHandles* net, MQTTClient_SSLOptions* opts)
SSL_CTX_set_default_passwd_cb_userdata(net->ctx, (void*)opts->privateKeyPassword);
}

#if OPENSSL_PROVIDERS
/* 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(opts->providerName && (opts->providerName[0] != '\0'))
{
// Load desired OpenSSL provider, e.g. tpm2.
OSSL_PROVIDER *provider = OSSL_PROVIDER_load(NULL, opts->providerName);
if(!provider)
{
rc = 0;
goto free_ctx;
}

// Perform providers Self-Test.
rc = OSSL_PROVIDER_self_test(provider);
if(rc != 1)
{
OSSL_PROVIDER_unload(provider);
goto free_ctx;
}

// Open STORE context with given providers handle.
OSSL_STORE_CTX *storeCtx = OSSL_STORE_open(opts->privateKey, NULL, NULL, NULL, NULL);
if (!storeCtx) {
OSSL_PROVIDER_unload(provider);
rc = 0;
goto free_ctx;
}

rc = OSSL_STORE_expect(storeCtx, OSSL_STORE_INFO_PKEY);
if (rc != 1) {
OSSL_STORE_close(storeCtx);
OSSL_PROVIDER_unload(provider);
rc = 0;
goto free_ctx;
}

EVP_PKEY *pkey = NULL;
while (pkey == NULL && !OSSL_STORE_eof(storeCtx)) {
// Load STORE context and get it's type.
OSSL_STORE_INFO *info = OSSL_STORE_load(storeCtx);

// This can happen when, for example, we're reaching stuff that's not there
// or there is a thing that we don't expect.
// If happens, then we just loop until EOF.
if (info == NULL) {
continue;
}

const int type = OSSL_STORE_INFO_get_type(info);
if (type == OSSL_STORE_INFO_PKEY) {
// If type is private key, fetch it.
pkey = OSSL_STORE_INFO_get1_PKEY(info);
} else {
break;
}

OSSL_STORE_INFO_free(info);
}

// If private key is invalid - exit.
if (!pkey) {
OSSL_STORE_close(storeCtx);
OSSL_PROVIDER_unload(provider);
rc = 0;
goto free_ctx;
}

// Load private key handle to ctx.
rc = SSL_CTX_use_PrivateKey(net->ctx, pkey);
if (rc != 1)
{
if (opts->struct_version >= 3)
SSLSocket_error("SSL_CTX_use_PrivateKey", NULL, net->socket, rc, opts->ssl_error_cb, opts->ssl_error_context);
else
SSLSocket_error("SSL_CTX_use_PrivateKey", NULL, net->socket, rc, NULL, NULL);
goto free_ctx;
}
}
else
#endif // OPENSSL_PROVIDERS
{
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;
}
}

// Check if private kay matches certificate.
rc = SSL_CTX_check_private_key(net->ctx);
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);
SSLSocket_error("SSL_CTX_check_private_key", 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);
SSLSocket_error("SSL_CTX_check_private_key", NULL, net->socket, rc, NULL, NULL);
goto free_ctx;
}
}
@@ -721,8 +823,9 @@ int SSLSocket_setSocketForSSL(networkHandles* net, MQTTClient_SSLOptions* opts,

SSL_CTX_set_info_callback(net->ctx, SSL_CTX_info_callback);
SSL_CTX_set_msg_callback(net->ctx, SSL_CTX_msg_callback);
if (opts->enableServerCertAuth)
if (opts->enableServerCertAuth) {
SSL_CTX_set_verify(net->ctx, SSL_VERIFY_PEER, NULL);
}

net->ssl = SSL_new(net->ctx);

@@ -773,8 +876,7 @@ int SSLSocket_connect(SSL* ssl, SOCKET sock, const char* hostname, int verify, i
rc = SSL_connect(ssl);
if (rc != 1)
{
int error;
error = SSLSocket_error("SSL_connect", ssl, sock, rc, cb, u);
int error = SSLSocket_error("SSL_connect", ssl, sock, rc, cb, u);
if (error == SSL_FATAL)
rc = error;
if (error == SSL_ERROR_WANT_READ || error == SSL_ERROR_WANT_WRITE)
@@ -1042,7 +1144,7 @@ int SSLSocket_putdatas(SSL* ssl, SOCKET socket, char* buf0, size_t buf0len, Pack
free(bufs.buffers[i]);
bufs.buffers[i] = NULL;
}
}
}
}
exit:
FUNC_EXIT_RC(rc);