diff --git a/components/mosquitto/CMakeLists.txt b/components/mosquitto/CMakeLists.txt
index ccef7144f..89d467ced 100644
--- a/components/mosquitto/CMakeLists.txt
+++ b/components/mosquitto/CMakeLists.txt
@@ -90,6 +90,10 @@ if (CONFIG_MOSQ_ENABLE_SYS)
endif()
target_compile_options(${COMPONENT_LIB} PRIVATE "-Wno-format")
+# Enable linker wrapping for mosquitto_unpwd_check to allow connection callback interception
+# without modifying upstream code
+target_link_options(${COMPONENT_LIB} PRIVATE "-Wl,--wrap=mosquitto_unpwd_check")
+
# Some mosquitto source unconditionally define `_GNU_SOURCE` which collides with IDF build system
# producing warning: "_GNU_SOURCE" redefined
# This workarounds this issue by undefining the macro for the selected files
diff --git a/components/mosquitto/api.md b/components/mosquitto/api.md
index 1e1219559..fb42c2a21 100644
--- a/components/mosquitto/api.md
+++ b/components/mosquitto/api.md
@@ -15,6 +15,7 @@
| Type | Name |
| ---: | :--- |
| struct | [**mosq\_broker\_config**](#struct-mosq_broker_config)
_Mosquitto configuration structure._ |
+| typedef int(\* | [**mosq\_connect\_cb\_t**](#typedef-mosq_connect_cb_t)
|
| typedef void(\* | [**mosq\_message\_cb\_t**](#typedef-mosq_message_cb_t)
|
## Functions
@@ -35,6 +36,8 @@ ESP port of mosquittto supports only the options in this configuration structure
Variables:
+- mosq\_connect\_cb\_t handle_connect_cb
On connect callback. If configured, user function is called whenever a client attempts to connect. The callback receives client\_id, username, password, and password length. Return 0 to accept the connection, non-zero to reject it.
+
- void(\* handle_message_cb
On message callback. If configured, user function is called whenever mosquitto processes a message.
- const char \* host
Address on which the broker is listening for connections
@@ -43,6 +46,12 @@ Variables:
- esp\_tls\_cfg\_server\_t \* tls_cfg
ESP-TLS configuration (if TLS transport used) Please refer to the ESP-TLS official documentation for more details on configuring the TLS options. You can open the respective docs with this idf.py command: `idf.py docs -sp api-reference/protocols/esp_tls.html`
+### typedef `mosq_connect_cb_t`
+
+```c
+typedef int(* mosq_connect_cb_t) (const char *client_id, const char *username, const char *password, int password_len);
+```
+
### typedef `mosq_message_cb_t`
```c
diff --git a/components/mosquitto/port/broker.c b/components/mosquitto/port/broker.c
index f78f65179..d169e221d 100644
--- a/components/mosquitto/port/broker.c
+++ b/components/mosquitto/port/broker.c
@@ -102,6 +102,7 @@ void mosq_broker_stop(void)
}
extern mosq_message_cb_t g_mosq_message_callback;
+extern mosq_connect_cb_t g_mosq_connect_callback;
int mosq_broker_run(struct mosq_broker_config *broker_config)
{
@@ -130,6 +131,9 @@ int mosq_broker_run(struct mosq_broker_config *broker_config)
if (broker_config->handle_message_cb) {
g_mosq_message_callback = broker_config->handle_message_cb;
}
+ if (broker_config->handle_connect_cb) {
+ g_mosq_connect_callback = broker_config->handle_connect_cb;
+ }
db.config = &config;
diff --git a/components/mosquitto/port/callbacks.c b/components/mosquitto/port/callbacks.c
index dc0f2ad1b..8b9306cc0 100644
--- a/components/mosquitto/port/callbacks.c
+++ b/components/mosquitto/port/callbacks.c
@@ -3,8 +3,9 @@
*
* SPDX-License-Identifier: EPL-2.0
*
- * SPDX-FileContributor: 2024 Espressif Systems (Shanghai) CO LTD
+ * SPDX-FileContributor: 2024-2025 Espressif Systems (Shanghai) CO LTD
*/
+#include
#include "mosquitto_internal.h"
#include "mosquitto_broker.h"
#include "memory_mosq.h"
@@ -16,6 +17,7 @@
#include "mosq_broker.h"
mosq_message_cb_t g_mosq_message_callback = NULL;
+mosq_connect_cb_t g_mosq_connect_callback = NULL;
int mosquitto_callback_register(
mosquitto_plugin_id_t *identifier,
@@ -51,3 +53,39 @@ int plugin__handle_message(struct mosquitto *context, struct mosquitto_msg_store
}
return MOSQ_ERR_SUCCESS;
}
+
+int __real_mosquitto_unpwd_check(struct mosquitto *context);
+
+/* Wrapper function to intercept mosquitto_unpwd_check calls via linker wrapping */
+int __wrap_mosquitto_unpwd_check(struct mosquitto *context)
+{
+ int rc;
+ int password_len = 0;
+
+ /* Call user's connect callback if set */
+ if (g_mosq_connect_callback) {
+ /* Extract password length if password is present.
+ * Note: MQTT passwords are binary data, but mosquitto stores them as null-terminated strings.
+ * If password contains null bytes, strlen() will not return the full length.
+ * This matches how mosquitto itself handles passwords in some security functions. */
+ if (context->password) {
+ password_len = (int)strlen(context->password);
+ }
+
+ /* Call user callback */
+ rc = g_mosq_connect_callback(
+ context->id ? context->id : "",
+ context->username ? context->username : NULL,
+ context->password ? context->password : NULL,
+ password_len
+ );
+
+ /* If callback rejects (returns non-zero), return AUTH error immediately */
+ if (rc != 0) {
+ return MOSQ_ERR_AUTH;
+ }
+ }
+
+ /* Call the original function */
+ return __real_mosquitto_unpwd_check(context);
+}
diff --git a/components/mosquitto/port/include/mosq_broker.h b/components/mosquitto/port/include/mosq_broker.h
index 62e7c2829..2e836fcd2 100644
--- a/components/mosquitto/port/include/mosq_broker.h
+++ b/components/mosquitto/port/include/mosq_broker.h
@@ -14,6 +14,8 @@ extern "C" {
struct mosquitto__config;
typedef void (*mosq_message_cb_t)(char *client, char *topic, char *data, int len, int qos, int retain);
+
+typedef int (*mosq_connect_cb_t)(const char *client_id, const char *username, const char *password, int password_len);
/**
* @brief Mosquitto configuration structure
*
@@ -33,6 +35,11 @@ struct mosq_broker_config {
* On message callback. If configured, user function is called
* whenever mosquitto processes a message.
*/
+ mosq_connect_cb_t handle_connect_cb; /*!< On connect callback. If configured, user function is called
+ * whenever a client attempts to connect. The callback receives
+ * client_id, username, password, and password length. Return 0 to
+ * accept the connection, non-zero to reject it.
+ */
};
/**