From 81852e5ba1fa6983df44f591c40154ea5fcef6b8 Mon Sep 17 00:00:00 2001 From: Thomas Weber Date: Fri, 3 Jan 2020 17:05:14 +0100 Subject: [PATCH] mqtt: add tls support --- src/modules/mqtt/doc/mqtt_admin.xml | 176 +++++++++++++++++++++++++++- src/modules/mqtt/mqtt_dispatch.c | 31 ++++- src/modules/mqtt/mqtt_dispatch.h | 11 +- src/modules/mqtt/mqtt_mod.c | 71 +++++++---- 4 files changed, 260 insertions(+), 29 deletions(-) diff --git a/src/modules/mqtt/doc/mqtt_admin.xml b/src/modules/mqtt/doc/mqtt_admin.xml index e6eb7ab895e..791daa5920d 100644 --- a/src/modules/mqtt/doc/mqtt_admin.xml +++ b/src/modules/mqtt/doc/mqtt_admin.xml @@ -202,7 +202,7 @@ modparam("mqtt", "will_topic", "kamailio123") ... modparam("mqtt", "will", "gone") ... - +
@@ -243,6 +243,180 @@ end
+
+ <varname>ca_file</varname> (str) + + Path to a file containing the PEM encoded trusted CA certificate files. + + + + Default value is NULL. + Set either this parameter or ca_path if you want to connect via TLS. + + + + Set <varname>ca_file</varname> parameter + +... +modparam("mqtt", "ca_file", "/etc/ssl/certs/myca.pem") +... + + +
+
+ <varname>ca_path</varname> (str) + + Used to define a directory that contains PEM encoded CA certificates + that are trusted. For ca_path to work correctly, the certificates files must have ".pem" as the + file ending and you must run "openssl rehash /your/ca_path" each time you add/remove a certificate. + + + + Default value is NULL. + Set either this parameter or ca_file if you want to connect via TLS. + ca_file and ca_path are mutual exclusive. + + + + Set <varname>ca_path</varname> parameter + +... +modparam("mqtt", "ca_path", "/etc/ssl/certs") +... + + +
+
+ <varname>tls_method</varname> (str) + + The version of the SSL/TLS protocol to use as a string. + If NULL, the default value is used. + The default value and the available values depend on the version of openssl that libmosquitto was compiled against. + + + Possible values: + + + + + tlsv1.3 is available with openssl >= 1.1.1 together with libmosquitto v1.6.8 and newer. + + + + + For openssl >= 1.0.1, the available options are tlsv1.2, tlsv1.1 and tlsv1, with tlv1.2 as the default. + + + + + For openssl < 1.0.1, only tlsv1 is available. + + + + + + + Default value is NULL. + + + + Set <varname>tls_method</varname> parameter + +... +modparam("mqtt", "tls_method", "tlsv1.3") +... + + +
+
+ <varname>certificate</varname> (str) + + Path to a file containing the PEM encoded certificate file for a TLS client connection. + + + + Default value is NULL. + If NULL, private_key must also be NULL and no client certificate will be used. + + + + Set <varname>certificate</varname> parameter + +... +modparam("mqtt", "certificate", "/etc/ssl/certs/myclient.pem") +... + + +
+
+ <varname>private_key</varname> (str) + + Path to a file containing the PEM encoded private key for a TLS client connection. + + + + Default value is NULL. + If NULL, certificate must also be NULL and no client certificate will be used. + + + + Set <varname>private_key</varname> parameter + +... +modparam("mqtt", "private_key", "/etc/ssl/private/myclient.key") +... + + +
+
+ <varname>cipher_list</varname> (str) + + A string describing the ciphers available for use. See the + cipher(1) OpenSSL man page. + If NULL, the libssl default ciphers will be used. + + + + Default value is NULL. + + + + Set <varname>cipher_list</varname> parameter + +... +modparam("mqtt", "cipher_list", "HIGH") +... + + +
+
+ <varname>verify_certificate</varname> (str) + + Configure verification of the server certificate. + If value is set to 0, it is impossible to guarantee that the host you are connecting to is not + impersonating your server. + + + This can be useful in initial server testing, but makes it possible for a + malicious third party to impersonate your server through DNS spoofing, for example. + + + Do not disable verification in a real system as it makes the connection encryption pointless. + + + + Default value is 1. + + + + Set <varname>verify_certificate</varname> parameter + +... +modparam("mqtt", "verify_certificate", "0") +... + + +
diff --git a/src/modules/mqtt/mqtt_dispatch.c b/src/modules/mqtt/mqtt_dispatch.c index a4cbdf2e9fa..7a39ede29b7 100644 --- a/src/modules/mqtt/mqtt_dispatch.c +++ b/src/modules/mqtt/mqtt_dispatch.c @@ -145,7 +145,7 @@ void mqtt_close_notify_sockets_parent(void) */ int mqtt_run_dispatcher(mqtt_dispatcher_cfg_t* cfg) { - int res; + int res, cert_req; struct ev_io request_notify; // prepare and init libmosquitto handle @@ -200,6 +200,32 @@ int mqtt_run_dispatcher(mqtt_dispatcher_cfg_t* cfg) ev_timer_init(&timer_notify, mqtt_timer_notify, _mqtt_timer_freq, 0.); ev_timer_start(loop, &timer_notify); + + // prepare tls configuration if at least a ca is configured + if (cfg->ca_file != NULL || cfg->ca_path != NULL) { + LM_DBG("Preparing TLS connection"); + if (cfg->verify_certificate == 0) { + cert_req = 0; + } else if (cfg->verify_certificate == 1) { + cert_req = 1; + } else { + LM_ERR("invalid verify_certificate parameter\n"); + return -1; + } + res = mosquitto_tls_opts_set(_mosquitto, cert_req, cfg->tls_method, cfg->cipher_list); + if (res != MOSQ_ERR_SUCCESS) { + LM_ERR("invalid tls_method or cipher_list parameters\n"); + LM_ERR("mosquitto_tls_opts_set() failed: %d %s\n",errno, strerror(errno)); + return -1; + } + res = mosquitto_tls_set(_mosquitto, cfg->ca_file, cfg->ca_path, cfg->certificate, cfg->private_key, NULL); + if (res != MOSQ_ERR_SUCCESS) { + LM_ERR("invalid ca_file, ca_path, certificate or private_key parameters\n"); + LM_ERR("mosquitto_tls_set() failed: %d %s\n",errno, strerror(errno)); + return -1; + } + } + res = mosquitto_connect(_mosquitto, cfg->host, cfg->port, cfg->keepalive); if (res == MOSQ_ERR_INVAL) { LM_ERR("invalid connect parameters\n"); @@ -269,6 +295,9 @@ void mqtt_timer_notify(struct ev_loop *loop, ev_timer *timer, int revents) wait_ticks = _reconnect_wait_ticks; } break; + case MOSQ_ERR_TLS: + LM_ERR("mosquitto_loop() failed, tls error\n"); + break; default: LM_ERR("mosquitto_loop() failed: case %i\n", res); break; diff --git a/src/modules/mqtt/mqtt_dispatch.h b/src/modules/mqtt/mqtt_dispatch.h index 1a4ba4619f9..a7f109df63d 100644 --- a/src/modules/mqtt/mqtt_dispatch.h +++ b/src/modules/mqtt/mqtt_dispatch.h @@ -29,13 +29,20 @@ void mqtt_init_environment(); typedef struct mqtt_dispatcher_cfg { char *host; - int port; + int port; char *id; char *username; char *password; - int keepalive; + int keepalive; char *will_topic; char *will; + char *ca_file; + char *ca_path; + char *certificate; + char *private_key; + char *tls_method; + int verify_certificate; + char *cipher_list; } mqtt_dispatcher_cfg_t; int mqtt_init_notify_sockets(void); diff --git a/src/modules/mqtt/mqtt_mod.c b/src/modules/mqtt/mqtt_mod.c index db53c809d7e..0dad3a9dbf9 100644 --- a/src/modules/mqtt/mqtt_mod.c +++ b/src/modules/mqtt/mqtt_mod.c @@ -31,14 +31,21 @@ MODULE_VERSION -static char *_mqtt_host = NULL; -static int _mqtt_port = 1883; -static char *_mqtt_id = NULL; -static char *_mqtt_username = NULL; -static char *_mqtt_password = NULL; -static char *_mqtt_will = NULL; -static char *_mqtt_willtopic = NULL; -static int _mqtt_keepalive = 5; +static char *_mqtt_host = NULL; +static int _mqtt_port = 1883; +static char *_mqtt_id = NULL; +static char *_mqtt_username = NULL; +static char *_mqtt_password = NULL; +static char *_mqtt_will = NULL; +static char *_mqtt_willtopic = NULL; +static int _mqtt_keepalive = 5; +static char *_mqtt_ca_file = NULL; +static char *_mqtt_ca_path = NULL; +static char *_mqtt_certificate = NULL; +static char *_mqtt_private_key = NULL; +static char *_mqtt_tls_method = NULL; +static int _mqtt_verify_certificate = 1; +static char *_mqtt_cipher_list = NULL; str _mqtt_event_callback = STR_NULL; int _mqtt_dispatcher_pid = -1; @@ -68,15 +75,22 @@ static cmd_export_t cmds[]={ }; static param_export_t params[]={ - {"host", PARAM_STRING, &_mqtt_host}, - {"port", INT_PARAM, &_mqtt_port}, - {"id", PARAM_STRING, &_mqtt_id}, - {"username", PARAM_STRING, &_mqtt_username}, - {"password", PARAM_STRING, &_mqtt_password}, - {"will_topic", PARAM_STRING, &_mqtt_willtopic}, - {"will", PARAM_STRING, &_mqtt_will}, - {"keepalive", INT_PARAM, &_mqtt_keepalive}, - {"event_callback", PARAM_STR, &_mqtt_event_callback}, + {"host", PARAM_STRING, &_mqtt_host}, + {"port", INT_PARAM, &_mqtt_port}, + {"id", PARAM_STRING, &_mqtt_id}, + {"username", PARAM_STRING, &_mqtt_username}, + {"password", PARAM_STRING, &_mqtt_password}, + {"will_topic", PARAM_STRING, &_mqtt_willtopic}, + {"will", PARAM_STRING, &_mqtt_will}, + {"keepalive", INT_PARAM, &_mqtt_keepalive}, + {"event_callback", PARAM_STR, &_mqtt_event_callback}, + {"tls_method", PARAM_STRING, &_mqtt_tls_method}, + {"ca_file", PARAM_STRING, &_mqtt_ca_file}, + {"ca_path", PARAM_STRING, &_mqtt_ca_path}, + {"certificate", PARAM_STRING, &_mqtt_certificate}, + {"private_key", PARAM_STRING, &_mqtt_private_key}, + {"verify_certificate", INT_PARAM, &_mqtt_verify_certificate}, + {"cipher_list", PARAM_STRING, &_mqtt_cipher_list}, {0,0,0} }; @@ -171,14 +185,21 @@ static int child_init(int rank) /* module parameter hand over to dispatcher */ mqtt_dispatcher_cfg_t cfg; - cfg.host = _mqtt_host; - cfg.port = _mqtt_port; - cfg.id = _mqtt_id; - cfg.username = _mqtt_username; - cfg.password = _mqtt_password; - cfg.keepalive = _mqtt_keepalive; - cfg.will = _mqtt_will; - cfg.will_topic = _mqtt_willtopic; + cfg.host = _mqtt_host; + cfg.port = _mqtt_port; + cfg.id = _mqtt_id; + cfg.username = _mqtt_username; + cfg.password = _mqtt_password; + cfg.keepalive = _mqtt_keepalive; + cfg.will = _mqtt_will; + cfg.will_topic = _mqtt_willtopic; + cfg.ca_file = _mqtt_ca_file; + cfg.ca_path = _mqtt_ca_path; + cfg.certificate = _mqtt_certificate; + cfg.private_key = _mqtt_private_key; + cfg.tls_method = _mqtt_tls_method; + cfg.verify_certificate = _mqtt_verify_certificate; + cfg.cipher_list = _mqtt_cipher_list; /* this process becomes the dispatcher, block now */ return mqtt_run_dispatcher(&cfg);