Skip to content

Commit

Permalink
Implemented OpenSSL providers support
Browse files Browse the repository at this point in the history
Signed-off-by: Tomasz Maczkowski <[email protected]>
  • Loading branch information
tomaszma1 committed Sep 30, 2024
1 parent 338ebca commit 3481a72
Show file tree
Hide file tree
Showing 7 changed files with 175 additions and 21 deletions.
9 changes: 7 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

cmake_minimum_required(VERSION 3.5)

project("Eclipse Paho C"
project("Eclipse Paho C"
VERSION 1.3.13
LANGUAGES C
)
Expand All @@ -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)
Expand Down Expand Up @@ -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)
Expand All @@ -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()
Expand Down
18 changes: 17 additions & 1 deletion src/MQTTAsync.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
Expand Down
11 changes: 8 additions & 3 deletions src/MQTTAsync.h
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
20 changes: 18 additions & 2 deletions src/MQTTClient.c
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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

Expand Down Expand Up @@ -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;
Expand Down
9 changes: 7 additions & 2 deletions src/MQTTClient.h
Original file line number Diff line number Diff line change
Expand Up @@ -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;

Expand Down Expand Up @@ -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
Expand Down
9 changes: 7 additions & 2 deletions src/MQTTProtocolClient.c
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 */
Expand Down
120 changes: 111 additions & 9 deletions src/SSLSocket.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
}
}
Expand Down Expand Up @@ -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);

Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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);
Expand Down

0 comments on commit 3481a72

Please sign in to comment.