user/password auth added

pull/16/head
Martin Ger 2017-08-16 11:04:31 +02:00
rodzic 10700b7b94
commit 171b75c65c
9 zmienionych plików z 154 dodań i 23 usunięć

Wyświetl plik

@ -36,6 +36,8 @@ Basic commands (enough to get it working in nearly all environments):
- set [ap_ssid|ap_password] _value_: changes the settings for the soft-AP of the ESP (for your stations)
- show [config|stats|script|mqtt]: prints the current config or some status information and statistics
- save: saves the current config parameters to flash
- 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)
- lock [_password_]: saves and locks the current config, changes are not allowed. Password can be left open if already set before
- unlock _password_: unlocks the config, requires password from the lock command
- reset [factory]: resets the esp, 'factory' optionally resets WiFi params to default values (works on a locked device only from serial console)
@ -70,7 +72,7 @@ By default the "remote" MQTT client is disabled. It can be enabled by setting th
# Scripting
The esp_uMQTT_broker comes with a build-in scripting engine. A script enables the ESP not just to act as a passive broker but to react on events (publications and timing events) and to send out its own items.
Here is a demo of a script to give you an idea of the power of the scripting feature. This script controls a Sonoff switch module. It connects to a remote MQTT broker and in parallel offers locally its own. On both brokers it subscribes to a topic named '/martinshome/switch/1/command', where it receives commands, and it publishes the topic '/martinshome/switch/1/status' with the current state of the switch relay. It understands the commands 'on','off', 'toggle', and 'blink'. Blinking is realized via a timer event. Local status is stored in the two variables $1 (switch state) and $2 (blinking on/off). The 'on gpio_interrupt' clause reacts on pressing the pushbutton of the Sonnoff and simply toggle the switch (and stops blinking). The last two 'on clock' clauses implement a daily on and off period:
Here is a demo of a script to give you an idea of the power of the scripting feature. This script controls a Sonoff switch module. It connects to a remote MQTT broker and in parallel offers locally its own. On both brokers it subscribes to a topic named '/martinshome/switch/1/command', where it receives commands, and it publishes the topic '/martinshome/switch/1/status' with the current state of the switch relay. It understands the commands 'on','off', 'toggle', and 'blink'. Blinking is realized via a timer event. Local status is stored in the two variables $1 (switch state) and $2 (blinking on/off). The 'on gpio_interrupt' clause reacts on pressing the pushbutton of the Sonnoff and simply toggles the switch (and stops blinking). The last two 'on clock' clauses implement a daily on and off period:
```
% Config params, overwrite any previous settings from the commandline
@ -78,6 +80,8 @@ config ap_ssid MyAP
config ap_password stupidPassword
config ntp_server 1.pool.ntp.org
config mqtt_host 192.168.1.20
config broker_user Martin
config broker_password secret
% Now the initialization, this is done once after booting
on init
@ -143,7 +147,7 @@ do
publish local /martinshome/switch/1/status $1 retained
publish remote /martinshome/switch/1/status $1 retained
% The local pushbotton
% The local pushbutton
on gpio_interrupt 0 pullup
do
println "New state GPIO 0: " | $this_gpio
@ -263,10 +267,9 @@ The broker does support:
- retained messages
- LWT
- QoS level 0
- a subset of MQTT (CONNECT, DISCONNECT, SUBSCRIBE, UNSUBSCRIBE, PUBLISH, PING)
- username/password authentication
The broker does not yet support:
- username, password authentication
- QoS levels other than 0
- many TCP(=MQTT) clients
- non-clear sessions
@ -294,3 +297,12 @@ void MQTT_local_onData(MqttDataCallback dataCb);
With these functions you can publish and subscribe topics as a local client like you would with a remote MQTT broker.
Username/password authentication is provided with the following interface:
```c
typedef bool (*MqttAuthCallback)(const char* username, const char *password);
void MQTT_server_onAuth(MqttAuthCallback authCb);
```
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. No provided username/password are empty strings. If no *MqttAuthCallback* function is set, each request will be admitted.

Plik binarny nie jest wyświetlany.

Plik binarny nie jest wyświetlany.

Wyświetl plik

@ -14,6 +14,8 @@
#define LOCAL_MQTT_CLIENT ((void*)-1)
typedef bool (*MqttAuthCallback)(const char* username, const char *password);
typedef struct _MQTT_ClientCon {
struct espconn *pCon;
// uint8_t security;
@ -38,6 +40,7 @@ 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_onAuth(MqttAuthCallback authCb);
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);

Wyświetl plik

@ -36,6 +36,7 @@ LOCAL uint8_t zero_len_id[2] = { 0, 0 };
MQTT_ClientCon *clientcon_list;
LOCAL MqttDataCallback local_data_cb = NULL;
LOCAL MqttAuthCallback local_auth_cb = NULL;
//#define MQTT_INFO os_printf
#define MQTT_WARNING os_printf
@ -335,22 +336,18 @@ static void ICACHE_FLASH_ATTR MQTT_ClientCon_recv_cb(void *arg, char *pdata, uns
}
}
uint16_t msg_used_len = var_header_len;
MQTT_INFO("MQTT: Connect flags %x\r\n", variable_header->flags);
clientcon->connect_info.clean_session = (variable_header->flags & MQTT_CONNECT_FLAG_CLEAN_SESSION) != 0;
if ((variable_header->flags & MQTT_CONNECT_FLAG_USERNAME) != 0 ||
(variable_header->flags & MQTT_CONNECT_FLAG_PASSWORD) != 0) {
MQTT_WARNING("MQTT: Connect option currently not supported\r\n");
msg_conn_ret = CONNECTION_REFUSE_NOT_AUTHORIZED;
clientcon->connState = TCP_DISCONNECTING;
break;
}
clientcon->connect_info.keepalive = (variable_header->keepaliveMsb << 8) + variable_header->keepaliveLsb;
espconn_regist_time(clientcon->pCon, 2 * clientcon->connect_info.keepalive, 1);
MQTT_INFO("MQTT: Keepalive %d\r\n", clientcon->connect_info.keepalive);
// Get the client id
uint16_t id_len = clientcon->mqtt_state.message_length - (2 + var_header_len);
const char *client_id = mqtt_get_str(&clientcon->mqtt_state.in_buffer[2 + var_header_len], &id_len);
uint16_t id_len = clientcon->mqtt_state.message_length - (2 + msg_used_len);
const char *client_id = mqtt_get_str(&clientcon->mqtt_state.in_buffer[2 + msg_used_len], &id_len);
if (client_id == NULL || id_len > 80) {
MQTT_WARNING("MQTT: Client Id invalid\r\n");
msg_conn_ret = CONNECTION_REFUSE_ID_REJECTED;
@ -384,6 +381,7 @@ static void ICACHE_FLASH_ATTR MQTT_ClientCon_recv_cb(void *arg, char *pdata, uns
break;
}
}
msg_used_len += 2 + id_len;
// Get the LWT
clientcon->connect_info.will_retain = (variable_header->flags & MQTT_CONNECT_FLAG_WILL_RETAIN) != 0;
@ -396,9 +394,9 @@ static void ICACHE_FLASH_ATTR MQTT_ClientCon_recv_cb(void *arg, char *pdata, uns
return;
}
} else {
uint16_t lw_topic_len = clientcon->mqtt_state.message_length - (4 + var_header_len + id_len);
uint16_t lw_topic_len = clientcon->mqtt_state.message_length - (2 + msg_used_len);
const char *lw_topic =
mqtt_get_str(&clientcon->mqtt_state.in_buffer[4 + var_header_len + id_len], &lw_topic_len);
mqtt_get_str(&clientcon->mqtt_state.in_buffer[2 + msg_used_len], &lw_topic_len);
if (lw_topic == NULL) {
MQTT_WARNING("MQTT: Last Will topic invalid\r\n");
@ -422,11 +420,12 @@ static void ICACHE_FLASH_ATTR MQTT_ClientCon_recv_cb(void *arg, char *pdata, uns
clientcon->connState = TCP_DISCONNECTING;
break;
}
msg_used_len += 2 + lw_topic_len;
uint16_t lw_data_len =
clientcon->mqtt_state.message_length - (6 + var_header_len + id_len + lw_topic_len);
clientcon->mqtt_state.message_length - (2 + msg_used_len);
const char *lw_data =
mqtt_get_str(&clientcon->mqtt_state.in_buffer[6 + var_header_len + id_len + lw_topic_len],
mqtt_get_str(&clientcon->mqtt_state.in_buffer[2 + msg_used_len],
&lw_data_len);
if (lw_data == NULL) {
@ -446,6 +445,71 @@ static void ICACHE_FLASH_ATTR MQTT_ClientCon_recv_cb(void *arg, char *pdata, uns
clientcon->connState = TCP_DISCONNECTING;
break;
}
msg_used_len += 2 + lw_data_len;
}
// Get the username
if ((variable_header->flags & MQTT_CONNECT_FLAG_USERNAME) != 0) {
uint16_t username_len = clientcon->mqtt_state.message_length - (2 + msg_used_len);
const char *username =
mqtt_get_str(&clientcon->mqtt_state.in_buffer[2 + msg_used_len], &username_len);
if (username == NULL) {
MQTT_WARNING("MQTT: Username invalid\r\n");
MQTT_ServerDisconnect(clientcon);
return;
}
clientcon->connect_info.username = (char *)os_zalloc(username_len+1);
if (clientcon->connect_info.username != NULL) {
os_memcpy(clientcon->connect_info.username, username, username_len);
clientcon->connect_info.username[username_len] = '\0';
MQTT_INFO("MQTT: Username %s\r\n", clientcon->connect_info.username);
} else {
MQTT_ERROR("MQTT: Out of mem\r\n");
msg_conn_ret = CONNECTION_REFUSE_SERVER_UNAVAILABLE;
clientcon->connState = TCP_DISCONNECTING;
break;
}
msg_used_len += 2 + username_len;
}
// Get the password
if ((variable_header->flags & MQTT_CONNECT_FLAG_PASSWORD) != 0) {
if ((variable_header->flags & MQTT_CONNECT_FLAG_USERNAME) == 0) {
MQTT_WARNING("MQTT: Password without username\r\n");
MQTT_ServerDisconnect(clientcon);
return;
}
uint16_t password_len = clientcon->mqtt_state.message_length - (2 + msg_used_len);
const char *password =
mqtt_get_str(&clientcon->mqtt_state.in_buffer[2 + msg_used_len], &password_len);
if (password != NULL)
clientcon->connect_info.password = (char *)os_zalloc(password_len+1);
if (clientcon->connect_info.password != NULL) {
os_memcpy(clientcon->connect_info.password, password, password_len);
clientcon->connect_info.password[password_len] = '\0';
MQTT_INFO("MQTT: Password %s\r\n", clientcon->connect_info.password);
} else {
MQTT_ERROR("MQTT: Out of mem\r\n");
msg_conn_ret = CONNECTION_REFUSE_SERVER_UNAVAILABLE;
clientcon->connState = TCP_DISCONNECTING;
break;
}
msg_used_len += 2 + password_len;
}
// 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) {
MQTT_WARNING("MQTT: Authorization failed\r\n");
msg_conn_ret = CONNECTION_REFUSE_NOT_AUTHORIZED;
clientcon->connState = TCP_DISCONNECTING;
break;
}
msg_conn_ret = CONNECTION_ACCEPTED;
@ -812,3 +876,7 @@ bool ICACHE_FLASH_ATTR MQTT_local_unsubscribe(uint8_t * topic) {
void ICACHE_FLASH_ATTR MQTT_local_onData(MqttDataCallback dataCb) {
local_data_cb = dataCb;
}
void ICACHE_FLASH_ATTR MQTT_server_onAuth(MqttAuthCallback authCb) {
local_auth_cb = authCb;
}

Wyświetl plik

@ -34,6 +34,9 @@ void config_load_default(sysconfig_p config) {
config->clock_speed = 80;
config->config_port = CONSOLE_SERVER_PORT;
os_sprintf(config->mqtt_broker_user, "%s", "none");
config->mqtt_broker_password[0] = 0;
#ifdef MQTT_CLIENT
os_sprintf(config->mqtt_host, "%s", "none");
config->mqtt_port = 1883;

Wyświetl plik

@ -13,7 +13,7 @@
#define FLASH_BLOCK_NO 0xc
#define MAGIC_NUMBER 0x015005fc
#define MAGIC_NUMBER 0x015005fd
typedef struct
{
@ -46,6 +46,9 @@ typedef struct
uint16_t clock_speed; // Freq of the CPU
uint16_t config_port; // Port on which the concole listenes (0 if no access)
uint8_t mqtt_broker_user[32]; // Username for client login, "none" if empty
uint8_t mqtt_broker_password[32]; // Password for client login
#ifdef MQTT_CLIENT
uint8_t mqtt_host[32]; // IP or hostname of the MQTT broker, "none" if empty
uint16_t mqtt_port; // Port of the MQTT broker

Wyświetl plik

@ -2,6 +2,8 @@
config ap_ssid DerKluge
config ap_password Bonn2016
config ntp_server 1.de.pool.ntp.org
config broker_user Martin
config broker_password secret
config mqtt_host martinshome.fritz.box
config speed 160
@ -70,7 +72,7 @@ do
publish remote /martinshome/switch/1/status $1 retained
% The local pushbotton
% The local pushbutton
on gpio_interrupt 0 pullup
do
println "New state GPIO 0: " | $this_gpio

Wyświetl plik

@ -348,7 +348,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] <val>\r\n|quit|save [config]|reset [factory]|lock [<password>]|unlock <password>");
"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|broker_user|broker_password] <val>\r\n|quit|save [config]|reset [factory]|lock [<password>]|unlock <password>");
to_console(response);
#ifdef SCRIPTED
os_sprintf(response, "|script <port>");
@ -396,6 +396,14 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) {
// if static DNS, add it
os_sprintf(response, config.dns_addr.addr ? "DNS: %d.%d.%d.%d\r\n" : "", IP2STR(&config.dns_addr));
to_console(response);
if (os_strcmp(config.mqtt_broker_user, "none") != 0) {
os_sprintf(response,
"MQTT broker username: %s password: %s\r\n",
config.mqtt_broker_user,
config.locked ? "***" : (char *)config.mqtt_broker_password);
to_console(response);
}
#ifdef MQTT_CLIENT
os_sprintf(response, "MQTT client %s\r\n", mqtt_enabled ? "enabled" : "disabled");
to_console(response);
@ -819,6 +827,23 @@ void ICACHE_FLASH_ATTR console_handle_command(struct espconn *pespconn) {
goto command_handled;
}
#endif
if (strcmp(tokens[1], "broker_user") == 0) {
os_strncpy(config.mqtt_broker_user, tokens[2], 32);
config.mqtt_broker_user[31] = '\0';
os_sprintf(response, "Broker username set\r\n");
goto command_handled;
}
if (strcmp(tokens[1], "broker_password") == 0) {
if (os_strcmp(tokens[2], "none") == 0) {
config.mqtt_broker_password[0] = '\0';
} else {
os_strncpy(config.mqtt_broker_password, tokens[2], 32);
config.mqtt_broker_password[31] = '\0';
}
os_sprintf(response, "Broker password set\r\n");
goto command_handled;
}
#ifdef NTP
if (strcmp(tokens[1], "ntp_server") == 0) {
os_strncpy(config.ntp_server, tokens[2], 32);
@ -1188,7 +1213,21 @@ void ICACHE_FLASH_ATTR user_set_station_config(void) {
wifi_station_set_auto_connect(config.auto_connect != 0);
}
void ICACHE_FLASH_ATTR user_init() {
bool ICACHE_FLASH_ATTR mqtt_broker_auth(const char* username, const char *password) {
if (os_strcmp(config.mqtt_broker_user, "none") == 0)
return true;
if (os_strcmp(username, config.mqtt_broker_user) != 0 ||
os_strcmp(password, config.mqtt_broker_password) != 0) {
os_printf("Authentication with %s/%s failed\r\n", username, password);
return false;
}
return true;
}
void user_init() {
struct ip_info info;
connected = false;
@ -1297,11 +1336,12 @@ void ICACHE_FLASH_ATTR 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_onAuth(mqtt_broker_auth);
MQTT_server_start(1883 /*port */ , 30 /*max_subscriptions */ ,
30 /*max_retained_items */ );
MQTT_local_onData(MQTT_local_DataCallback);
//Start task
system_os_task(user_procTask, user_procTaskPrio, user_procTaskQueue, user_procTaskQueueLen);