Skip to content

Commit

Permalink
Add TLS engine and keyform support to libmosquitto
Browse files Browse the repository at this point in the history
- Clients can now offload crypto tasks to an external crypto device through
  the OpenSSL ENGINE API.
- The keyfiles can now be treated as PEM or ENGINE keys.
- Two new functions were added to libmosquitto to set up the previously
  mentioned features.
- Both mosquitto_sub and mosquitto_pub include support to turn on the mentioned
  features through command line options.

Signed-off-by: Nicolás Pernas Maradei <[email protected]>
  • Loading branch information
nicopernas authored and ralight committed Feb 26, 2019
1 parent 098a1c8 commit f88cc06
Show file tree
Hide file tree
Showing 15 changed files with 281 additions and 11 deletions.
32 changes: 32 additions & 0 deletions client/client_shared.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,8 @@ void client_config_cleanup(struct mosq_config *cfg)
free(cfg->keyfile);
free(cfg->ciphers);
free(cfg->tls_version);
free(cfg->tls_engine);
free(cfg->keyform);
# ifdef WITH_TLS_PSK
free(cfg->psk);
free(cfg->psk_identity);
Expand Down Expand Up @@ -308,6 +310,10 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char *
fprintf(stderr, "Error: Both certfile and keyfile must be provided if one of them is.\n");
return 1;
}
if((cfg->keyform && !cfg->keyfile)){
fprintf(stderr, "Error: keyfile must be specified if keyform is.\n");
return 1;
}
#endif
#ifdef WITH_TLS_PSK
if((cfg->cafile || cfg->capath) && cfg->psk){
Expand Down Expand Up @@ -425,6 +431,14 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c
cfg->ciphers = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--tls-engine")){
if(i==argc-1){
fprintf(stderr, "Error: --tls-engine argument given but no engine_id specified.\n\n");
return 1;
}else{
cfg->tls_engine = strdup(argv[i+1]);
}
i++;
#endif
}else if(!strcmp(argv[i], "-C")){
if(pub_or_sub == CLIENT_PUB){
Expand Down Expand Up @@ -561,6 +575,14 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c
cfg->keyfile = strdup(argv[i+1]);
}
i++;
}else if(!strcmp(argv[i], "--keyform")){
if(i==argc-1){
fprintf(stderr, "Error: --keyform argument given but no keyform specified.\n\n");
return 1;
}else{
cfg->keyform = strdup(argv[i+1]);
}
i++;
#endif
}else if(!strcmp(argv[i], "-L") || !strcmp(argv[i], "--url")){
if(i==argc-1){
Expand Down Expand Up @@ -917,6 +939,16 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg)
mosquitto_lib_cleanup();
return 1;
}
if(cfg->tls_engine && mosquitto_tls_engine_set(mosq, cfg->tls_engine)){
if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS engine.\n");
mosquitto_lib_cleanup();
return 1;
}
if(cfg->keyform && mosquitto_tls_keyform_set(mosq, cfg->keyform)){
if(!cfg->quiet) fprintf(stderr, "Error: Problem setting keyform.\n");
mosquitto_lib_cleanup();
return 1;
}
# ifdef WITH_TLS_PSK
if(cfg->psk && mosquitto_tls_psk_set(mosq, cfg->psk, cfg->psk_identity, NULL)){
if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS-PSK options.\n");
Expand Down
2 changes: 2 additions & 0 deletions client/client_shared.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,8 @@ struct mosq_config {
char *ciphers;
bool insecure;
char *tls_version;
char *tls_engine;
char *keyform;
# ifdef WITH_TLS_PSK
char *psk;
char *psk_identity;
Expand Down
5 changes: 4 additions & 1 deletion client/pub_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,8 @@ void print_usage(void)
printf(" [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\n");
#ifdef WITH_TLS
printf(" [{--cafile file | --capath dir} [--cert file] [--key file]\n");
printf(" [--ciphers ciphers] [--insecure]]\n");
printf(" [--ciphers ciphers] [--insecure] [--tls-engine engine]\n");
printf(" [--keyform keyform]]\n");
#ifdef WITH_TLS_PSK
printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n");
#endif
Expand Down Expand Up @@ -273,13 +274,15 @@ 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 one of pem or engine.\n");
printf(" --ciphers : openssl compatible list of TLS ciphers to support.\n");
printf(" --tls-version : TLS protocol version, can be one of tlsv1.2 tlsv1.1 or tlsv1.\n");
printf(" Defaults to tlsv1.2 if available.\n");
printf(" --insecure : do not check that the server certificate hostname matches the remote\n");
printf(" hostname. Using this option means that you cannot be sure that the\n");
printf(" remote host is the server you wish to connect to and so is insecure.\n");
printf(" Do not use this option in a production environment.\n");
printf(" --tls-engine : toggles the usage of a SSL engine device.\n");
# ifdef WITH_TLS_PSK
printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n");
printf(" --psk-identity : client identity string for TLS-PSK mode.\n");
Expand Down
5 changes: 4 additions & 1 deletion client/sub_client.c
Original file line number Diff line number Diff line change
Expand Up @@ -157,7 +157,8 @@ void print_usage(void)
printf(" [--will-topic [--will-payload payload] [--will-qos qos] [--will-retain]]\n");
#ifdef WITH_TLS
printf(" [{--cafile file | --capath dir} [--cert file] [--key file]\n");
printf(" [--ciphers ciphers] [--insecure]]\n");
printf(" [--ciphers ciphers] [--insecure] [--tls-engine engine]\n");
printf(" [--keyform keyform]]\n");
#ifdef WITH_TLS_PSK
printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n");
#endif
Expand Down Expand Up @@ -215,13 +216,15 @@ 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 one of pem or engine.\n");
printf(" --ciphers : openssl compatible list of TLS ciphers to support.\n");
printf(" --tls-version : TLS protocol version, can be one of tlsv1.2 tlsv1.1 or tlsv1.\n");
printf(" Defaults to tlsv1.2 if available.\n");
printf(" --insecure : do not check that the server certificate hostname matches the remote\n");
printf(" hostname. Using this option means that you cannot be sure that the\n");
printf(" remote host is the server you wish to connect to and so is insecure.\n");
printf(" Do not use this option in a production environment.\n");
printf(" --tls-engine : toggles the usage of a SSL engine device.\n");
#ifdef WITH_TLS_PSK
printf(" --psk : pre-shared-key in hexadecimal (no leading 0x) to enable TLS-PSK mode.\n");
printf(" --psk-identity : client identity string for TLS-PSK mode.\n");
Expand Down
10 changes: 10 additions & 0 deletions lib/cpp/mosquittopp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -366,4 +366,14 @@ int mosquittopp::tls_psk_set(const char *psk, const char *identity, const char *
return mosquitto_tls_psk_set(m_mosq, psk, identity, ciphers);
}

int mosquittopp::tls_engine_set(const char *engine_id)
{
return mosquitto_tls_engine_set(m_mosq, engine_id);
}

int mosquittopp::tls_keyform_set(const char *keyform)
{
return mosquitto_tls_keyform_set(m_mosq, keyform);
}

}
2 changes: 2 additions & 0 deletions lib/cpp/mosquittopp.h
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,8 @@ class mosqpp_EXPORT mosquittopp {
int tls_opts_set(int cert_reqs, const char *tls_version=NULL, const char *ciphers=NULL);
int tls_insecure_set(bool value);
int tls_psk_set(const char *psk, const char *identity, const char *ciphers=NULL);
int tls_engine_set(const char *engine_id);
int tls_keyform_set(const char *keyform);
int opts_set(enum mosq_opt_t option, void *value);

int loop(int timeout=-1, int max_packets=1);
Expand Down
2 changes: 2 additions & 0 deletions lib/linker.version
Original file line number Diff line number Diff line change
Expand Up @@ -90,6 +90,8 @@ MOSQ_1.5 {
mosquitto_sub_topic_check2;
mosquitto_topic_matches_sub2;
mosquitto_connect_with_flags_callback_set;
mosquitto_tls_engine_set;
mosquitto_tls_keyform_set;
} MOSQ_1.4;

MOSQ_1.6 {
Expand Down
38 changes: 38 additions & 0 deletions lib/mosquitto.h
Original file line number Diff line number Diff line change
Expand Up @@ -1160,6 +1160,44 @@ libmosq_EXPORT int mosquitto_tls_opts_set(struct mosquitto *mosq, int cert_reqs,
*/
libmosq_EXPORT int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers);

/*
* Function: mosquitto_tls_engine_set
*
* Configure the client for TLS engine support. Must be called
* before <mosquitto_connect>.
*
* Parameters:
* mosq - a valid mosquitto instance.
* engine_id - the engine ID that wants to be used.
*
* Returns:
* MOSQ_ERR_SUCCESS - on success.
* MOSQ_ERR_INVAL - if the input parameters were invalid.
*
* See Also:
* <mosquitto_tls_set>
*/
libmosq_EXPORT int mosquitto_tls_engine_set(struct mosquitto *mosq, const char *engine_id);

/*
* Function: mosquitto_tls_keyform_set
*
* Configure the client to treat the keyfile differently depending on its type.
* Must be called before <mosquitto_connect>.
*
* Parameters:
* mosq - a valid mosquitto instance.
* keyform - the key type. Currently only "pem" or "engine" are supported.
*
* Returns:
* MOSQ_ERR_SUCCESS - on success.
* MOSQ_ERR_INVAL - if the input parameters were invalid.
*
* See Also:
* <mosquitto_tls_set>
*/
libmosq_EXPORT int mosquitto_tls_keyform_set(struct mosquitto *mosq, const char *keyform);

/*
* Function: mosquitto_connect_callback_set
*
Expand Down
9 changes: 9 additions & 0 deletions lib/mosquitto_internal.h
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,13 @@ struct mosquitto_message_all{
struct mosquitto_message msg;
};

#ifdef WITH_TLS
enum _mosquitto_keyform {
mosq_k_pem = 0,
mosq_k_engine = 1,
};
#endif

struct mosquitto {
mosq_sock_t sock;
#ifndef WITH_BROKER
Expand Down Expand Up @@ -191,6 +198,8 @@ struct mosquitto {
int tls_cert_reqs;
bool tls_insecure;
bool ssl_ctx_defaults;
char *tls_engine;
enum _mosquitto_keyform tls_keyform;
#endif
bool want_write;
bool want_connect;
Expand Down
98 changes: 89 additions & 9 deletions lib/net_mosq.c
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,42 @@ and the Eclipse Distribution License is available at

#ifdef WITH_TLS
int tls_ex_index_mosq = -1;
UI_METHOD *_ui_method = NULL;

/* Functions taken from OpenSSL s_server/s_client */
static int ui_open(UI *ui)
{
return UI_method_get_opener(UI_OpenSSL())(ui);
}

static int ui_read(UI *ui, UI_STRING *uis)
{
return UI_method_get_reader(UI_OpenSSL())(ui, uis);
}

static int ui_write(UI *ui, UI_STRING *uis)
{
return UI_method_get_writer(UI_OpenSSL())(ui, uis);
}

static int ui_close(UI *ui)
{
return UI_method_get_closer(UI_OpenSSL())(ui);
}

static void setup_ui_method(void)
{
_ui_method = UI_create_method("OpenSSL application user interface");
UI_method_set_opener(_ui_method, ui_open);
UI_method_set_reader(_ui_method, ui_read);
UI_method_set_writer(_ui_method, ui_write);
UI_method_set_closer(_ui_method, ui_close);
}

UI_METHOD *net__get_ui_method(void)
{
return _ui_method;
}
#endif

int net__init(void)
Expand All @@ -90,6 +126,9 @@ int net__init(void)
SSL_load_error_strings();
SSL_library_init();
OpenSSL_add_all_algorithms();
OPENSSL_config(NULL);
ENGINE_load_builtin_engines();
setup_ui_method();
if(tls_ex_index_mosq == -1){
tls_ex_index_mosq = SSL_get_ex_new_index(0, "client context", NULL, NULL, NULL);
}
Expand Down Expand Up @@ -440,6 +479,7 @@ int net__socket_connect_tls(struct mosquitto *mosq)
static int net__init_ssl_ctx(struct mosquitto *mosq)
{
int ret;
ENGINE *engine = NULL;

if(mosq->ssl_ctx){
if(!mosq->ssl_ctx_defaults){
Expand Down Expand Up @@ -493,10 +533,28 @@ static int net__init_ssl_ctx(struct mosquitto *mosq)
SSL_CTX_set_mode(mosq->ssl_ctx, SSL_MODE_RELEASE_BUFFERS);
#endif

if(mosq->tls_engine){
engine = ENGINE_by_id(mosq->tls_engine);
if(!engine){
log__printf(mosq, MOSQ_LOG_ERR, "Error loading %s engine\n", mosq->tls_engine);
COMPAT_CLOSE(mosq->sock);
return MOSQ_ERR_TLS;
}
if(!ENGINE_init(engine)){
log__printf(mosq, MOSQ_LOG_ERR, "Failed engine initialisation\n");
ENGINE_free(engine);
COMPAT_CLOSE(mosq->sock);
return MOSQ_ERR_TLS;
}
ENGINE_set_default(engine, ENGINE_METHOD_ALL);
ENGINE_free(engine); /* release the structural reference from ENGINE_by_id() */
}

if(mosq->tls_ciphers){
ret = SSL_CTX_set_cipher_list(mosq->ssl_ctx, mosq->tls_ciphers);
if(ret == 0){
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set TLS ciphers. Check cipher list \"%s\".", mosq->tls_ciphers);
ENGINE_FINISH(engine);
COMPAT_CLOSE(mosq->sock);
mosq->sock = INVALID_SOCKET;
net__print_ssl_error(mosq);
Expand All @@ -523,6 +581,7 @@ static int net__init_ssl_ctx(struct mosquitto *mosq)
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load CA certificates, check capath \"%s\".", mosq->tls_capath);
}
#endif
ENGINE_FINISH(engine);
COMPAT_CLOSE(mosq->sock);
mosq->sock = INVALID_SOCKET;
net__print_ssl_error(mosq);
Expand All @@ -547,28 +606,49 @@ static int net__init_ssl_ctx(struct mosquitto *mosq)
#else
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client certificate \"%s\".", mosq->tls_certfile);
#endif
ENGINE_FINISH(engine);
COMPAT_CLOSE(mosq->sock);
mosq->sock = INVALID_SOCKET;
net__print_ssl_error(mosq);
return MOSQ_ERR_TLS;
}
}
if(mosq->tls_keyfile){
ret = SSL_CTX_use_PrivateKey_file(mosq->ssl_ctx, mosq->tls_keyfile, SSL_FILETYPE_PEM);
if(ret != 1){
if(mosq->tls_keyform == mosq_k_engine){
EVP_PKEY *pkey = ENGINE_load_private_key(engine, mosq->tls_keyfile, ui_method, NULL);
if(!pkey){
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load engine private key file \"%s\".", mosq->tls_keyfile);
ENGINE_FINISH(engine);
COMPAT_CLOSE(mosq->sock);
net__print_ssl_error(mosq);
return MOSQ_ERR_TLS;
}
if(SSL_CTX_use_PrivateKey(mosq->ssl_ctx, pkey) <= 0){
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to use engine private key file \"%s\".", mosq->tls_keyfile);
ENGINE_FINISH(engine);
COMPAT_CLOSE(mosq->sock);
net__print_ssl_error(mosq);
return MOSQ_ERR_TLS;
}
}else{
ret = SSL_CTX_use_PrivateKey_file(mosq->ssl_ctx, mosq->tls_keyfile, SSL_FILETYPE_PEM);
if(ret != 1){
#ifdef WITH_BROKER
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file, check bridge_keyfile \"%s\".", mosq->tls_keyfile);
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file, check bridge_keyfile \"%s\".", mosq->tls_keyfile);
#else
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile);
#endif
COMPAT_CLOSE(mosq->sock);
mosq->sock = INVALID_SOCKET;
net__print_ssl_error(mosq);
return MOSQ_ERR_TLS;
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile);
#endif
ENGINE_FINISH(engine);
COMPAT_CLOSE(mosq->sock);
mosq->sock = INVALID_SOCKET;
net__print_ssl_error(mosq);
return MOSQ_ERR_TLS;
}
}
ret = SSL_CTX_check_private_key(mosq->ssl_ctx);
if(ret != 1){
log__printf(mosq, MOSQ_LOG_ERR, "Error: Client certificate/key are inconsistent.");
ENGINE_FINISH(engine);
COMPAT_CLOSE(mosq->sock);
mosq->sock = INVALID_SOCKET;
net__print_ssl_error(mosq);
Expand Down
2 changes: 2 additions & 0 deletions lib/net_mosq.h
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ ssize_t net__write(struct mosquitto *mosq, void *buf, size_t count);
#ifdef WITH_TLS
int net__socket_apply_tls(struct mosquitto *mosq);
int net__socket_connect_tls(struct mosquitto *mosq);
UI_METHOD *net__get_ui_method(void);
#define ENGINE_FINISH(e) if(e) ENGINE_finish(e)
#endif

#endif
Loading

0 comments on commit f88cc06

Please sign in to comment.