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
Show file tree
Hide file tree
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
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