Skip to content
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
2 changes: 1 addition & 1 deletion apps/mosquitto_ctrl/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -623,7 +623,7 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
}
#ifdef WITH_TLS
if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){
fprintf(stderr, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n");
fprintf(stderr, "Error: Problem setting key form, it must be one of 'pem', 'engine', or 'uri'.\n");
return 1;
}
if(cfg->cafile || cfg->capath){
Expand Down
9 changes: 5 additions & 4 deletions client/client_shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -1370,6 +1370,11 @@ static int client_tls_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
SSL_CTX_set_keylog_callback(cfg->ssl_ctx, tls_keylog_callback);
}

/* keyform must be known before calling mosquitto_tls_set() */
if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){
err_printf(cfg, "Error: Problem setting key form, it must be one of 'pem', 'engine' or 'uri'.\n");
return 1;
}
if(cfg->cafile || cfg->capath){
rc = mosquitto_tls_set(mosq, cfg->cafile, cfg->capath, cfg->certfile, cfg->keyfile, NULL);
if(rc){
Expand Down Expand Up @@ -1402,10 +1407,6 @@ static int client_tls_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
err_printf(cfg, "Error: Problem setting TLS engine, is %s a valid engine?\n", cfg->tls_engine);
return 1;
}
if(cfg->keyform && mosquitto_string_option(mosq, MOSQ_OPT_TLS_KEYFORM, cfg->keyform)){
err_printf(cfg, "Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n");
return 1;
}
if(cfg->tls_engine_kpass_sha1 && mosquitto_string_option(mosq, MOSQ_OPT_TLS_ENGINE_KPASS_SHA1, cfg->tls_engine_kpass_sha1)){
err_printf(cfg, "Error: Problem setting TLS engine key pass sha, is it a 40 character hex string?\n");
return 1;
Expand Down
2 changes: 1 addition & 1 deletion client/pub_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ static void print_usage(void)
printf(" communication.\n");
printf(" --cert : client certificate for authentication, if required by server.\n");
printf(" --key : client private key for authentication, if required by server.\n");
printf(" --keyform : keyfile type, can be either \"pem\" or \"engine\".\n");
printf(" --keyform : keyfile type, can be \"pem\", \"engine\", or \"uri\".\n");
printf(" --ciphers : openssl compatible list of TLS ciphers to support.\n");
printf(" --tls-version : TLS protocol version, can be one of tlsv1.3 or tlsv1.2.\n");
printf(" Defaults to tlsv1.2 if available.\n");
Expand Down
2 changes: 1 addition & 1 deletion client/sub_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -344,7 +344,7 @@ static void print_usage(void)
printf(" communication.\n");
printf(" --cert : client certificate for authentication, if required by server.\n");
printf(" --key : client private key for authentication, if required by server.\n");
printf(" --keyform : keyfile type, can be either \"pem\" or \"engine\".\n");
printf(" --keyform : keyfile type, can be \"pem\", \"engine\", or \"uri\".\n");
printf(" --ciphers : openssl compatible list of TLS ciphers to support.\n");
printf(" --tls-version : TLS protocol version, can be one of tlsv1.3 or tlsv1.2.\n");
printf(" Defaults to tlsv1.2 if available.\n");
Expand Down
5 changes: 4 additions & 1 deletion include/mosquitto/libmosquitto_options.h
Original file line number Diff line number Diff line change
Expand Up @@ -172,9 +172,12 @@ libmosq_EXPORT int mosquitto_int_option(struct mosquitto *mosq, enum mosq_opt_t
* MOSQ_OPT_TLS_KEYFORM - Configure the client to treat the keyfile
* differently depending on its type. Must be set
* before <mosquitto_connect>.
* Set as either "pem" or "engine", to determine from where the
* Set as "pem", "engine", or "uri" to determine from where the
* private key for a TLS connection will be obtained. Defaults to
* "pem", a normal private key file.
* "engine" allows private keys operations via an OpenSSL engine.
* "uri" is available with OpenSSL 3.0 or later, and allows keys
* to be referenced through OpenSSL provider URIs such as pkcs11: URIs.
*
* MOSQ_OPT_TLS_ENGINE_KPASS_SHA1 - Where the TLS Engine requires the use of
* a password to be accessed, this option allows a hex encoded
Expand Down
1 change: 1 addition & 0 deletions lib/mosquitto_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -222,6 +222,7 @@ struct mosquitto_message_all {
enum mosquitto__keyform {
mosq_k_pem = 0,
mosq_k_engine = 1,
mosq_k_uri = 2,
};
#endif

Expand Down
50 changes: 49 additions & 1 deletion lib/net_mosq.c
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,9 @@ SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#include <openssl/engine.h>
#include <openssl/err.h>
#include <openssl/ui.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
#include <openssl/store.h>
#endif
#include <tls_mosq.h>
#endif

Expand Down Expand Up @@ -681,7 +684,9 @@ static int net__init_ssl_ctx(struct mosquitto *mosq)
int ret;
#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000
ENGINE *engine = NULL;
EVP_PKEY *pkey;
#endif
#if (!defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000) || OPENSSL_VERSION_NUMBER >= 0x30000000L
EVP_PKEY *pkey = NULL; /* Holds the EVP_PKEY when engine (API level < 3) or provider (Version >= 3) is used */
#endif
uint8_t tls_alpn_wire[256];
uint8_t tls_alpn_len;
Expand Down Expand Up @@ -854,11 +859,54 @@ static int net__init_ssl_ctx(struct mosquitto *mosq)
return MOSQ_ERR_TLS;
}
if(SSL_CTX_use_PrivateKey(mosq->ssl_ctx, pkey) <= 0){
EVP_PKEY_free(pkey);
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to use engine private key file \"%s\".", mosq->tls_keyfile);
ENGINE_FINISH(engine);
net__print_ssl_error(mosq, "while trying to use the private key");
return MOSQ_ERR_TLS;
}
EVP_PKEY_free(pkey);
#endif
}else if(mosq->tls_keyform == mosq_k_uri){
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_STORE_CTX *store_ctx = NULL;
OSSL_STORE_INFO *info = NULL;

store_ctx = OSSL_STORE_open(mosq->tls_keyfile, NULL, NULL, NULL, NULL);
if(!store_ctx){
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to open key URI \"%s\".", mosq->tls_keyfile);
net__print_ssl_error(mosq, "while trying to open the key URI");
return MOSQ_ERR_TLS;
}

while(!OSSL_STORE_eof(store_ctx)){
info = OSSL_STORE_load(store_ctx);
if(info){
if(OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY){
pkey = OSSL_STORE_INFO_get1_PKEY(info);
OSSL_STORE_INFO_free(info);
break;
}
OSSL_STORE_INFO_free(info);
}else if(!OSSL_STORE_eof(store_ctx)){
log__printf(mosq, MOSQ_LOG_ERR, "Error: Failed to load object from URI \"%s\".", mosq->tls_keyfile);
net__print_ssl_error(mosq, "while trying to load from the key URI");
}
}
OSSL_STORE_close(store_ctx);

if(!pkey){
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load private key from URI \"%s\".", mosq->tls_keyfile);
net__print_ssl_error(mosq, "while trying to load the private key from URI");
return MOSQ_ERR_TLS;
}
if(SSL_CTX_use_PrivateKey(mosq->ssl_ctx, pkey) <= 0){
EVP_PKEY_free(pkey);
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to use private key from URI \"%s\".", mosq->tls_keyfile);
net__print_ssl_error(mosq, "while trying to use the private key from URI");
return MOSQ_ERR_TLS;
}
EVP_PKEY_free(pkey);
#endif
}else{
ret = SSL_CTX_use_PrivateKey_file(mosq->ssl_ctx, mosq->tls_keyfile, SSL_FILETYPE_PEM);
Expand Down
9 changes: 8 additions & 1 deletion lib/options.c
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,7 @@ int mosquitto_tls_set(struct mosquitto *mosq, const char *cafile, const char *ca

mosquitto_FREE(mosq->tls_keyfile);
if(keyfile){
/* For engine and URI keyforms, skip file validation as they may not be regular files */
if(mosq->tls_keyform == mosq_k_pem){
fptr = mosquitto_fopen(keyfile, "rt", false);
if(fptr){
Expand Down Expand Up @@ -342,14 +343,20 @@ int mosquitto_string_option(struct mosquitto *mosq, enum mosq_opt_t option, cons
break;

case MOSQ_OPT_TLS_KEYFORM:
#if defined(WITH_TLS) && !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000
#ifdef WITH_TLS
if(!value){
return MOSQ_ERR_INVAL;
}
if(!strcasecmp(value, "pem")){
mosq->tls_keyform = mosq_k_pem;
#if !defined(OPENSSL_NO_ENGINE) && OPENSSL_API_LEVEL < 30000
}else if(!strcasecmp(value, "engine")){
mosq->tls_keyform = mosq_k_engine;
#endif
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
}else if(!strcasecmp(value, "uri")){
mosq->tls_keyform = mosq_k_uri;
#endif
}else{
return MOSQ_ERR_INVAL;
}
Expand Down
7 changes: 6 additions & 1 deletion man/common/option-tls-keyform.xml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
<listitem>
<para>
Specifies the type of private key in use when making TLS
connections.. This can be "pem" or "engine". This parameter is
connections. This can be "pem", "engine", or "uri". This parameter is
useful when a TPM module is being used and the private key has been
created with it. Defaults to "pem", which means normal private key
files are used.
</para>
<para>
The "engine" option allows private keys operations via an OpenSSL engine.
The "uri" option is available with OpenSSL 3.0 or later, and allows
keys to be referenced through OpenSSL provider URIs such as pkcs11: URIs.
</para>
<para>
See also <option>--tls-engine</option>.
</para>
Expand Down
1 change: 1 addition & 0 deletions man/common/synopsis-tls-certificate-options.xml
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
<group choice='req'>
<arg choice='plain'><replaceable>pem</replaceable></arg>
<arg choice='plain'><replaceable>engine</replaceable></arg>
<arg choice='plain'><replaceable>uri</replaceable></arg>
</group></arg>
<arg><option>--tls-engine-kpass-sha1</option> <replaceable>kpass-sha1</replaceable></arg>
<sbr/>
Expand Down
12 changes: 9 additions & 3 deletions man/mosquitto.conf.5.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1882,6 +1882,8 @@ openssl dhparam -out dhparam.pem 2048</programlisting>
to enable certificate based TLS encryption. If
<option>tls_keyform</option> is "engine" this represents
the engine handle of the private key.
If <option>tls_keyform</option> is "uri" this represents
the OpenSSL provider URI for the key, e.g. pkcs11: URIs.
</para>
<para>
The private key pointed to by this option will be
Expand Down Expand Up @@ -1928,14 +1930,18 @@ openssl dhparam -out dhparam.pem 2048</programlisting>
</listitem>
</varlistentry>
<varlistentry>
<term><option>tls_keyform</option> [ pem | engine ]</term>
<term><option>tls_keyform</option> [ pem | engine | uri ]</term>
<listitem>
<para>Specifies the type of private key in use when
making TLS connections.. This can be "pem" or
"engine". This parameter is useful when a TPM
making TLS connections. This can be "pem", "engine",
or "uri". This parameter is useful when a TPM
module is being used and the private key has been
created with it. Defaults to "pem", which means
normal private key files are used.</para>
<para>The "engine" option is only available with OpenSSL &lt; 3.0.
The "uri" option is only available with OpenSSL &gt;= 3.0,
and allows the key to be obtained from URIs such as
pkcs11: URIs.</para>
</listitem>
</varlistentry>
<varlistentry>
Expand Down
2 changes: 2 additions & 0 deletions src/conf.c
Original file line number Diff line number Diff line change
Expand Up @@ -2691,6 +2691,8 @@ static int config__read_file_core(struct mosquitto__config *config, bool reload,
cur_listener->tls_keyform = mosq_k_pem;
if(!strcmp(keyform, "engine")){
cur_listener->tls_keyform = mosq_k_engine;
}else if(!strcmp(keyform, "uri")){
cur_listener->tls_keyform = mosq_k_uri;
}
mosquitto_FREE(keyform);
#else
Expand Down
54 changes: 53 additions & 1 deletion src/net.c
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,9 @@ SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
#ifdef WITH_TLS
# include "tls_mosq.h"
# include <openssl/err.h>
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
# include <openssl/store.h>
#endif
static int tls_ex_index_context = -1;
static int tls_ex_index_listener = -1;
#endif
Expand Down Expand Up @@ -563,13 +566,62 @@ int net__load_certificates(struct mosquitto__listener *listener)
net__print_ssl_error(NULL, NULL);
return MOSQ_ERR_TLS;
}
if(listener->tls_engine == NULL || listener->tls_keyform == mosq_k_pem){
if(listener->tls_keyform == mosq_k_pem){
rc = SSL_CTX_use_PrivateKey_file(listener->ssl_ctx, listener->keyfile, SSL_FILETYPE_PEM);
if(rc != 1){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load server key file \"%s\". Check keyfile.", listener->keyfile);
net__print_ssl_error(NULL, NULL);
return MOSQ_ERR_TLS;
}
}else if(listener->tls_keyform == mosq_k_uri){
#if OPENSSL_VERSION_NUMBER >= 0x30000000L
OSSL_STORE_CTX *store = NULL;
OSSL_STORE_INFO *info = NULL;
EVP_PKEY *pkey = NULL;

store = OSSL_STORE_open(listener->keyfile, NULL, NULL, NULL, NULL);
if(!store){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to open key URI \"%s\".", listener->keyfile);
net__print_ssl_error(NULL, NULL);
return MOSQ_ERR_TLS;
}

while(!OSSL_STORE_eof(store)){
info = OSSL_STORE_load(store);
if(info == NULL){
if(!OSSL_STORE_eof(store)){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load key from URI \"%s\".", listener->keyfile);
net__print_ssl_error(NULL, NULL);
}
continue;
}
if(OSSL_STORE_INFO_get_type(info) == OSSL_STORE_INFO_PKEY){
pkey = OSSL_STORE_INFO_get1_PKEY(info);
OSSL_STORE_INFO_free(info);
if(pkey){
break;
}
}
OSSL_STORE_INFO_free(info);
}
OSSL_STORE_close(store);

if(!pkey){
log__printf(NULL, MOSQ_LOG_ERR, "Error: No private key found in URI \"%s\".", listener->keyfile);
return MOSQ_ERR_TLS;
}

rc = SSL_CTX_use_PrivateKey(listener->ssl_ctx, pkey);
EVP_PKEY_free(pkey);
if(rc != 1){
log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to use private key from URI \"%s\".", listener->keyfile);
net__print_ssl_error(NULL, NULL);
return MOSQ_ERR_TLS;
}
#else
log__printf(NULL, MOSQ_LOG_ERR, "Error: 'uri' keyform requires OpenSSL 3.0 or later.");
return MOSQ_ERR_NOT_SUPPORTED;
#endif
}
rc = SSL_CTX_check_private_key(listener->ssl_ctx);
if(rc != 1){
Expand Down
2 changes: 1 addition & 1 deletion test/apps/ctrl/ctrl-args.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def do_test(args, rc_expected, response=None):
do_test(["--keyform"], 1, response="Error: --keyform argument given but no keyform specified.\n\n")
do_test(["--keyform", "key"], 1, response="Error: If keyform is set, keyfile must be also specified.\n")
do_test(["--keyform", "key", "--cafile", "file", "--cert", "file", "--key", "file", "broker", "listListeners"], 1,
response="Error: Problem setting key form, it must be one of 'pem' or 'engine'.\n")
response="Error: Problem setting key form, it must be one of 'pem', 'engine', or 'uri'.\n")
do_test(['-L'], 1, response="Error: -L argument given but no URL specified.\n\n")
do_test(['-L', 'invalid://'], 1, response="Error: Unsupported URL scheme.\n\n")
do_test(['-L', 'mqtt://localhost'], 1, response="Error: Invalid URL for -L argument specified - topic missing.\n")
Expand Down
Loading