diff --git a/README.md b/README.md index 0132182..c8dc5bb 100644 --- a/README.md +++ b/README.md @@ -63,6 +63,7 @@ MQTT broker related command: - set broker_user _unsername_: sets the username for authentication of MQTT clients ("none" if no auth, default) - set broker_password _password_: sets the password for authentication of MQTT clients ("none" if empty, default) +- set broker_access _mode_: controls the networks that allow MQTT broker access (0: no access, 1: only internal, 2: only external, 3: both (default)) - set broker_subscriptions _max_: sets the max number of subscription the broker can store (default: 30) - set broker_retained_messages _max_: sets the max number of retained messages the broker can store (default: 30) - set script_logging [0|1]: switches logging of script execution on or off (not permanently stored in the configuration) @@ -324,7 +325,8 @@ Your code can locally interact with the broker using the functions: bool MQTT_local_publish(uint8_t* topic, uint8_t* data, uint16_t data_length, uint8_t qos, uint8_t retain); bool MQTT_local_subscribe(uint8_t* topic, uint8_t qos); bool MQTT_local_unsubscribe(uint8_t* topic); -void MQTT_local_onData(MqttDataCallback dataCb); + +void MQTT_server_onData(MqttDataCallback dataCb); ``` With these functions you can publish and subscribe topics as a local client like you would with a remote MQTT broker. @@ -332,9 +334,15 @@ With these functions you can publish and subscribe topics as a local client like Username/password authentication is provided with the following interface: ```c -typedef bool (*MqttAuthCallback)(const char* username, const char *password); +typedef bool (*MqttAuthCallback)(const char* username, const char *password, struct espconn *pesp_conn); void MQTT_server_onAuth(MqttAuthCallback authCb); + +typedef bool (*MqttConnectCallback)(struct espconn *pesp_conn); + +void MQTT_server_onConnect(MqttConnectCallback connectCb); ``` -If an *MqttAuthCallback* function is provided, it is called on each connect request. Based on username and password the function has to return *true* for authenticated or *false* for rejected. If a request provides no username and or password the strings are empty. If no *MqttAuthCallback* function is set, each request will be admitted. +If an *MqttAuthCallback* function is registered with MQTT_server_onAuth(), it is called on each connect request. Based on username, password, and optionally the connection info (e.g. the IP address) the function has to return *true* for authenticated or *false* for rejected. If a request provides no username and/or password these parameter strings are empty. If no *MqttAuthCallback* function is set, each request will be admitted. + +The *MqttConnectCallback* function does a similar check for the connection, but it is called right after the connect request before any internal status is allocated. This is done in order to reject requests from unautorized clients in an early stage. diff --git a/firmware/0x00000.bin b/firmware/0x00000.bin index fd2dd22..656742b 100644 Binary files a/firmware/0x00000.bin and b/firmware/0x00000.bin differ diff --git a/firmware/0x10000.bin b/firmware/0x10000.bin index 60a565c..ecd249c 100644 Binary files a/firmware/0x10000.bin and b/firmware/0x10000.bin differ diff --git a/mqtt/include/mqtt_server.h b/mqtt/include/mqtt_server.h index 145202a..16f7122 100644 --- a/mqtt/include/mqtt_server.h +++ b/mqtt/include/mqtt_server.h @@ -14,7 +14,8 @@ #define LOCAL_MQTT_CLIENT ((void*)-1) -typedef bool (*MqttAuthCallback)(const char* username, const char *password); +typedef bool (*MqttAuthCallback)(const char* username, const char *password, struct espconn *pesp_conn); +typedef bool (*MqttConnectCallback)(struct espconn *pesp_conn); typedef struct _MQTT_ClientCon { struct espconn *pCon; @@ -40,11 +41,12 @@ typedef struct _MQTT_ClientCon { extern MQTT_ClientCon *clientcon_list; bool MQTT_server_start(uint16_t portno, uint16_t max_subscriptions, uint16_t max_retained_topics); +void MQTT_server_onConnect(MqttConnectCallback connectCb); void MQTT_server_onAuth(MqttAuthCallback authCb); +void MQTT_server_onData(MqttDataCallback dataCb); bool MQTT_local_publish(uint8_t* topic, uint8_t* data, uint16_t data_length, uint8_t qos, uint8_t retain); bool MQTT_local_subscribe(uint8_t* topic, uint8_t qos); bool MQTT_local_unsubscribe(uint8_t* topic); -void MQTT_local_onData(MqttDataCallback dataCb); #endif /* _MQTT_SERVER_H_ */ diff --git a/mqtt/mqtt_server.c b/mqtt/mqtt_server.c index 05db432..f01ad2b 100644 --- a/mqtt/mqtt_server.c +++ b/mqtt/mqtt_server.c @@ -36,6 +36,7 @@ LOCAL uint8_t zero_len_id[2] = { 0, 0 }; MQTT_ClientCon *clientcon_list; LOCAL MqttDataCallback local_data_cb = NULL; +LOCAL MqttConnectCallback local_connect_cb = NULL; LOCAL MqttAuthCallback local_auth_cb = NULL; //#define MQTT_INFO os_printf @@ -505,7 +506,8 @@ static void ICACHE_FLASH_ATTR MQTT_ClientCon_recv_cb(void *arg, char *pdata, uns // Check Auth if ((local_auth_cb != NULL) && local_auth_cb(clientcon->connect_info.username==NULL?"":clientcon->connect_info.username, - clientcon->connect_info.password==NULL?"":clientcon->connect_info.password) == false) { + clientcon->connect_info.password==NULL?"":clientcon->connect_info.password, + clientcon->pCon) == false) { MQTT_WARNING("MQTT: Authorization failed\r\n"); msg_conn_ret = CONNECTION_REFUSE_NOT_AUTHORIZED; clientcon->connState = TCP_DISCONNECTING; @@ -763,9 +765,16 @@ static void ICACHE_FLASH_ATTR MQTT_ClientCon_sent_cb(void *arg) { static void ICACHE_FLASH_ATTR MQTT_ClientCon_connected_cb(void *arg) { struct espconn *pespconn = (struct espconn *)arg; MQTT_ClientCon *mqttClientCon; + pespconn->reverse = NULL; MQTT_INFO("MQTT_ClientCon_connected_cb(): Client connected\r\n"); + if (local_connect_cb != NULL && local_connect_cb(pespconn) == false) { + MQTT_INFO("Connected not allowed\r\n"); + espconn_disconnect(pespconn); + return; + } + espconn_regist_sentcb(pespconn, MQTT_ClientCon_sent_cb); espconn_regist_disconcb(pespconn, MQTT_ClientCon_discon_cb); espconn_regist_recvcb(pespconn, MQTT_ClientCon_recv_cb); @@ -873,10 +882,14 @@ bool ICACHE_FLASH_ATTR MQTT_local_unsubscribe(uint8_t * topic) { return delete_topic(LOCAL_MQTT_CLIENT, topic); } -void ICACHE_FLASH_ATTR MQTT_local_onData(MqttDataCallback dataCb) { +void ICACHE_FLASH_ATTR MQTT_server_onData(MqttDataCallback dataCb) { local_data_cb = dataCb; } +void ICACHE_FLASH_ATTR MQTT_server_onConnect(MqttConnectCallback connectCb) { + local_connect_cb = connectCb; +} + void ICACHE_FLASH_ATTR MQTT_server_onAuth(MqttAuthCallback authCb) { local_auth_cb = authCb; } diff --git a/user/config_flash.c b/user/config_flash.c index b46801e..38298b1 100644 --- a/user/config_flash.c +++ b/user/config_flash.c @@ -41,6 +41,7 @@ void config_load_default(sysconfig_p config) { config->max_retained_messages = 30; os_sprintf(config->mqtt_broker_user, "%s", "none"); config->mqtt_broker_password[0] = 0; + config->mqtt_broker_access = LOCAL_ACCESS | REMOTE_ACCESS; #ifdef MQTT_CLIENT os_sprintf(config->mqtt_host, "%s", "none"); diff --git a/user/config_flash.h b/user/config_flash.h index 16fedf9..6ed5bba 100644 --- a/user/config_flash.h +++ b/user/config_flash.h @@ -53,6 +53,7 @@ typedef struct uint16_t max_retained_messages; // Upper limit of stored retained messages uint8_t mqtt_broker_user[32]; // Username for client login, "none" if empty uint8_t mqtt_broker_password[32]; // Password for client login + uint8_t mqtt_broker_access; // Controls the interfaces that allow MQTT access (default LOCAL_ACCESS | REMOTE_ACCESS) #ifdef MQTT_CLIENT uint8_t mqtt_host[32]; // IP or hostname of the MQTT broker, "none" if empty diff --git a/user/user_main.c b/user/user_main.c index d0845e7..f0a9809 100644 --- a/user/user_main.c +++ b/user/user_main.c @@ -368,7 +368,7 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) { if (strcmp(tokens[0], "help") == 0) { os_sprintf(response, - "show [config|stats|mqtt|script]\r\n|set [ssid|password|auto_connect|ap_ssid|ap_password|network|dns|ip|netmask|gw|ap_on|ap_open|speed|config_port|config_access|broker_subscriptions|broker_retained_messages|broker_user|broker_password] \r\n|quit|save [config]|reset [factory]|lock []|unlock "); + "show [config|stats|mqtt|script]\r\n|set [ssid|password|auto_connect|ap_ssid|ap_password|network|dns|ip|netmask|gw|ap_on|ap_open|speed|config_port|config_access|broker_subscriptions|broker_retained_messages|broker_user|broker_password|broker_access] \r\n|quit|save [config]|reset [factory]|lock []|unlock "); to_console(response); #ifdef SCRIPTED os_sprintf(response, "|script "); @@ -441,6 +441,14 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) { config.locked ? "***" : (char *)config.mqtt_broker_password); to_console(response); } + response[0] = '\0'; + if (config.mqtt_broker_access == LOCAL_ACCESS) + os_sprintf(response, "MQTT broker: local access only\r\n"); + if (config.mqtt_broker_access == REMOTE_ACCESS) + os_sprintf(response, "MQTT broker: remote access only\r\n"); + if (config.mqtt_broker_access == 0) + os_sprintf(response, "MQTT broker: no access!!\r\n"); + to_console(response); #ifdef MQTT_CLIENT os_sprintf(response, "MQTT client %s\r\n", mqtt_enabled ? "enabled" : "disabled"); to_console(response); @@ -898,7 +906,7 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) { if (config.config_access == 0) os_sprintf(response, "WARNING: if you save this, remote console access will be disabled!\r\n"); else - os_sprintf(response, "Config access set\r\n", config.config_port); + os_sprintf(response, "Config access set\r\n"); goto command_handled; } #endif @@ -931,6 +939,12 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) { os_sprintf(response, "Broker password set\r\n"); goto command_handled; } + + if (strcmp(tokens[1], "broker_access") == 0) { + config.mqtt_broker_access = atoi(tokens[2]) & (LOCAL_ACCESS | REMOTE_ACCESS); + os_sprintf(response, "Broker access set\r\n", config.config_port); + goto command_handled; + } #ifdef SCRIPTED if (strcmp(tokens[1], "script_logging") == 0) { lang_logging = atoi(tokens[2]); @@ -1359,7 +1373,9 @@ void ICACHE_FLASH_ATTR user_set_station_config(void) { } -bool ICACHE_FLASH_ATTR mqtt_broker_auth(const char* username, const char *password) { +bool ICACHE_FLASH_ATTR mqtt_broker_auth(const char* username, const char *password, struct espconn *pesp_conn) { + //os_printf("connect from " IPSTR "\r\n", IP2STR((ip_addr_t *)&(pesp_conn->proto.tcp->remote_ip))); + if (os_strcmp(config.mqtt_broker_user, "none") == 0) return true; @@ -1372,6 +1388,19 @@ bool ICACHE_FLASH_ATTR mqtt_broker_auth(const char* username, const char *passwo } +bool ICACHE_FLASH_ATTR mqtt_broker_connect(struct espconn *pesp_conn) { + //os_printf("connect from " IPSTR "\r\n", IP2STR((ip_addr_t *)&(pesp_conn->proto.tcp->remote_ip))); + + if (!check_connection_access(pesp_conn, config.mqtt_broker_access)) { + os_printf("Client disconnected - no mqtt access from the address " IPSTR "\r\n", + IP2STR((ip_addr_t *)&(pesp_conn->proto.tcp->remote_ip))); + return false; + } + + return true; +} + + void user_init() { struct ip_info info; @@ -1486,7 +1515,8 @@ void user_init() { espconn_tcp_set_max_con(10); os_printf("Max number of TCP clients: %d\r\n", espconn_tcp_get_max_con()); - MQTT_local_onData(MQTT_local_DataCallback); + MQTT_server_onData(MQTT_local_DataCallback); + MQTT_server_onConnect(mqtt_broker_connect); MQTT_server_onAuth(mqtt_broker_auth); MQTT_server_start(1883 /*port */ , config.max_subscriptions,