Merge commit 'refs/pull/2438/head' of github.com:meshcore-dev/MeshCore into keymindCascade

# Conflicts:
#	examples/simple_repeater/MyMesh.cpp
#	examples/simple_repeater/MyMesh.h
#	examples/simple_repeater/main.cpp
This commit is contained in:
mikecarper committed 2026-07-06 22:53:04 -07:00
commit c411e65112
3 files changed
+36 -5

No files matched your search

+31 -3
View File
@@ -317,7 +317,7 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr
MESH_DEBUG_PRINTLN("Login success!");
client->last_timestamp = sender_timestamp;
client->last_activity = getRTCClock()->getCurrentTime();
client->permissions &= ~0x03;
client->permissions &= ~PERM_ACL_ROLE_MASK;
client->permissions |= perms;
memcpy(client->shared_secret, secret, PUB_KEY_SIZE);
@@ -334,7 +334,7 @@ uint8_t MyMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secr
memcpy(reply_data, &now, 4); // response packets always prefixed with timestamp
reply_data[4] = RESP_SERVER_LOGIN_OK;
reply_data[5] = 0; // Legacy: was recommended keep-alive interval (secs / 16)
reply_data[6] = client->isAdmin() ? 1 : 0;
reply_data[6] = (client->isAdmin() || client->isRegionMgr()) ? 1 : 0;
reply_data[7] = client->permissions;
getRNG()->random(&reply_data[8], 4); // random blob to help packet-hash uniqueness
reply_data[12] = FIRMWARE_VER_LEVEL; // New field
@@ -1981,7 +1981,7 @@ void MyMesh::onPeerDataRecv(mesh::Packet *packet, uint8_t type, int sender_idx,
} else {
MESH_DEBUG_PRINTLN("onPeerDataRecv: possible replay attack detected");
}
} else if (type == PAYLOAD_TYPE_TXT_MSG && len > 5 && client->isAdmin()) { // a CLI command
} else if (type == PAYLOAD_TYPE_TXT_MSG && len > 5 && (client->isAdmin() || client->isRegionMgr())) { // a CLI command
uint32_t sender_timestamp;
memcpy(&sender_timestamp, data, 4); // timestamp (by sender's RTC clock - which could be wrong)
uint8_t flags = (data[4] >> 2); // message attempt number, and other flags
@@ -3485,6 +3485,25 @@ static void formatPathReply(const uint8_t* path, uint8_t path_len, char* out, si
snprintf(out, out_len, "> hs=%u hops=%u hex=%s", (uint32_t)hash_size, (uint32_t)hop_count, hex);
}
// Whitelist helper for region manager command perms
static bool isRegionMgrAllowed(const char* cmd) {
while (*cmd == ' ') cmd++; // skip leading spaces
// region commands (read + write region map)
if (memcmp(cmd, "region", 6) == 0) return true;
// read-only getters / status
if (memcmp(cmd, "get ", 4) == 0) return true;
if (memcmp(cmd, "ver", 3) == 0) return true;
if (memcmp(cmd, "board", 5) == 0) return true;
// "neighbors" (plural) is read-only; reject "neighbor.remove" by checking next char
if (memcmp(cmd, "neighbors", 9) == 0) return true;
// bare "clock" is read-only; "clock sync" must be denied
if (memcmp(cmd, "clock", 5) == 0 && memcmp(cmd, "clock sync", 10) != 0) return true;
// sensor reads only
if (memcmp(cmd, "sensor get ", 11) == 0) return true;
if (memcmp(cmd, "sensor list", 11) == 0) return true;
return false;
}
void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *command, char *reply) {
char* reply_start = reply;
int recent_page = 1;
@@ -3530,6 +3549,15 @@ void MyMesh::handleCommand(uint32_t sender_timestamp, ClientInfo* sender, char *
command += 3;
}
// Region managers are limited to read-only queries and region commands
// Admins are unrestricted
if (sender && !sender->isAdmin() && sender->isRegionMgr()) {
if (!isRegionMgrAllowed(command)) {
strcpy(reply, "Err - not permitted");
return;
}
}
// handle ACL related commands
if (memcmp(command, "setperm ", 8) == 0) { // format: setperm {pubkey-hex} {permissions-int8}
char* hex = &command[8];
+2 -1
View File
@@ -109,7 +109,8 @@ ClientInfo* ClientACL::putClient(const mesh::Identity& id, uint8_t init_perms) {
ClientInfo* oldest = &clients[MAX_CLIENTS - 1];
for (int i = 0; i < num_clients; i++) {
if (id.matches(clients[i].id)) return &clients[i]; // already known
if (!clients[i].isAdmin() && clients[i].last_activity < min_time) {
if ( (!clients[i].isAdmin() && !clients[i].isRegionMgr())
&& clients[i].last_activity < min_time) {
oldest = &clients[i];
min_time = oldest->last_activity;
}
+3 -1
View File
@@ -4,11 +4,12 @@
#include <Mesh.h>
#include <helpers/IdentityStore.h>
#define PERM_ACL_ROLE_MASK 3 // lower 2 bits
#define PERM_ACL_ROLE_MASK 7 // lower 3 bits
#define PERM_ACL_GUEST 0
#define PERM_ACL_READ_ONLY 1
#define PERM_ACL_READ_WRITE 2
#define PERM_ACL_ADMIN 3
#define PERM_ACL_REGION_MGR 4
#define OUT_PATH_FORCE_FLOOD 0xFE
#define OUT_PATH_UNKNOWN 0xFF
@@ -34,6 +35,7 @@ struct ClientInfo {
} extra;
bool isAdmin() const { return (permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_ADMIN; }
bool isRegionMgr() const { return (permissions & PERM_ACL_ROLE_MASK) == PERM_ACL_REGION_MGR; }
};
#ifndef MAX_CLIENTS