From ea85fff7ca3269a1c3c9ab72dc2d985d24eb4ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pernas=20Maradei?= Date: Thu, 9 Aug 2018 21:37:33 +0200 Subject: [PATCH 1/3] Add TLS engine and keyform support to libmosquitto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 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 --- client/client_shared.c | 32 ++++++++++++++ client/client_shared.h | 2 + client/pub_client.c | 5 ++- client/sub_client.c | 5 ++- lib/cpp/mosquittopp.cpp | 10 +++++ lib/cpp/mosquittopp.h | 2 + lib/linker.version | 2 + lib/mosquitto.h | 38 ++++++++++++++++ lib/mosquitto_internal.h | 9 ++++ lib/net_mosq.c | 94 +++++++++++++++++++++++++++++++++++++--- lib/net_mosq.h | 2 + lib/options.c | 38 ++++++++++++++++ lib/tls_mosq.h | 1 + man/mosquitto_pub.1.xml | 24 ++++++++++ man/mosquitto_sub.1.xml | 24 ++++++++++ 15 files changed, 279 insertions(+), 9 deletions(-) diff --git a/client/client_shared.c b/client/client_shared.c index f14ebb1ecc..1179c567ba 100644 --- a/client/client_shared.c +++ b/client/client_shared.c @@ -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); @@ -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){ @@ -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){ @@ -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){ @@ -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"); diff --git a/client/client_shared.h b/client/client_shared.h index f1ce6f3132..789664a417 100644 --- a/client/client_shared.h +++ b/client/client_shared.h @@ -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; diff --git a/client/pub_client.c b/client/pub_client.c index 85fd73c8f0..ed6263d61c 100644 --- a/client/pub_client.c +++ b/client/pub_client.c @@ -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 @@ -274,6 +275,7 @@ 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"); @@ -281,6 +283,7 @@ void print_usage(void) 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"); diff --git a/client/sub_client.c b/client/sub_client.c index 1d83684610..66a090ee82 100644 --- a/client/sub_client.c +++ b/client/sub_client.c @@ -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 @@ -211,6 +212,7 @@ 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"); @@ -218,6 +220,7 @@ void print_usage(void) 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"); diff --git a/lib/cpp/mosquittopp.cpp b/lib/cpp/mosquittopp.cpp index 71b32068d0..2fcef663d2 100644 --- a/lib/cpp/mosquittopp.cpp +++ b/lib/cpp/mosquittopp.cpp @@ -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); +} + } diff --git a/lib/cpp/mosquittopp.h b/lib/cpp/mosquittopp.h index 38a5835fb1..961c3c92e0 100644 --- a/lib/cpp/mosquittopp.h +++ b/lib/cpp/mosquittopp.h @@ -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); diff --git a/lib/linker.version b/lib/linker.version index c592d17eef..c330d4aafd 100644 --- a/lib/linker.version +++ b/lib/linker.version @@ -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; diff --git a/lib/mosquitto.h b/lib/mosquitto.h index e67a2b3427..aad1877628 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -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 . + * + * 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: + * + */ +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 . + * + * 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: + * + */ +libmosq_EXPORT int mosquitto_tls_keyform_set(struct mosquitto *mosq, const char *keyform); + /* * Function: mosquitto_connect_callback_set * diff --git a/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index 05667a78b1..21dda9eee2 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -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 @@ -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; diff --git a/lib/net_mosq.c b/lib/net_mosq.c index daf8ce94ba..0fce346ea6 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -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) @@ -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); } @@ -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){ @@ -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; @@ -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; @@ -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; diff --git a/lib/net_mosq.h b/lib/net_mosq.h index b5d854948d..5bb5cefa6d 100644 --- a/lib/net_mosq.h +++ b/lib/net_mosq.h @@ -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 diff --git a/lib/options.c b/lib/options.c index dd9f718099..7ed2a95ac6 100644 --- a/lib/options.c +++ b/lib/options.c @@ -219,6 +219,44 @@ int mosquitto_tls_insecure_set(struct mosquitto *mosq, bool value) } +int mosquitto_tls_engine_set(struct mosquitto *mosq, const char *engine_id) +{ +#ifdef WITH_TLS + if(!mosq) return MOSQ_ERR_INVAL; + ENGINE *e = ENGINE_by_id(engine_id); + if (!e) + return MOSQ_ERR_INVAL; + ENGINE_free(e); /* release the structural reference from ENGINE_by_id() */ + mosq->tls_engine = mosquitto__strdup(engine_id); + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif +} + + +int mosquitto_tls_keyform_set(struct mosquitto *mosq, const char *keyform) +{ +#ifdef WITH_TLS + if(!mosq) return MOSQ_ERR_INVAL; + + if (keyform){ + if(!strcasecmp(keyform, "pem")) + mosq->tls_keyform = mosq_k_pem; + else if (!strcasecmp(keyform, "engine")) + mosq->tls_keyform = mosq_k_engine; + else + return MOSQ_ERR_INVAL; + }else{ + mosq->tls_keyform = mosq_k_pem; + } + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif +} + + int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers) { #ifdef WITH_TLS_PSK diff --git a/lib/tls_mosq.h b/lib/tls_mosq.h index 6694818056..f28d2eb16c 100644 --- a/lib/tls_mosq.h +++ b/lib/tls_mosq.h @@ -26,6 +26,7 @@ and the Eclipse Distribution License is available at #ifdef WITH_TLS #include +#include int mosquitto__server_certificate_verify(int preverify_ok, X509_STORE_CTX *ctx); int mosquitto__verify_certificate_hostname(X509 *cert, const char *hostname); diff --git a/man/mosquitto_pub.1.xml b/man/mosquitto_pub.1.xml index ab221c7d7c..d2ed0771f2 100644 --- a/man/mosquitto_pub.1.xml +++ b/man/mosquitto_pub.1.xml @@ -62,6 +62,12 @@ file ciphers version + engine + + + pem + engine + @@ -180,6 +186,24 @@ for more information. + + + + A valid openssl engine id. These can be listed with + openssl engine command. + See also . + + + + + + Specifies the type of key in use. This could be pem or + engine. This parameter is useful for example when a TPM + module is being used and the key has been created with + it. If not specified, pem keys are assumed + See also . + + diff --git a/man/mosquitto_sub.1.xml b/man/mosquitto_sub.1.xml index 01f9c979e7..bcb2921bb0 100644 --- a/man/mosquitto_sub.1.xml +++ b/man/mosquitto_sub.1.xml @@ -68,6 +68,12 @@ file file version + engine + + + pem + engine + @@ -189,6 +195,24 @@ for more information. + + + + A valid openssl engine id. These can be listed with + openssl engine command. + See also . + + + + + + Specifies the type of key in use. This could be pem or + engine. This parameter is useful for example when a TPM + module is being used and the key has been created with + it. If not specified, pem keys are assumed + See also . + + From c311757b5741564757d208e0850c8ba9aeb27724 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pernas=20Maradei?= Date: Thu, 9 Aug 2018 22:07:08 +0200 Subject: [PATCH 2/3] Add TLS engine and keyform support to mosquitto MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add same OpenSSL engine support to mosquitto (server side) previously added to client side only. Signed-off-by: Nicolás Pernas Maradei --- man/mosquitto.conf.5.xml | 16 +++++++++++ src/conf.c | 23 +++++++++++++++ src/mosquitto_broker_internal.h | 2 ++ src/net.c | 50 +++++++++++++++++++++++++++++---- 4 files changed, 86 insertions(+), 5 deletions(-) diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index 9347cfc5ea..b74b750b63 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -872,6 +872,22 @@ the "openssl ciphers" command. + + engine + + A valid openssl engine id. These can be listed with + openssl engine command. + + + + [ pem | engine ] + + Specifies the type of key in use. This could be pem or + engine. This parameter is useful for example when a TPM + module is being used and the key has been created with + it. If not specified, pem keys are assumed + + file path diff --git a/src/conf.c b/src/conf.c index f3d812dbc4..5bfdfa0254 100644 --- a/src/conf.c +++ b/src/conf.c @@ -298,6 +298,7 @@ void config__cleanup(struct mosquitto__config *config) mosquitto__free(config->listeners[i].psk_hint); mosquitto__free(config->listeners[i].crlfile); mosquitto__free(config->listeners[i].tls_version); + mosquitto__free(config->listeners[i].tls_engine); #ifdef WITH_WEBSOCKETS if(!config->listeners[i].ws_context) /* libwebsockets frees its own SSL_CTX */ #endif @@ -433,6 +434,8 @@ int config__parse_args(struct mosquitto_db *db, struct mosquitto__config *config || config->default_listener.capath || config->default_listener.certfile || config->default_listener.keyfile + || config->default_listener.tls_engine + || config->default_listener.tls_keyform != mosq_k_pem || config->default_listener.ciphers || config->default_listener.psk_hint || config->default_listener.require_certificate @@ -483,6 +486,8 @@ int config__parse_args(struct mosquitto_db *db, struct mosquitto__config *config config->listeners[config->listener_count-1].use_username_as_clientid = config->default_listener.use_username_as_clientid; #ifdef WITH_TLS config->listeners[config->listener_count-1].tls_version = config->default_listener.tls_version; + config->listeners[config->listener_count-1].tls_engine = config->default_listener.tls_engine; + config->listeners[config->listener_count-1].tls_keyform = config->default_listener.tls_keyform; config->listeners[config->listener_count-1].cafile = config->default_listener.cafile; config->listeners[config->listener_count-1].capath = config->default_listener.capath; config->listeners[config->listener_count-1].certfile = config->default_listener.certfile; @@ -1076,6 +1081,24 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct if(conf__parse_string(&token, "certfile", &cur_listener->certfile, saveptr)) return MOSQ_ERR_INVAL; #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); +#endif + }else if(!strcmp(token, "tls_engine")){ +#ifdef WITH_TLS + if(reload) continue; // Listeners not valid for reloading. + if(conf__parse_string(&token, "tls_engine", &cur_listener->tls_engine, saveptr)) return MOSQ_ERR_INVAL; +#else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); +#endif + }else if(!strcmp(token, "tls_keyform")){ +#ifdef WITH_TLS + if(reload) continue; // Listeners not valid for reloading. + char *keyform = NULL; + if(conf__parse_string(&token, "tls_keyform", &keyform, saveptr)) return MOSQ_ERR_INVAL; + cur_listener->tls_keyform = mosq_k_pem; + if(!strcmp(keyform, "engine")) cur_listener->tls_keyform = mosq_k_engine; + mosquitto__free(keyform); +#else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "ciphers")){ #ifdef WITH_TLS diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index 85aff0536e..0ec34efe15 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -221,6 +221,8 @@ struct mosquitto__listener { char *capath; char *certfile; char *keyfile; + char *tls_engine; + enum _mosquitto_keyform tls_keyform; char *ciphers; char *psk_hint; SSL_CTX *ssl_ctx; diff --git a/src/net.c b/src/net.c index 7397de577d..82ff54f7f3 100644 --- a/src/net.c +++ b/src/net.c @@ -347,6 +347,7 @@ int net__socket_listen(struct mosquitto__listener *listener) int rc; X509_STORE *store; X509_LOOKUP *lookup; + ENGINE *engine = NULL; #endif if(!listener) return MOSQ_ERR_INVAL; @@ -430,6 +431,22 @@ int net__socket_listen(struct mosquitto__listener *listener) COMPAT_CLOSE(sock); return 1; } + if(listener->tls_engine){ + engine = ENGINE_by_id(listener->tls_engine); + if(!engine){ + log__printf(NULL, MOSQ_LOG_ERR, "Error loading %s engine\n", listener->tls_engine); + COMPAT_CLOSE(sock); + return 1; + } + if(!ENGINE_init(engine)){ + log__printf(NULL, MOSQ_LOG_ERR, "Failed engine initialisation\n"); + ENGINE_free(engine); + COMPAT_CLOSE(sock); + return 1; + } + ENGINE_set_default(engine, ENGINE_METHOD_ALL); + ENGINE_free(engine); /* release the structural reference from ENGINE_by_id() */ + } /* FIXME user data? */ if(listener->require_certificate){ SSL_CTX_set_verify(listener->ssl_ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, client_certificate_verify); @@ -440,18 +457,37 @@ int net__socket_listen(struct mosquitto__listener *listener) if(rc != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load server certificate \"%s\". Check certfile.", listener->certfile); COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); return 1; } - 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); - COMPAT_CLOSE(sock); - return 1; + if(listener->tls_keyform == mosq_k_engine){ + EVP_PKEY *pkey = ENGINE_load_private_key(engine, listener->keyfile, net__get_ui_method(), NULL); + if(!pkey){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load engine private key file \"%s\".", listener->keyfile); + COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); + return 1; + } + if(SSL_CTX_use_PrivateKey(listener->ssl_ctx, pkey) <= 0){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to use engine private key file \"%s\".", listener->keyfile); + COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); + return 1; + } + }else{ + 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); + COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); + return 1; + } } rc = SSL_CTX_check_private_key(listener->ssl_ctx); if(rc != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Server certificate/key are inconsistent."); COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); return 1; } /* Load CRLs if they exist. */ @@ -460,6 +496,7 @@ int net__socket_listen(struct mosquitto__listener *listener) if(!store){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to obtain TLS store."); COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); return 1; } lookup = X509_STORE_add_lookup(store, X509_LOOKUP_file()); @@ -467,6 +504,7 @@ int net__socket_listen(struct mosquitto__listener *listener) if(rc != 1){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load certificate revocation file \"%s\". Check crlfile.", listener->crlfile); COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); return 1; } X509_STORE_set_flags(store, X509_V_FLAG_CRL_CHECK); @@ -483,6 +521,7 @@ int net__socket_listen(struct mosquitto__listener *listener) if(mosquitto__tls_server_ctx(listener)){ COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); return 1; } SSL_CTX_set_psk_server_callback(listener->ssl_ctx, psk_server_callback); @@ -491,6 +530,7 @@ int net__socket_listen(struct mosquitto__listener *listener) if(rc == 0){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to set TLS PSK hint."); COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); return 1; } } From f26b197cb75ada31541d942b850c9b369d259c2e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=A1s=20Pernas=20Maradei?= Date: Sat, 11 Aug 2018 17:57:58 +0100 Subject: [PATCH 3/3] Add engine private key password support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some OpenSSL engines (selectable via tls_engine option) may require a password to make use of private keys created with them in the first place. The TPM engine for example, will require a password to access the underlying TPM's Storage Root Key (SRK), which is the root key of a hierarchy of keys associated with a TPM; it is generated within a TPM and is a non-migratable key. Each owned TPM contains a SRK, generated by the TPM at the request of the Owner. [1] By default, the engine will prompt the user to introduce the SRK password before any private keys created with the engine can be used. This could be inconvenient when running on an unattended system. Here's where the new tls_engine_kpass_sha option comes in handy. The user can specify a SHA1 hash of its engine private key password via command line or config file and it will be passed on to the engine directly. This commit adds support for both clients (libmosquitto) and broker. [1] https://goo.gl/qQoXBY Signed-off-by: Nicolás Pernas Maradei --- client/client_shared.c | 18 ++++++++++++++++++ client/client_shared.h | 1 + client/pub_client.c | 3 ++- client/sub_client.c | 3 ++- lib/cpp/mosquittopp.cpp | 5 +++++ lib/cpp/mosquittopp.h | 1 + lib/linker.version | 1 + lib/mosquitto.h | 21 +++++++++++++++++++++ lib/mosquitto_internal.h | 1 + lib/net_mosq.c | 18 ++++++++++++++++++ lib/net_mosq.h | 3 +++ lib/options.c | 14 ++++++++++++++ lib/util_mosq.c | 17 ++++++++++++++++- lib/util_mosq.h | 6 +++++- man/mosquitto.conf.5.xml | 11 +++++++++++ man/mosquitto_pub.1.xml | 13 +++++++++++++ man/mosquitto_sub.1.xml | 13 +++++++++++++ src/conf.c | 17 +++++++++++++++++ src/mosquitto_broker_internal.h | 1 + src/net.c | 18 +++++++++++++++++- 20 files changed, 180 insertions(+), 5 deletions(-) diff --git a/client/client_shared.c b/client/client_shared.c index 1179c567ba..c45280aef2 100644 --- a/client/client_shared.c +++ b/client/client_shared.c @@ -152,6 +152,7 @@ void client_config_cleanup(struct mosq_config *cfg) free(cfg->ciphers); free(cfg->tls_version); free(cfg->tls_engine); + free(cfg->tls_engine_kpass_sha); free(cfg->keyform); # ifdef WITH_TLS_PSK free(cfg->psk); @@ -314,6 +315,10 @@ int client_config_load(struct mosq_config *cfg, int pub_or_sub, int argc, char * fprintf(stderr, "Error: keyfile must be specified if keyform is.\n"); return 1; } + if((cfg->tls_engine_kpass_sha && (!cfg->keyform || !cfg->tls_engine))){ + fprintf(stderr, "Error: when using tls-engine-kpass-sha, both tls-engine and keyform must also be provided.\n"); + return 1; + } #endif #ifdef WITH_TLS_PSK if((cfg->cafile || cfg->capath) && cfg->psk){ @@ -439,6 +444,14 @@ int client_config_line_proc(struct mosq_config *cfg, int pub_or_sub, int argc, c cfg->tls_engine = strdup(argv[i+1]); } i++; + }else if(!strcmp(argv[i], "--tls-engine-kpass-sha")){ + if(i==argc-1){ + fprintf(stderr, "Error: --tls-engine-kpass-sha argument given but no kpass sha specified.\n\n"); + return 1; + }else{ + cfg->tls_engine_kpass_sha = strdup(argv[i+1]); + } + i++; #endif }else if(!strcmp(argv[i], "-C")){ if(pub_or_sub == CLIENT_PUB){ @@ -944,6 +957,11 @@ int client_opts_set(struct mosquitto *mosq, struct mosq_config *cfg) mosquitto_lib_cleanup(); return 1; } + if(cfg->tls_engine_kpass_sha && mosquitto_tls_engine_kpass_sha_set(mosq, cfg->tls_engine_kpass_sha)){ + if(!cfg->quiet) fprintf(stderr, "Error: Problem setting TLS engine key pass sha.\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"); diff --git a/client/client_shared.h b/client/client_shared.h index 789664a417..9c6b13887d 100644 --- a/client/client_shared.h +++ b/client/client_shared.h @@ -67,6 +67,7 @@ struct mosq_config { bool insecure; char *tls_version; char *tls_engine; + char *tls_engine_kpass_sha; char *keyform; # ifdef WITH_TLS_PSK char *psk; diff --git a/client/pub_client.c b/client/pub_client.c index ed6263d61c..6997675e66 100644 --- a/client/pub_client.c +++ b/client/pub_client.c @@ -224,7 +224,7 @@ void print_usage(void) #ifdef WITH_TLS printf(" [{--cafile file | --capath dir} [--cert file] [--key file]\n"); printf(" [--ciphers ciphers] [--insecure] [--tls-engine engine]\n"); - printf(" [--keyform keyform]]\n"); + printf(" [--keyform keyform] [--tls-engine-kpass-sha]]\n"); #ifdef WITH_TLS_PSK printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n"); #endif @@ -284,6 +284,7 @@ void print_usage(void) 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"); + printf(" --tls-engine-kpass-sha : SHA1 of the key password to be used with the selected SSL engine.\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"); diff --git a/client/sub_client.c b/client/sub_client.c index 66a090ee82..e1854d7f5a 100644 --- a/client/sub_client.c +++ b/client/sub_client.c @@ -155,7 +155,7 @@ void print_usage(void) #ifdef WITH_TLS printf(" [{--cafile file | --capath dir} [--cert file] [--key file]\n"); printf(" [--ciphers ciphers] [--insecure] [--tls-engine engine]\n"); - printf(" [--keyform keyform]]\n"); + printf(" [--keyform keyform] [--tls-engine-kpass-sha]]\n"); #ifdef WITH_TLS_PSK printf(" [--psk hex-key --psk-identity identity [--ciphers ciphers]]\n"); #endif @@ -221,6 +221,7 @@ void print_usage(void) 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"); + printf(" --tls-engine-kpass-sha : SHA1 of the key password to be used with the selected SSL engine.\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"); diff --git a/lib/cpp/mosquittopp.cpp b/lib/cpp/mosquittopp.cpp index 2fcef663d2..345b9d97a1 100644 --- a/lib/cpp/mosquittopp.cpp +++ b/lib/cpp/mosquittopp.cpp @@ -376,4 +376,9 @@ int mosquittopp::tls_keyform_set(const char *keyform) return mosquitto_tls_keyform_set(m_mosq, keyform); } +int mosquittopp::tls_engine_kpass_sha_set(const char *kpass_sha) +{ + return mosquitto_tls_engine_kpass_sha_set(m_mosq, kpass_sha); +} + } diff --git a/lib/cpp/mosquittopp.h b/lib/cpp/mosquittopp.h index 961c3c92e0..b29ee94791 100644 --- a/lib/cpp/mosquittopp.h +++ b/lib/cpp/mosquittopp.h @@ -112,6 +112,7 @@ class mosqpp_EXPORT mosquittopp { 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 tls_engine_kpass_sha_set(const char *kpass_sha); int opts_set(enum mosq_opt_t option, void *value); int loop(int timeout=-1, int max_packets=1); diff --git a/lib/linker.version b/lib/linker.version index c330d4aafd..8178c7b482 100644 --- a/lib/linker.version +++ b/lib/linker.version @@ -92,4 +92,5 @@ MOSQ_1.5 { mosquitto_connect_with_flags_callback_set; mosquitto_tls_engine_set; mosquitto_tls_keyform_set; + mosquitto_tls_engine_kpass_sha_set; } MOSQ_1.4; diff --git a/lib/mosquitto.h b/lib/mosquitto.h index aad1877628..d540074071 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -1168,6 +1168,27 @@ libmosq_EXPORT int mosquitto_tls_engine_set(struct mosquitto *mosq, const char * */ libmosq_EXPORT int mosquitto_tls_keyform_set(struct mosquitto *mosq, const char *keyform); +/* + * Function: mosquitto_tls_engine_kpass_sha_set + * + * Some SSL engines may require the usage of a password in order to being + * accessed, like the TPM engine. This function allows a SHA1 hash of the + * password to be passed on to the engine directly. + * Must be called before . + * + * Parameters: + * mosq - a valid mosquitto instance. + * kpass_sha - SHA1 of the private key password. + * + * Returns: + * MOSQ_ERR_SUCCESS - on success. + * MOSQ_ERR_INVAL - if the input parameters were invalid. + * + * See Also: + * + */ +libmosq_EXPORT int mosquitto_tls_engine_kpass_sha_set(struct mosquitto *mosq, const char *kpass_sha); + /* * Function: mosquitto_connect_callback_set * diff --git a/lib/mosquitto_internal.h b/lib/mosquitto_internal.h index 21dda9eee2..ada384f48e 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -197,6 +197,7 @@ struct mosquitto { bool tls_insecure; bool ssl_ctx_defaults; char *tls_engine; + char *tls_engine_kpass_sha; enum _mosquitto_keyform tls_keyform; #endif bool want_write; diff --git a/lib/net_mosq.c b/lib/net_mosq.c index 0fce346ea6..bc3b68dd0f 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -617,6 +617,24 @@ static int net__init_ssl_ctx(struct mosquitto *mosq) } if(mosq->tls_keyfile){ if(mosq->tls_keyform == mosq_k_engine){ + UI_METHOD *ui_method = net__get_ui_method(); + if(mosq->tls_engine_kpass_sha){ + if(!ENGINE_ctrl_cmd(engine, ENGINE_SECRET_MODE, ENGINE_SECRET_MODE_SHA, NULL, NULL, 0)){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set engine secret mode sha"); + ENGINE_FINISH(engine); + COMPAT_CLOSE(mosq->sock); + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + if(!ENGINE_ctrl_cmd(engine, ENGINE_PIN, 0, mosq->tls_engine_kpass_sha, NULL, 0)){ + log__printf(mosq, MOSQ_LOG_ERR, "Error: Unable to set engine pin"); + ENGINE_FINISH(engine); + COMPAT_CLOSE(mosq->sock); + net__print_ssl_error(mosq); + return MOSQ_ERR_TLS; + } + ui_method = NULL; + } 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); diff --git a/lib/net_mosq.h b/lib/net_mosq.h index 5bb5cefa6d..fe58749827 100644 --- a/lib/net_mosq.h +++ b/lib/net_mosq.h @@ -73,6 +73,9 @@ 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) +#define ENGINE_SECRET_MODE "SECRET_MODE" +#define ENGINE_SECRET_MODE_SHA 0x1000 +#define ENGINE_PIN "PIN" #endif #endif diff --git a/lib/options.c b/lib/options.c index 7ed2a95ac6..a5c9d796aa 100644 --- a/lib/options.c +++ b/lib/options.c @@ -257,6 +257,20 @@ int mosquitto_tls_keyform_set(struct mosquitto *mosq, const char *keyform) } +int mosquitto_tls_engine_kpass_sha_set(struct mosquitto *mosq, const char *kpass_sha) +{ +#ifdef WITH_TLS + if(!mosq) return MOSQ_ERR_INVAL; + char *kpass_sha_bin = NULL; + if(mosquitto__hex2bin_sha1(kpass_sha, (unsigned char**)&kpass_sha_bin) != MOSQ_ERR_SUCCESS) return MOSQ_ERR_INVAL; + mosq->tls_engine_kpass_sha = kpass_sha_bin; + return MOSQ_ERR_SUCCESS; +#else + return MOSQ_ERR_NOT_SUPPORTED; +#endif +} + + int mosquitto_tls_psk_set(struct mosquitto *mosq, const char *psk, const char *identity, const char *ciphers) { #ifdef WITH_TLS_PSK diff --git a/lib/util_mosq.c b/lib/util_mosq.c index b85b95cefc..3d0eec58c3 100644 --- a/lib/util_mosq.c +++ b/lib/util_mosq.c @@ -344,7 +344,22 @@ int mosquitto_topic_matches_sub2(const char *sub, size_t sublen, const char *top return MOSQ_ERR_SUCCESS; } -#ifdef WITH_TLS_PSK +#ifdef WITH_TLS +int mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin) +{ + unsigned char *sha, tmp[SHA_DIGEST_LENGTH]; + + if(mosquitto__hex2bin(hex, tmp, SHA_DIGEST_LENGTH) != SHA_DIGEST_LENGTH) + return MOSQ_ERR_INVAL; + + sha = mosquitto__malloc(SHA_DIGEST_LENGTH); + memcpy(sha, tmp, SHA_DIGEST_LENGTH); + *bin = sha; + return MOSQ_ERR_SUCCESS; +} +#endif + +#if defined(WITH_TLS_PSK) || defined(WITH_TLS) int mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len) { BIGNUM *bn = NULL; diff --git a/lib/util_mosq.h b/lib/util_mosq.h index 0e65dd9889..bec6a2ec91 100644 --- a/lib/util_mosq.h +++ b/lib/util_mosq.h @@ -33,7 +33,11 @@ void mosquitto__check_keepalive(struct mosquitto *mosq); uint16_t mosquitto__mid_generate(struct mosquitto *mosq); FILE *mosquitto__fopen(const char *path, const char *mode, bool restrict_read); -#ifdef WITH_TLS_PSK +#ifdef WITH_TLS +int mosquitto__hex2bin_sha1(const char *hex, unsigned char **bin); +#endif + +#if defined(WITH_TLS_PSK) || defined(WITH_TLS) int mosquitto__hex2bin(const char *hex, unsigned char *bin, int bin_max_len); #endif diff --git a/man/mosquitto.conf.5.xml b/man/mosquitto.conf.5.xml index b74b750b63..d7cbe9c5a1 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -888,6 +888,17 @@ it. If not specified, pem keys are assumed + + engine_kpass_sha + + SHA1 of private key password when using an SSL engine. + Some SSL engines may require the usage of a password + in order to being accessed, like the TPM engine. Instead + of being prompted for the password, this option allows + a SHA1 hash of the password to be passed on to the + engine directly, without user interaction. + + file path diff --git a/man/mosquitto_pub.1.xml b/man/mosquitto_pub.1.xml index d2ed0771f2..9504c26427 100644 --- a/man/mosquitto_pub.1.xml +++ b/man/mosquitto_pub.1.xml @@ -68,6 +68,7 @@ pem engine + kpass-sha @@ -204,6 +205,18 @@ See also . + + + + SHA1 of private key password when using an SSL engine. + Some SSL engines may require the usage of a password + in order to being accessed, like the TPM engine. Instead + of being prompted for the password, this option allows + a SHA1 hash of the password to be passed on to the + engine directly, without user interaction. + See also . + + diff --git a/man/mosquitto_sub.1.xml b/man/mosquitto_sub.1.xml index bcb2921bb0..a5fd8b0e65 100644 --- a/man/mosquitto_sub.1.xml +++ b/man/mosquitto_sub.1.xml @@ -74,6 +74,7 @@ pem engine + kpass-sha @@ -213,6 +214,18 @@ See also . + + + + SHA1 of private key password when using an SSL engine. + Some SSL engines may require the usage of a password + in order to being accessed, like the TPM engine. Instead + of being prompted for the password, this option allows + a SHA1 hash of the password to be passed on to the + engine directly, without user interaction. + See also . + + diff --git a/src/conf.c b/src/conf.c index 5bfdfa0254..c5420b996e 100644 --- a/src/conf.c +++ b/src/conf.c @@ -299,6 +299,7 @@ void config__cleanup(struct mosquitto__config *config) mosquitto__free(config->listeners[i].crlfile); mosquitto__free(config->listeners[i].tls_version); mosquitto__free(config->listeners[i].tls_engine); + mosquitto__free(config->listeners[i].tls_engine_kpass_sha); #ifdef WITH_WEBSOCKETS if(!config->listeners[i].ws_context) /* libwebsockets frees its own SSL_CTX */ #endif @@ -436,6 +437,7 @@ int config__parse_args(struct mosquitto_db *db, struct mosquitto__config *config || config->default_listener.keyfile || config->default_listener.tls_engine || config->default_listener.tls_keyform != mosq_k_pem + || config->default_listener.tls_engine_kpass_sha || config->default_listener.ciphers || config->default_listener.psk_hint || config->default_listener.require_certificate @@ -488,6 +490,7 @@ int config__parse_args(struct mosquitto_db *db, struct mosquitto__config *config config->listeners[config->listener_count-1].tls_version = config->default_listener.tls_version; config->listeners[config->listener_count-1].tls_engine = config->default_listener.tls_engine; config->listeners[config->listener_count-1].tls_keyform = config->default_listener.tls_keyform; + config->listeners[config->listener_count-1].tls_engine_kpass_sha = config->default_listener.tls_engine_kpass_sha; config->listeners[config->listener_count-1].cafile = config->default_listener.cafile; config->listeners[config->listener_count-1].capath = config->default_listener.capath; config->listeners[config->listener_count-1].certfile = config->default_listener.certfile; @@ -1099,6 +1102,20 @@ int config__read_file_core(struct mosquitto__config *config, bool reload, struct mosquitto__free(keyform); #else log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); +#endif + }else if(!strcmp(token, "tls_engine_kpass_sha")){ +#ifdef WITH_TLS + if(reload) continue; // Listeners not valid for reloading. + char *kpass_sha = NULL, *kpass_sha_bin = NULL; + if(conf__parse_string(&token, "tls_engine_kpass_sha", &kpass_sha, saveptr)) return MOSQ_ERR_INVAL; + if(mosquitto__hex2bin_sha1(kpass_sha, (unsigned char**)&kpass_sha_bin) != MOSQ_ERR_SUCCESS){ + mosquitto__free(kpass_sha); + return MOSQ_ERR_INVAL; + } + cur_listener->tls_engine_kpass_sha = kpass_sha_bin; + mosquitto__free(kpass_sha); +#else + log__printf(NULL, MOSQ_LOG_WARNING, "Warning: TLS support not available."); #endif }else if(!strcmp(token, "ciphers")){ #ifdef WITH_TLS diff --git a/src/mosquitto_broker_internal.h b/src/mosquitto_broker_internal.h index 0ec34efe15..310c429f69 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -223,6 +223,7 @@ struct mosquitto__listener { char *keyfile; char *tls_engine; enum _mosquitto_keyform tls_keyform; + char *tls_engine_kpass_sha; char *ciphers; char *psk_hint; SSL_CTX *ssl_ctx; diff --git a/src/net.c b/src/net.c index 82ff54f7f3..f7466a46fe 100644 --- a/src/net.c +++ b/src/net.c @@ -461,7 +461,23 @@ int net__socket_listen(struct mosquitto__listener *listener) return 1; } if(listener->tls_keyform == mosq_k_engine){ - EVP_PKEY *pkey = ENGINE_load_private_key(engine, listener->keyfile, net__get_ui_method(), NULL); + UI_METHOD *ui_method = net__get_ui_method(); + if(listener->tls_engine_kpass_sha){ + if(!ENGINE_ctrl_cmd(engine, ENGINE_SECRET_MODE, ENGINE_SECRET_MODE_SHA, NULL, NULL, 0)){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to set engine secret mode sha"); + COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); + return 1; + } + if(!ENGINE_ctrl_cmd(engine, ENGINE_PIN, 0, listener->tls_engine_kpass_sha, NULL, 0)){ + log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to set engine pin"); + COMPAT_CLOSE(sock); + ENGINE_FINISH(engine); + return 1; + } + ui_method = NULL; + } + EVP_PKEY *pkey = ENGINE_load_private_key(engine, listener->keyfile, ui_method, NULL); if(!pkey){ log__printf(NULL, MOSQ_LOG_ERR, "Error: Unable to load engine private key file \"%s\".", listener->keyfile); COMPAT_CLOSE(sock);