From 42f2c1ece556b054ff75dc980f28d6c94c508896 Mon Sep 17 00:00:00 2001 From: Scott Powell Date: Mon, 10 Feb 2025 16:36:14 +1100 Subject: [PATCH] * new "set guest.password .." CLI command --- examples/simple_repeater/main.cpp | 81 +++++++++++++++------------- examples/simple_room_server/main.cpp | 15 ++++-- 2 files changed, 57 insertions(+), 39 deletions(-) diff --git a/examples/simple_repeater/main.cpp b/examples/simple_repeater/main.cpp index 551d6874..60179746 100644 --- a/examples/simple_repeater/main.cpp +++ b/examples/simple_repeater/main.cpp @@ -106,7 +106,8 @@ struct ClientInfo { mesh::Identity id; uint32_t last_timestamp; uint8_t secret[PUB_KEY_SIZE]; - int out_path_len; + bool is_admin; + int8_t out_path_len; uint8_t out_path[MAX_PATH_SIZE]; }; @@ -125,6 +126,7 @@ struct NodePrefs { // persisted to file uint8_t disable_fwd; uint8_t advert_interval; // minutes / 2 uint8_t unused; + char guest_password[16]; }; class MyMesh : public mesh::Mesh { @@ -157,14 +159,7 @@ class MyMesh : public mesh::Mesh { memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp switch (payload[0]) { - case CMD_GET_STATS: { - uint32_t max_age_secs; - if (payload_len >= 5) { - memcpy(&max_age_secs, &payload[1], 4); // first param in request pkt - } else { - max_age_secs = 12*60*60; // default, 12 hours - } - + case CMD_GET_STATS: { // guests can also access this now RepeaterStats stats; stats.batt_milli_volts = board.getBattMilliVolts(); stats.curr_tx_queue_len = _mgr->getOutboundCount(); @@ -228,38 +223,47 @@ protected: uint32_t timestamp; memcpy(×tamp, data, 4); + bool is_admin; if (memcmp(&data[4], _prefs.password, strlen(_prefs.password)) == 0) { // check for valid password - auto client = putClient(sender); // add to known clients (if not already known) - if (client == NULL || timestamp <= client->last_timestamp) { - MESH_DEBUG_PRINTLN("Client table full, or replay attack!"); - return; // FATAL: client table is full -OR- replay attack - } + is_admin = true; + } else if (memcmp(&data[4], _prefs.guest_password, strlen(_prefs.guest_password)) == 0) { // check guest password + is_admin = false; + } else { + #if MESH_DEBUG + data[len] = 0; // ensure null terminator + MESH_DEBUG_PRINTLN("Invalid password: %s", &data[4]); + #endif + return; + } - MESH_DEBUG_PRINTLN("Login success!"); - client->last_timestamp = timestamp; + auto client = putClient(sender); // add to known clients (if not already known) + if (client == NULL || timestamp <= client->last_timestamp) { + MESH_DEBUG_PRINTLN("Client table full, or replay attack!"); + return; // FATAL: client table is full -OR- replay attack + } - uint32_t now = getRTCClock()->getCurrentTime(); - memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp - memcpy(&reply_data[4], "OK", 2); + MESH_DEBUG_PRINTLN("Login success!"); + client->last_timestamp = timestamp; + client->is_admin = is_admin; - if (packet->isRouteFlood()) { - // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response - mesh::Packet* path = createPathReturn(sender, client->secret, packet->path, packet->path_len, - PAYLOAD_TYPE_RESPONSE, reply_data, 4 + 2); - if (path) sendFlood(path); - } else { - mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->secret, reply_data, 4 + 2); - if (reply) { - if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT - sendDirect(reply, client->out_path, client->out_path_len); - } else { - sendFlood(reply); - } + uint32_t now = getRTCClock()->getCurrentTime(); + memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp + memcpy(&reply_data[4], "OK", 2); + + if (packet->isRouteFlood()) { + // let this sender know path TO here, so they can use sendDirect(), and ALSO encode the response + mesh::Packet* path = createPathReturn(sender, client->secret, packet->path, packet->path_len, + PAYLOAD_TYPE_RESPONSE, reply_data, 4 + 2); + if (path) sendFlood(path); + } else { + mesh::Packet* reply = createDatagram(PAYLOAD_TYPE_RESPONSE, sender, client->secret, reply_data, 4 + 2); + if (reply) { + if (client->out_path_len >= 0) { // we have an out_path, so send DIRECT + sendDirect(reply, client->out_path, client->out_path_len); + } else { + sendFlood(reply); } } - } else { - data[4+8] = 0; // ensure null terminator - MESH_DEBUG_PRINTLN("Incorrect password: %s", &data[4]); } } } @@ -321,7 +325,7 @@ protected: } else { MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected"); } - } else if (type == PAYLOAD_TYPE_TXT_MSG && len > 5) { // a CLI command + } else if (type == PAYLOAD_TYPE_TXT_MSG && len > 5 && client->is_admin) { // a CLI command uint32_t sender_timestamp; memcpy(&sender_timestamp, data, 4); // timestamp (by sender's RTC clock - which could be wrong) uint flags = (data[4] >> 2); // message attempt number, and other flags @@ -527,6 +531,11 @@ public: savePrefs(); strcpy(reply, "OK"); } + } else if (memcmp(config, "guest.password ", 15) == 0) { + strncpy(_prefs.guest_password, &config[15], sizeof(_prefs.guest_password)-1); + _prefs.guest_password[sizeof(_prefs.guest_password)-1] = 0; // truncate if necessary + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "name ", 5) == 0) { strncpy(_prefs.node_name, &config[5], sizeof(_prefs.node_name)-1); _prefs.node_name[sizeof(_prefs.node_name)-1] = 0; // truncate if nec diff --git a/examples/simple_room_server/main.cpp b/examples/simple_room_server/main.cpp index 533b3cd9..6ce3ab53 100644 --- a/examples/simple_room_server/main.cpp +++ b/examples/simple_room_server/main.cpp @@ -142,6 +142,7 @@ struct NodePrefs { // persisted to file uint8_t disable_fwd; uint8_t advert_interval; // minutes uint8_t unused; + char guest_password[16]; }; class MyMesh : public mesh::Mesh { @@ -291,12 +292,11 @@ protected: is_admin = true; } else { is_admin = false; - #ifdef ROOM_PASSWORD - if (memcmp(&data[8], ROOM_PASSWORD, strlen(ROOM_PASSWORD)) != 0) { // check the room/public password + int len = strlen(_prefs.guest_password); + if (len > 0 && memcmp(&data[8], _prefs.guest_password, len) != 0) { // check the room/public password MESH_DEBUG_PRINTLN("Incorrect room password"); return; // no response. Client will timeout } - #endif } auto client = putClient(sender); // add to known clients (if not already known) @@ -524,6 +524,10 @@ public: _prefs.tx_power_dbm = LORA_TX_POWER; _prefs.disable_fwd = 1; _prefs.advert_interval = 2; // default to 2 minutes for NEW installs + #ifdef ROOM_PASSWORD + strncpy(_prefs.guest_password, ROOM_PASSWORD, sizeof(_prefs.guest_password)-1); + _prefs.guest_password[sizeof(_prefs.guest_password)-1] = 0; // truncate if necessary + #endif num_clients = 0; next_post_idx = 0; @@ -625,6 +629,11 @@ public: savePrefs(); strcpy(reply, "OK"); } + } else if (memcmp(config, "guest.password ", 15) == 0) { + strncpy(_prefs.guest_password, &config[15], sizeof(_prefs.guest_password)-1); + _prefs.guest_password[sizeof(_prefs.guest_password)-1] = 0; // truncate if necessary + savePrefs(); + strcpy(reply, "OK"); } else if (memcmp(config, "name ", 5) == 0) { strncpy(_prefs.node_name, &config[5], sizeof(_prefs.node_name)-1); _prefs.node_name[sizeof(_prefs.node_name)-1] = 0; // truncate if nec