diff --git a/client/client_shared.c b/client/client_shared.c index f2b8ca60..f34d6688 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){ @@ -949,6 +962,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 3df157f8..031ba40a 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 8c9ced3e..c97c274b 100644 --- a/client/pub_client.c +++ b/client/pub_client.c @@ -223,7 +223,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 @@ -283,6 +283,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 cc56299a..f8519995 100644 --- a/client/sub_client.c +++ b/client/sub_client.c @@ -158,7 +158,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 @@ -225,6 +225,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 2fcef663..345b9d97 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 961c3c92..b29ee947 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 15fb4196..8725bd4c 100644 --- a/lib/linker.version +++ b/lib/linker.version @@ -92,6 +92,7 @@ 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; MOSQ_1.6 { diff --git a/lib/mosquitto.h b/lib/mosquitto.h index 8a99ee90..077a3027 100644 --- a/lib/mosquitto.h +++ b/lib/mosquitto.h @@ -1198,6 +1198,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 cf1bd38c..b89d48bd 100644 --- a/lib/mosquitto_internal.h +++ b/lib/mosquitto_internal.h @@ -199,6 +199,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 b0ec50bb..a28b376a 100644 --- a/lib/net_mosq.c +++ b/lib/net_mosq.c @@ -615,6 +615,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 0fbc95bc..a5676032 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 7ed2a95a..a5c9d796 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 56d8cb05..690d2347 100644 --- a/lib/util_mosq.c +++ b/lib/util_mosq.c @@ -346,7 +346,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 0e65dd98..bec6a2ec 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 a3e27be3..edb839fd 100644 --- a/man/mosquitto.conf.5.xml +++ b/man/mosquitto.conf.5.xml @@ -910,6 +910,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 d2ed0771..9504c264 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 50d778da..7ee78783 100644 --- a/man/mosquitto_sub.1.xml +++ b/man/mosquitto_sub.1.xml @@ -75,6 +75,7 @@ pem engine + kpass-sha @@ -214,6 +215,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 3d7fb89f..454b98b7 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; @@ -1108,6 +1111,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 1ddd1300..2127cead 100644 --- a/src/mosquitto_broker_internal.h +++ b/src/mosquitto_broker_internal.h @@ -226,6 +226,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 acdb0b51..792766b9 100644 --- a/src/net.c +++ b/src/net.c @@ -501,7 +501,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);