From b1f3d6268dc4c852ae782b4d8a0a8f493afb7fb5 Mon Sep 17 00:00:00 2001 From: Giuliano Zaro <3684609+GMagician@users.noreply.github.com> Date: Thu, 20 Feb 2025 22:51:52 +0100 Subject: [PATCH] Allow acl in mqtt when client certificate is in use (#22998) * Allow acl in mqtt Acl in mqtt needs user name if 'use_identity_as_username' is not true. It also seems that login in mqtt don't accept an user with an empty password, then reenabled both * Add new define for mosquitto Keep compatibility with AWS_IOT * Better approach to custom client certificate Don't duplicate defined check. Let USE_MQTT_AWS_IOT automatically set USE_MQTT_CLIENT_CERT and use latter in all code * with mosquitto user and password may be empty When mosquitto 'use_identity_as_username' option is enabled password and user are not used --- BUILDS.md | 1 + lib/lib_ssl/tls_mini/src/StackThunk_light.cpp | 2 +- .../src/WiFiClientSecureLightBearSSL.cpp | 8 ++--- tasmota/my_user_config.h | 14 +++++---- tasmota/tasmota_support/settings.ino | 6 ++-- .../tasmota_xdrv_driver/xdrv_02_9_mqtt.ino | 30 ++++++++++--------- 6 files changed, 34 insertions(+), 27 deletions(-) diff --git a/BUILDS.md b/BUILDS.md index f73691be2..40baa4c4f 100644 --- a/BUILDS.md +++ b/BUILDS.md @@ -14,6 +14,7 @@ Note: the `minimal` variant is not listed as it shouldn't be used outside of the | USE_HOME_ASSISTANT | - | - / - | - | - | - | - | | USE_TASMOTA_DISCOVERY | x | x / x | x | x | x | x | | USE_MQTT_TLS\* | - | - / x | - | - | - | - | +| USE_MQTT_CLIENT_CERT | - | - / - | - | - | - | - | | USE_MQTT_AWS_IOT | - | - / - | - | - | - | - | | USE_4K_RSA | - | - / - | - | - | - | - | | USE_TELEGRAM | - | - / - | - | - | - | - | diff --git a/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp b/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp index 098752c88..00799d66c 100644 --- a/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp +++ b/lib/lib_ssl/tls_mini/src/StackThunk_light.cpp @@ -44,7 +44,7 @@ uint32_t *stack_thunk_light_save = NULL; /* Saved A1 while in BearSSL */ uint32_t stack_thunk_light_refcnt = 0; //#define _stackSize (5600/4) -#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT) || defined(USE_MQTT_AZURE_IOT) +#if defined(USE_MQTT_CLIENT_CERT) || defined(USE_MQTT_AWS_IOT_LIGHT) || defined(USE_MQTT_AZURE_IOT) #define _stackSize (5300/4) // using a light version of bearssl we can save 300 bytes #else #define _stackSize (4800/4) // no private key, we can reduce a little, max observed 4300 diff --git a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp index ce7a28270..e21444d84 100755 --- a/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp +++ b/lib/lib_ssl/tls_mini/src/WiFiClientSecureLightBearSSL.cpp @@ -994,16 +994,16 @@ bool WiFiClientSecure_light::_connectSSL(const char* hostName) { br_ssl_engine_set_buffers_bidi(_eng, _iobuf_in.get(), _iobuf_in_size, _iobuf_out.get(), _iobuf_out_size); // ============================================================ - // allocate Private key if needed, only if USE_MQTT_AWS_IOT + // allocate Private key if needed, only if USE_MQTT_CLIENT_CERT LOG_HEAP_SIZE("_connectSSL before PrivKey allocation"); - #ifdef USE_MQTT_AWS_IOT + #if defined(USE_MQTT_CLIENT_CERT) // ============================================================ - // Set the EC Private Key, only USE_MQTT_AWS_IOT + // Set the EC Private Key, only USE_MQTT_CLIENT_CERT // limited to P256 curve br_ssl_client_set_single_ec(_sc.get(), _chain_P, 1, _sk_ec_P, _allowed_usages, _cert_issuer_key_type, &br_ec_p256_m15, br_ecdsa_sign_asn1_get_default()); - #endif // USE_MQTT_AWS_IOT + #endif // USE_MQTT_CLIENT_CERT // ============================================================ // Start TLS connection, ALL diff --git a/tasmota/my_user_config.h b/tasmota/my_user_config.h index 79c9f57e5..c7bb92371 100644 --- a/tasmota/my_user_config.h +++ b/tasmota/my_user_config.h @@ -463,9 +463,10 @@ //#define USE_MQTT_TLS // Use TLS for MQTT connection (+34.5k code, +7.0k mem and +4.8k additional during connection handshake) // #define USE_MQTT_TLS_CA_CERT // [DEPRECATED] Now TLS supports dual mode using SetOption132 - this flag is now ignored // #define USE_MQTT_AWS_IOT_LIGHT // Enable MQTT for AWS IoT in light mode, with user/password instead of private certificate -// #define USE_MQTT_AWS_IOT // [Deprecated] Enable MQTT for AWS IoT - requires a private key (+11.9k code, +0.4k mem) - // Note: you need to generate a private key + certificate per device and update 'tasmota/tasmota_aws_iot.cpp' - // Full documentation here: https://github.com/arendst/Tasmota/wiki/AWS-IoT +// #define USE_MQTT_CLIENT_CERT // Enable MQTT with custom client certificate - requires a private key (+11.9k code, +0.4k mem) +// #define USE_MQTT_AWS_IOT // [Deprecated] Enable MQTT for AWS IoT - it includes USE_MQTT_CLIENT_CERT but it forces no user account/password + // Note: you need to generate a private key + certificate per device and update 'tasmota/tasmota_aws_iot.cpp' + // Full documentation here: https://github.com/arendst/Tasmota/wiki/AWS-IoT // for USE_4K_RSA (support for 4096 bits certificates, instead of 2048), you need to uncommend `-DUSE_4K_RSA` in `build_flags` from `platform.ini` or `platform_override.ini` // -- MQTT - TLS - Azure IoT & IoT Central --------- @@ -1340,6 +1341,9 @@ #ifdef USE_CONFIG_OVERRIDE #include "user_config_override.h" // Configuration overrides for my_user_config.h #endif +#if defined(USE_MQTT_AWS_IOT) && !defined(USE_MQTT_CLIENT_CERT) + #define USE_MQTT_CLIENT_CERT // USE_MQTT_AWS_IOT requires USE_MQTT_CLIENT_CERT +#endif /*********************************************************************************************\ * Post-process obsoletes @@ -1353,8 +1357,8 @@ * Mutual exclude options \*********************************************************************************************/ -#if defined(ESP8266) && defined(USE_DISCOVERY) && (defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT)) - #error "Select either USE_DISCOVERY or USE_MQTT_AWS_IOT, mDNS takes too much code space and is not needed for AWS IoT" +#if defined(ESP8266) && defined(USE_DISCOVERY) && (defined(USE_MQTT_CLIENT_CERT) || defined(USE_MQTT_AWS_IOT_LIGHT)) + #error "Select either USE_DISCOVERY or USE_MQTT_CLIENT_CERT/USE_MQTT_AWS_IOT, mDNS takes too much code space and is not needed for AWS IoT" #endif #if defined(USE_RULES) && defined(USE_SCRIPT) diff --git a/tasmota/tasmota_support/settings.ino b/tasmota/tasmota_support/settings.ino index ff1666bf3..e3d6f2d88 100644 --- a/tasmota/tasmota_support/settings.ino +++ b/tasmota/tasmota_support/settings.ino @@ -1557,7 +1557,7 @@ void SettingsDelta(void) { SettingsUpdateText(SET_STAPWD1, temp41); SettingsUpdateText(SET_STAPWD2, temp42); -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_CLIENT_CERT) if (!strlen(Settings->ex_mqtt_user)) { SettingsUpdateText(SET_MQTT_HOST, temp7); SettingsUpdateText(SET_MQTT_USER, temp9); @@ -1567,10 +1567,10 @@ void SettingsDelta(void) { SettingsUpdateText(SET_MQTT_HOST, aws_mqtt_host); SettingsUpdateText(SET_MQTT_USER, ""); } -#else // No USE_MQTT_TLS and USE_MQTT_AWS_IOT +#else // No USE_MQTT_TLS and USE_MQTT_CLIENT_CERT SettingsUpdateText(SET_MQTT_HOST, temp7); SettingsUpdateText(SET_MQTT_USER, temp9); -#endif // USE_MQTT_TLS and USE_MQTT_AWS_IOT +#endif // USE_MQTT_TLS and USE_MQTT_CLIENT_CERT SettingsUpdateText(SET_MQTT_PWD, temp10); SettingsUpdateText(SET_MQTT_TOPIC, temp11); } diff --git a/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino b/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino index 97ec2a6ee..8bf74e581 100644 --- a/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino +++ b/tasmota/tasmota_xdrv_driver/xdrv_02_9_mqtt.ino @@ -57,7 +57,7 @@ const char kMqttCommands[] PROGMEM = "|" // No prefix D_CMND_MQTTFINGERPRINT "|" #endif D_CMND_MQTTUSER "|" D_CMND_MQTTPASSWORD "|" D_CMND_MQTTKEEPALIVE "|" D_CMND_MQTTTIMEOUT "|" D_CMND_MQTTWIFITIMEOUT "|" -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_CLIENT_CERT) D_CMND_TLSKEY "|" #endif #ifdef USE_MQTT_FILE @@ -84,7 +84,7 @@ void (* const MqttCommand[])(void) PROGMEM = { &CmndMqttFingerprint, #endif &CmndMqttUser, &CmndMqttPassword, &CmndMqttKeepAlive, &CmndMqttTimeout, &CmndMqttWifiTimeout, -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_CLIENT_CERT) &CmndTlsKey, #endif #ifdef USE_MQTT_FILE @@ -111,7 +111,7 @@ struct MQTT { #ifdef USE_MQTT_TLS // This part of code is necessary to store Private Key and Cert in Flash -#ifdef USE_MQTT_AWS_IOT +#if defined(USE_MQTT_CLIENT_CERT) #include const br_ec_private_key *AWS_IoT_Private_Key = nullptr; @@ -134,7 +134,7 @@ public: tls_dir_t tls_dir; // memory copy of tls_dir from flash -#endif // USE_MQTT_AWS_IOT +#endif // USE_MQTT_CLIENT_CERT // check whether the fingerprint is filled with a single value // Filled with 0x00 = accept any fingerprint and learn it for next time @@ -250,7 +250,7 @@ void MqttInit(void) { static const char * alpn_mqtt = "mqtt"; // needs to be static tlsClient->setALPN(&alpn_mqtt, 1); // need to set alpn to 'mqtt' for AWS IoT } -#ifdef USE_MQTT_AWS_IOT +#if defined(USE_MQTT_CLIENT_CERT) loadTlsDir(); // load key and certificate data from Flash if ((nullptr != AWS_IoT_Private_Key) && (nullptr != AWS_IoT_Client_Certificate)) { tlsClient->setClientECCert(AWS_IoT_Client_Certificate, @@ -818,7 +818,7 @@ void MqttPublishPayloadPrefixTopic_P(uint32_t prefix, const char* subtopic, cons free(romram); // Free 16k heap from 64 bytes MqttPublishPayload(stopic, payload, binary_length, retained); -#if defined(USE_MQTT_AWS_IOT) || defined(USE_MQTT_AWS_IOT_LIGHT) +#if defined(USE_MQTT_CLIENT_CERT) || defined(USE_MQTT_AWS_IOT_LIGHT) if ((prefix > 0) && (Settings->flag4.awsiot_shadow) && (Mqtt.connected)) { // placeholder for SetOptionXX // compute the target topic char *topic = SettingsText(SET_MQTT_TOPIC); @@ -844,7 +844,7 @@ void MqttPublishPayloadPrefixTopic_P(uint32_t prefix, const char* subtopic, cons AddLog(LOG_LEVEL_DEBUG, PSTR(D_LOG_MQTT "Updated shadow: %s"), romram); yield(); // #3313 } -#endif // USE_MQTT_AWS_IOT +#endif // USE_MQTT_CLIENT_CERT } void MqttPublishPayloadPrefixTopic_P(uint32_t prefix, const char* subtopic, const char* payload, uint32_t binary_length) { @@ -1193,19 +1193,21 @@ void MqttReconnect(void) { MqttClient.setClient(EspClient); MqttNonTLSWarning(); } -#ifdef USE_MQTT_AWS_IOT - // re-assign private keys in case it was updated in between +#if defined(USE_MQTT_CLIENT_CERT) + // re-assign private key in case it was updated in between if (Mqtt.mqtt_tls) { if ((nullptr != AWS_IoT_Private_Key) && (nullptr != AWS_IoT_Client_Certificate)) { - // if private key is there, we remove user/pwd - mqtt_user = nullptr; - mqtt_pwd = nullptr; + #ifdef USE_MQTT_AWS_IOT + // if private key is there, we remove user/pwd + mqtt_user = nullptr; + mqtt_pwd = nullptr; + #endif tlsClient->setClientECCert(AWS_IoT_Client_Certificate, AWS_IoT_Private_Key, 0xFFFF /* all usages, don't care */, 0); } } -#endif // USE_MQTT_AWS_IOT +#endif // USE_MQTT_CLIENT_CERT #ifdef USE_MQTT_AZURE_IOT String azureMqtt_password = SettingsText(SET_MQTT_PWD); if (azureMqtt_password.indexOf("SharedAccessSignature") == -1) { @@ -1805,7 +1807,7 @@ void CmndStatusRetain(void) { /*********************************************************************************************\ * TLS private key and certificate - store into Flash \*********************************************************************************************/ -#if defined(USE_MQTT_TLS) && defined(USE_MQTT_AWS_IOT) +#if defined(USE_MQTT_TLS) && defined(USE_MQTT_CLIENT_CERT) #ifdef ESP32 static uint8_t * tls_spi_start = nullptr;