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

Openssl engine support - revisited #915

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from 1 commit
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
Next Next commit
Add TLS engine and keyform support to libmosquitto
- 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 committed Aug 11, 2018
commit ea85fff7ca3269a1c3c9ab72dc2d985d24eb4ebc
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 @@ -556,6 +570,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 @@ -912,6 +934,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 @@ -223,7 +223,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 @@ -274,13 +275,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 @@ -154,7 +154,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 @@ -211,13 +212,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,4 +90,6 @@ 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;
38 changes: 38 additions & 0 deletions lib/mosquitto.h
Original file line number Diff line number Diff line change
Expand Up @@ -1130,6 +1130,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 @@ -151,6 +151,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 @@ -189,6 +196,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
94 changes: 87 additions & 7 deletions lib/net_mosq.c
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,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 @@ -99,6 +135,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 @@ -447,6 +486,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 @@ -498,10 +538,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);
net__print_ssl_error(mosq);
return MOSQ_ERR_TLS;
Expand All @@ -527,6 +585,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);
net__print_ssl_error(mosq);
return MOSQ_ERR_TLS;
Expand All @@ -550,26 +609,47 @@ 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);
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);
log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to load client key file \"%s\".", mosq->tls_keyfile);
#endif
COMPAT_CLOSE(mosq->sock);
net__print_ssl_error(mosq);
return MOSQ_ERR_TLS;
ENGINE_FINISH(engine);
COMPAT_CLOSE(mosq->sock);
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);
net__print_ssl_error(mosq);
return MOSQ_ERR_TLS;
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