Cache responses to addr requests

Prevents a spy from scraping victim's AddrMan by
reconnecting and re-requesting addrs.
This commit is contained in:
Gleb Naumenko 2020-05-16 21:05:44 -04:00
parent 7cc0e8101f
commit acd6135b43
3 changed files with 43 additions and 1 deletions

View File

@ -2539,6 +2539,17 @@ std::vector<CAddress> CConnman::GetAddresses()
return addresses;
}
std::vector<CAddress> CConnman::GetAddresses(Network requestor_network)
{
const auto current_time = GetTime<std::chrono::microseconds>();
if (m_addr_response_caches.find(requestor_network) == m_addr_response_caches.end() ||
m_addr_response_caches[requestor_network].m_update_addr_response < current_time) {
m_addr_response_caches[requestor_network].m_addrs_response_cache = GetAddresses();
m_addr_response_caches[requestor_network].m_update_addr_response = current_time + std::chrono::hours(21) + GetRandMillis(std::chrono::hours(6));
}
return m_addr_response_caches[requestor_network].m_addrs_response_cache;
}
bool CConnman::AddNode(const std::string& strNode)
{
LOCK(cs_vAddedNodes);

View File

@ -27,6 +27,7 @@
#include <atomic>
#include <cstdint>
#include <deque>
#include <map>
#include <thread>
#include <memory>
#include <condition_variable>
@ -254,6 +255,13 @@ public:
void MarkAddressGood(const CAddress& addr);
void AddNewAddresses(const std::vector<CAddress>& vAddr, const CAddress& addrFrom, int64_t nTimePenalty = 0);
std::vector<CAddress> GetAddresses();
/**
* Cache is used to minimize topology leaks, so it should
* be used for all non-trusted calls, for example, p2p.
* A non-malicious call (from RPC) should
* call the function without a parameter to avoid using the cache.
*/
std::vector<CAddress> GetAddresses(Network requestor_network);
// This allows temporarily exceeding m_max_outbound_full_relay, with the goal of finding
// a peer that is better than all our current peers.
@ -418,6 +426,29 @@ private:
std::atomic<NodeId> nLastNodeId{0};
unsigned int nPrevNodeCount{0};
/**
* Cache responses to addr requests to minimize privacy leak.
* Attack example: scraping addrs in real-time may allow an attacker
* to infer new connections of the victim by detecting new records
* with fresh timestamps (per self-announcement).
*/
struct CachedAddrResponse {
std::vector<CAddress> m_addrs_response_cache;
std::chrono::microseconds m_update_addr_response{0};
};
/**
* Addr responses stored in different caches
* per network prevent cross-network node identification.
* If a node for example is multi-homed under Tor and IPv6,
* a single cache (or no cache at all) would let an attacker
* to easily detect that it is the same node by comparing responses.
* The used memory equals to 1000 CAddress records (or around 32 bytes) per
* distinct Network (up to 5) we have/had an inbound peer from,
* resulting in at most ~160 KB.
*/
std::map<Network, CachedAddrResponse> m_addr_response_caches;
/**
* Services this instance offers.
*

View File

@ -3477,7 +3477,7 @@ void ProcessMessage(
pfrom.fSentAddr = true;
pfrom.vAddrToSend.clear();
std::vector<CAddress> vAddr = connman.GetAddresses();
std::vector<CAddress> vAddr = connman.GetAddresses(pfrom.addr.GetNetwork());
FastRandomContext insecure_rand;
for (const CAddress &addr : vAddr) {
pfrom.PushAddress(addr, insecure_rand);