mirror of
https://github.com/Nheko-Reborn/nheko.git
synced 2024-11-22 11:00:48 +03:00
Prevent the homeserver from inserting malicious secrets
Correctly verify that the reply to a secrets request is actually coming from a verified device. While we did verify that it was us who replied, we didn't properly cancel storing the secret if the sending device was one of ours but was maliciously inserted by the homeserver and unverified. We only send secret requests to verified devices in the first place, so only the homeserver could abuse this issue. Additionally we protected against malicious secret poisoning by verifying that the secret is actually the reply to a request. This means the server only has 2 places where it can poison the secrets: - After a verification when we automatically request the secrets - When the user manually hits the request button It also needs to prevent other secret answers to reach the client first since we ignore all replies after that one. The impact of this might be quite severe. It could allow the server to replace the cross-signing keys silently and while we might not trust that key, we possibly could trust it in the future if we rely on the stored secret. Similarly this could potentially be abused to make the client trust a malicious online key backup. If your deployment is not patched yet and you don't control your homeserver, you can protect against this by simply not doing any verifications of your own devices and not pressing the request button in the settings menu.
This commit is contained in:
parent
9010acd909
commit
67bee15a38
1 changed files with 25 additions and 15 deletions
|
@ -342,10 +342,13 @@ handle_olm_message(const OlmMessage &msg, const UserKeyCache &otherUserDeviceKey
|
||||||
if (msg.sender != local_user.to_string())
|
if (msg.sender != local_user.to_string())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto secret_name = request_id_to_secret_name.find(e->content.request_id);
|
auto secret_name_it = request_id_to_secret_name.find(e->content.request_id);
|
||||||
|
|
||||||
if (secret_name != request_id_to_secret_name.end()) {
|
if (secret_name_it != request_id_to_secret_name.end()) {
|
||||||
nhlog::crypto()->info("Received secret: {}", secret_name->second);
|
auto secret_name = secret_name_it->second;
|
||||||
|
request_id_to_secret_name.erase(secret_name_it);
|
||||||
|
|
||||||
|
nhlog::crypto()->info("Received secret: {}", secret_name);
|
||||||
|
|
||||||
mtx::events::msg::SecretRequest secretRequest{};
|
mtx::events::msg::SecretRequest secretRequest{};
|
||||||
secretRequest.action = mtx::events::msg::RequestAction::Cancellation;
|
secretRequest.action = mtx::events::msg::RequestAction::Cancellation;
|
||||||
|
@ -358,15 +361,24 @@ handle_olm_message(const OlmMessage &msg, const UserKeyCache &otherUserDeviceKey
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto deviceKeys = cache::userKeys(local_user.to_string());
|
auto deviceKeys = cache::userKeys(local_user.to_string());
|
||||||
|
if (!deviceKeys)
|
||||||
|
return;
|
||||||
|
|
||||||
std::string sender_device_id;
|
std::string sender_device_id;
|
||||||
if (deviceKeys) {
|
for (auto &[dev, key] : deviceKeys->device_keys) {
|
||||||
for (auto &[dev, key] : deviceKeys->device_keys) {
|
if (key.keys["curve25519:" + dev] == msg.sender_key) {
|
||||||
if (key.keys["curve25519:" + dev] == msg.sender_key) {
|
sender_device_id = dev;
|
||||||
sender_device_id = dev;
|
break;
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (!verificationStatus->verified_devices.count(sender_device_id) ||
|
||||||
|
!verificationStatus->verified_device_keys.count(msg.sender_key) ||
|
||||||
|
verificationStatus->verified_device_keys.at(msg.sender_key) !=
|
||||||
|
crypto::Trust::Verified) {
|
||||||
|
nhlog::net()->critical(
|
||||||
|
"Received secret from unverified device {}! Ignoring!", sender_device_id);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
std::map<mtx::identifiers::User,
|
std::map<mtx::identifiers::User,
|
||||||
std::map<std::string, mtx::events::msg::SecretRequest>>
|
std::map<std::string, mtx::events::msg::SecretRequest>>
|
||||||
|
@ -380,19 +392,17 @@ handle_olm_message(const OlmMessage &msg, const UserKeyCache &otherUserDeviceKey
|
||||||
http::client()->send_to_device<mtx::events::msg::SecretRequest>(
|
http::client()->send_to_device<mtx::events::msg::SecretRequest>(
|
||||||
http::client()->generate_txn_id(),
|
http::client()->generate_txn_id(),
|
||||||
body,
|
body,
|
||||||
[name = secret_name->second](mtx::http::RequestErr err) {
|
[secret_name](mtx::http::RequestErr err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
nhlog::net()->error("Failed to send request cancellation "
|
nhlog::net()->error("Failed to send request cancellation "
|
||||||
"for secrect "
|
"for secrect "
|
||||||
"'{}'",
|
"'{}'",
|
||||||
name);
|
secret_name);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
nhlog::crypto()->info("Storing secret {}", secret_name->second);
|
nhlog::crypto()->info("Storing secret {}", secret_name);
|
||||||
cache::client()->storeSecret(secret_name->second, e->content.secret);
|
cache::client()->storeSecret(secret_name, e->content.secret);
|
||||||
|
|
||||||
request_id_to_secret_name.erase(secret_name);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} else if (auto sec_req = std::get_if<DeviceEvent<msg::SecretRequest>>(&device_event)) {
|
} else if (auto sec_req = std::get_if<DeviceEvent<msg::SecretRequest>>(&device_event)) {
|
||||||
|
|
Loading…
Reference in a new issue