From 8f75223ee19d7aeca59a6f37e687dd34eaae0002 Mon Sep 17 00:00:00 2001 From: Alexander Hoffer Date: Mon, 20 Jul 2026 11:03:29 +0100 Subject: [PATCH 1/2] Fix blank guest room authorization --- examples/simple_room_server/MyMesh.cpp | 27 ++++++++++-------- examples/simple_room_server/RoomAuth.h | 31 ++++++++++++++++++++ test/test_room_auth/test_room_auth.cpp | 39 ++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 12 deletions(-) create mode 100644 examples/simple_room_server/RoomAuth.h create mode 100644 test/test_room_auth/test_room_auth.cpp diff --git a/examples/simple_room_server/MyMesh.cpp b/examples/simple_room_server/MyMesh.cpp index 36978e808f..5d944d58f3 100644 --- a/examples/simple_room_server/MyMesh.cpp +++ b/examples/simple_room_server/MyMesh.cpp @@ -1,4 +1,12 @@ #include "MyMesh.h" +#include "RoomAuth.h" + +static_assert(static_cast(room_server::LoginPermission::Guest) == PERM_ACL_GUEST, + "room login guest permission must match the ACL role"); +static_assert(static_cast(room_server::LoginPermission::ReadWrite) == PERM_ACL_READ_WRITE, + "room login read/write permission must match the ACL role"); +static_assert(static_cast(room_server::LoginPermission::Admin) == PERM_ACL_ADMIN, + "room login admin permission must match the ACL role"); #define REPLY_DELAY_MILLIS 1500 #define PUSH_NOTIFY_DELAY_MILLIS 2000 @@ -325,19 +333,14 @@ void MyMesh::onAnonDataRecv(mesh::Packet *packet, const uint8_t *secret, const m } } if (client == NULL) { - uint8_t perm; - if (strcmp((char *)&data[8], _prefs.password) == 0) { // check for valid admin password - perm = PERM_ACL_ADMIN; - } else { - if (strcmp((char *)&data[8], _prefs.guest_password) == 0) { // check the room/public password - perm = PERM_ACL_READ_WRITE; - } else if (_prefs.allow_read_only) { - perm = PERM_ACL_GUEST; - } else { - MESH_DEBUG_PRINTLN("Incorrect room password"); - return; // no response. Client will timeout - } + const auto login_permission = room_server::resolveLoginPermission((char *)&data[8], _prefs.password, + _prefs.guest_password, + _prefs.allow_read_only); + if (login_permission == room_server::LoginPermission::Rejected) { + MESH_DEBUG_PRINTLN("Incorrect room password"); + return; // no response. Client will timeout } + const uint8_t perm = static_cast(login_permission); client = acl.putClient(sender, 0); // add to known clients (if not already known) if (sender_timestamp <= client->last_timestamp) { diff --git a/examples/simple_room_server/RoomAuth.h b/examples/simple_room_server/RoomAuth.h new file mode 100644 index 0000000000..e8bc68abdb --- /dev/null +++ b/examples/simple_room_server/RoomAuth.h @@ -0,0 +1,31 @@ +#pragma once + +#include +#include + +namespace room_server { + +enum class LoginPermission : uint8_t { + Guest = 0, + ReadWrite = 2, + Admin = 3, + Rejected = 0xFF, +}; + +inline LoginPermission resolveLoginPermission(const char *supplied_password, const char *admin_password, + const char *guest_password, bool allow_read_only) { + if (strcmp(supplied_password, admin_password) == 0) { + return LoginPermission::Admin; + } + // An empty guest password disables authenticated read/write access. Without + // this guard, a blank login is promoted before open read-only access applies. + if (guest_password[0] != 0 && strcmp(supplied_password, guest_password) == 0) { + return LoginPermission::ReadWrite; + } + if (allow_read_only) { + return LoginPermission::Guest; + } + return LoginPermission::Rejected; +} + +} // namespace room_server diff --git a/test/test_room_auth/test_room_auth.cpp b/test/test_room_auth/test_room_auth.cpp new file mode 100644 index 0000000000..e03e1e4704 --- /dev/null +++ b/test/test_room_auth/test_room_auth.cpp @@ -0,0 +1,39 @@ +#include + +#include "../../examples/simple_room_server/RoomAuth.h" + +using room_server::LoginPermission; +using room_server::resolveLoginPermission; + +TEST(RoomAuth, MatchingAdminPasswordGrantsAdmin) { + EXPECT_EQ(LoginPermission::Admin, resolveLoginPermission("admin", "admin", "guest", true)); +} + +TEST(RoomAuth, AdminPasswordTakesPriorityWhenPasswordsMatch) { + EXPECT_EQ(LoginPermission::Admin, resolveLoginPermission("shared", "shared", "shared", true)); +} + +TEST(RoomAuth, MatchingNonEmptyGuestPasswordTakesPriorityOverOpenReadOnly) { + EXPECT_EQ(LoginPermission::ReadWrite, resolveLoginPermission("guest", "admin", "guest", true)); +} + +TEST(RoomAuth, BlankGuestPasswordDoesNotGrantReadWrite) { + EXPECT_EQ(LoginPermission::Guest, resolveLoginPermission("", "admin", "", true)); +} + +TEST(RoomAuth, OpenReadOnlyAccessAcceptsAnyNonAdminAsGuest) { + EXPECT_EQ(LoginPermission::Guest, resolveLoginPermission("wrong", "admin", "guest", true)); +} + +TEST(RoomAuth, ClosedServerRejectsBlankPasswordWithBlankGuestPassword) { + EXPECT_EQ(LoginPermission::Rejected, resolveLoginPermission("", "admin", "", false)); +} + +TEST(RoomAuth, ClosedServerRejectsIncorrectPassword) { + EXPECT_EQ(LoginPermission::Rejected, resolveLoginPermission("wrong", "admin", "guest", false)); +} + +int main(int argc, char **argv) { + ::testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} From 6981b880ec938f9ef44de524ba67a1921c657200 Mon Sep 17 00:00:00 2001 From: Alexander Hoffer Date: Mon, 20 Jul 2026 14:48:17 +0100 Subject: [PATCH 2/2] fix: disable authentication for blank admin password --- examples/simple_room_server/RoomAuth.h | 6 +++--- test/test_room_auth/test_room_auth.cpp | 4 ++++ 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/examples/simple_room_server/RoomAuth.h b/examples/simple_room_server/RoomAuth.h index e8bc68abdb..3153de425d 100644 --- a/examples/simple_room_server/RoomAuth.h +++ b/examples/simple_room_server/RoomAuth.h @@ -14,11 +14,11 @@ enum class LoginPermission : uint8_t { inline LoginPermission resolveLoginPermission(const char *supplied_password, const char *admin_password, const char *guest_password, bool allow_read_only) { - if (strcmp(supplied_password, admin_password) == 0) { + // Empty configured passwords disable their corresponding authenticated role. + if (admin_password[0] != 0 && strcmp(supplied_password, admin_password) == 0) { return LoginPermission::Admin; } - // An empty guest password disables authenticated read/write access. Without - // this guard, a blank login is promoted before open read-only access applies. + // Without this guard, a blank login is promoted before open read-only access applies. if (guest_password[0] != 0 && strcmp(supplied_password, guest_password) == 0) { return LoginPermission::ReadWrite; } diff --git a/test/test_room_auth/test_room_auth.cpp b/test/test_room_auth/test_room_auth.cpp index e03e1e4704..2423d9450b 100644 --- a/test/test_room_auth/test_room_auth.cpp +++ b/test/test_room_auth/test_room_auth.cpp @@ -13,6 +13,10 @@ TEST(RoomAuth, AdminPasswordTakesPriorityWhenPasswordsMatch) { EXPECT_EQ(LoginPermission::Admin, resolveLoginPermission("shared", "shared", "shared", true)); } +TEST(RoomAuth, BlankAdminPasswordDoesNotGrantAdmin) { + EXPECT_EQ(LoginPermission::Guest, resolveLoginPermission("", "", "guest", true)); +} + TEST(RoomAuth, MatchingNonEmptyGuestPasswordTakesPriorityOverOpenReadOnly) { EXPECT_EQ(LoginPermission::ReadWrite, resolveLoginPermission("guest", "admin", "guest", true)); }