mirror of
https://github.com/liquidraver/ZephCore.git
synced 2026-10-22 19:44:57 +00:00
room-server: drop dead anon-request handlers inherited from the clone
The room server's onAnonDataRecv handles login inline (the room login protocol carries a sync_since cursor the repeater's doesn't), so the cloned repeater helpers handleLoginReq + handleAnonRegionsReq / handleAnonOwnerReq / handleAnonClockReq — and the anon_limiter they used — were dead after the port. Remove them (~160 lines). No behavior change. The inherited neighbor-tracking / node-discovery machinery is also unused on a room server; left in place for now (can be trimmed in a follow-up).
This commit is contained in:
2 files changed
+3
-166
No files matched your search
@@ -156,162 +156,6 @@ void RoomServerMesh::putNeighbour(const mesh::Identity& id, uint32_t timestamp,
|
||||
#endif
|
||||
}
|
||||
|
||||
uint8_t RoomServerMesh::handleLoginReq(const mesh::Identity& sender, const uint8_t* secret, uint32_t sender_timestamp, const uint8_t* data, bool is_flood) {
|
||||
ClientInfo* client = nullptr;
|
||||
|
||||
if (data[0] == 0) {
|
||||
client = acl.getClient(sender.pub_key, PUB_KEY_SIZE);
|
||||
}
|
||||
|
||||
if (client == nullptr) {
|
||||
uint8_t perms;
|
||||
|
||||
/* Constant-time comparison: pad the received password to the full
|
||||
* 16-byte storage size with zeros, then compare against both
|
||||
* stored passwords (which are already zero-padded by initNodePrefs).
|
||||
* Compare both unconditionally so timing is identical for any
|
||||
* wrong password regardless of which (admin/guest) it most
|
||||
* resembles. */
|
||||
uint8_t received[sizeof(_prefs.password)] = {0};
|
||||
size_t r_len = strnlen((const char *)data, sizeof(received) - 1);
|
||||
memcpy(received, data, r_len);
|
||||
|
||||
bool admin_match = mesh::Utils::constantTimeEqual(received,
|
||||
_prefs.password,
|
||||
sizeof(received));
|
||||
bool guest_match = mesh::Utils::constantTimeEqual(received,
|
||||
_prefs.guest_password,
|
||||
sizeof(received));
|
||||
|
||||
if (admin_match) {
|
||||
perms = PERM_ACL_ADMIN;
|
||||
} else if (guest_match) {
|
||||
perms = PERM_ACL_GUEST;
|
||||
} else {
|
||||
/* Apply global failed-login rate limit. The check itself is
|
||||
* unconditional regardless of admin/guest path so its timing
|
||||
* doesn't leak which credential the attempt was closer to. */
|
||||
if (!login_fail_limiter.allow(getRTCClock()->getCurrentTime())) {
|
||||
LOG_WRN("Login rate-limited (failed attempts exceeded)");
|
||||
} else {
|
||||
LOG_WRN("Invalid password");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
client = acl.putClient(sender, 0);
|
||||
if (sender_timestamp <= client->last_timestamp) {
|
||||
LOG_WRN("Possible login replay attack!");
|
||||
return 0;
|
||||
}
|
||||
|
||||
LOG_INF("Login success");
|
||||
client->last_timestamp = sender_timestamp;
|
||||
client->last_activity = getRTCClock()->getCurrentTime();
|
||||
client->permissions &= ~0x03;
|
||||
client->permissions |= perms;
|
||||
memcpy(client->shared_secret, secret, PUB_KEY_SIZE);
|
||||
|
||||
if (perms != PERM_ACL_GUEST) {
|
||||
if (!dirty_contacts_expiry) dirty_contacts_expiry = futureMillis(LAZY_CONTACTS_WRITE_DELAY);
|
||||
}
|
||||
}
|
||||
|
||||
if (is_flood) {
|
||||
client->out_path_len = OUT_PATH_UNKNOWN;
|
||||
}
|
||||
|
||||
uint32_t now = getRTCClock()->getCurrentTimeUnique();
|
||||
memcpy(reply_data, &now, 4);
|
||||
reply_data[4] = RESP_SERVER_LOGIN_OK;
|
||||
reply_data[5] = 0;
|
||||
reply_data[6] = client->isAdmin() ? 1 : 0;
|
||||
reply_data[7] = client->permissions;
|
||||
getRNG()->random(&reply_data[8], 4);
|
||||
reply_data[12] = FIRMWARE_VER_LEVEL;
|
||||
|
||||
return 13;
|
||||
}
|
||||
|
||||
uint8_t RoomServerMesh::handleAnonRegionsReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data, size_t data_len) {
|
||||
if (anon_limiter.allow(getRTCClock()->getCurrentTime())) {
|
||||
if (data_len < 1) return 0;
|
||||
reply_path_len = *data++;
|
||||
data_len--;
|
||||
/* data is anon-req-supplied; bound copy with remaining data_len.
|
||||
* If the claimed path is longer than the bytes provided, copyPath
|
||||
* rejects (returns 0) and leaves reply_path stale — fall back to a
|
||||
* flood reply instead of emitting a corrupt direct path (F2).
|
||||
* path_len == 0 is a valid zero-hop direct reply, so don't treat
|
||||
* its (also-0) return as a rejection. */
|
||||
if (reply_path_len != 0 &&
|
||||
mesh::Packet::copyPath(reply_path, data, data_len, reply_path_len) == 0) {
|
||||
reply_path_len = OUT_PATH_UNKNOWN;
|
||||
}
|
||||
|
||||
memcpy(reply_data, &sender_timestamp, 4);
|
||||
uint32_t now = getRTCClock()->getCurrentTime();
|
||||
memcpy(&reply_data[4], &now, 4);
|
||||
|
||||
return 8 + region_map.exportNamesTo((char*)&reply_data[8], sizeof(reply_data) - 12, REGION_DENY_FLOOD);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t RoomServerMesh::handleAnonOwnerReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data, size_t data_len) {
|
||||
if (anon_limiter.allow(getRTCClock()->getCurrentTime())) {
|
||||
if (data_len < 1) return 0;
|
||||
reply_path_len = *data++;
|
||||
data_len--;
|
||||
/* data is anon-req-supplied; bound copy with remaining data_len.
|
||||
* If the claimed path is longer than the bytes provided, copyPath
|
||||
* rejects (returns 0) and leaves reply_path stale — fall back to a
|
||||
* flood reply instead of emitting a corrupt direct path (F2).
|
||||
* path_len == 0 is a valid zero-hop direct reply, so don't treat
|
||||
* its (also-0) return as a rejection. */
|
||||
if (reply_path_len != 0 &&
|
||||
mesh::Packet::copyPath(reply_path, data, data_len, reply_path_len) == 0) {
|
||||
reply_path_len = OUT_PATH_UNKNOWN;
|
||||
}
|
||||
|
||||
memcpy(reply_data, &sender_timestamp, 4);
|
||||
uint32_t now = getRTCClock()->getCurrentTime();
|
||||
memcpy(&reply_data[4], &now, 4);
|
||||
sprintf((char*)&reply_data[8], "%s\n%s", _prefs.node_name, _prefs.owner_info);
|
||||
|
||||
return 8 + strlen((char*)&reply_data[8]);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint8_t RoomServerMesh::handleAnonClockReq(const mesh::Identity& sender, uint32_t sender_timestamp, const uint8_t* data, size_t data_len) {
|
||||
if (anon_limiter.allow(getRTCClock()->getCurrentTime())) {
|
||||
if (data_len < 1) return 0;
|
||||
reply_path_len = *data++;
|
||||
data_len--;
|
||||
/* data is anon-req-supplied; bound copy with remaining data_len.
|
||||
* If the claimed path is longer than the bytes provided, copyPath
|
||||
* rejects (returns 0) and leaves reply_path stale — fall back to a
|
||||
* flood reply instead of emitting a corrupt direct path (F2).
|
||||
* path_len == 0 is a valid zero-hop direct reply, so don't treat
|
||||
* its (also-0) return as a rejection. */
|
||||
if (reply_path_len != 0 &&
|
||||
mesh::Packet::copyPath(reply_path, data, data_len, reply_path_len) == 0) {
|
||||
reply_path_len = OUT_PATH_UNKNOWN;
|
||||
}
|
||||
|
||||
memcpy(reply_data, &sender_timestamp, 4);
|
||||
uint32_t now = getRTCClock()->getCurrentTime();
|
||||
memcpy(&reply_data[4], &now, 4);
|
||||
reply_data[8] = 0; // features
|
||||
if (_prefs.disable_fwd) {
|
||||
reply_data[8] |= 0x80;
|
||||
}
|
||||
return 9;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int RoomServerMesh::handleRequest(ClientInfo* sender, uint32_t sender_timestamp, uint8_t* payload, size_t payload_len) {
|
||||
memcpy(reply_data, &sender_timestamp, 4);
|
||||
|
||||
@@ -1134,11 +978,8 @@ RoomServerMesh::RoomServerMesh(mesh::MainBoard& board, mesh::Radio& radio, mesh:
|
||||
_cli(board, rtc, acl, &_prefs, this),
|
||||
region_map(key_store), temp_map(key_store),
|
||||
discover_limiter(4, 120),
|
||||
anon_limiter(4, 180),
|
||||
/* Failed-login rate limit: 4 wrong-password attempts per 180s. Matches
|
||||
* anon_limiter's shape so legitimate operators don't notice; brute-force
|
||||
* attempts hit the cap quickly and trip the LOG_WRN below. Global rate
|
||||
* (not per-sender) — trade-off documented in CRYPTO_AUDIT_INDEX.md
|
||||
/* Failed-login rate limit: 4 wrong-password attempts per 180s. Global
|
||||
* rate (not per-sender) — trade-off documented in CRYPTO_AUDIT_INDEX.md
|
||||
* Phase 4 (mitigation for upstream MeshCore#2556). */
|
||||
login_fail_limiter(4, 180) {
|
||||
|
||||
|
||||
Reference in new issue
Block a user