net: Do not apply whitelist permission to onion inbounds
What changed, and why it matters
This change fixes a logic issue in Bitcoin Core's network handling. When a node operator configures special permissions (like bypassing connection limits or relay rules) for specific IP addresses, those permissions were mistakenly being granted to inbound connections arriving through a Tor hidden service. Because Tor hides the true IP address of the connecting peer, the node cannot verify that the peer actually belongs to the whitelisted address range. The patch ensures that whitelist permissions are not applied to Tor inbound connections, so only connections whose real network address is known can receive those privileges.
This appears to be a security-hardening fix. Operators running nodes with Tor inbound services and whitelisted IP ranges should upgrade to a release containing this patch. Review any custom configurations that grant whitelist permissions over Tor, as they may have been unintentionally effective prior to this fix.
Security signals we found
Address-based authorization bypass risk for Tor inbound peers
Privilege misassignment due to inability to verify true peer network address
Network-layer anonymity interaction with access control policy
Evidence from the diff
The commit modifies CConnman::AddWhitelistPermissionFlags to accept an optional CNetAddr instead of a required address. In CreateNodeFromAcceptedSocket, it detects whether the accepted socket is bound to an onion service address (m_onion_binds). If so, it passes std::nullopt to AddWhitelistPermissionFlags, preventing address-based whitelist permissions from being applied. The comment explains that Tor inbound connections do not reveal the peer’s actual network address, so address-based whitelisting is unreliable. The inbound_onion check is moved earlier in the function so it can be used for this permission decision.
Changed components
src/net.cppsrc/net.hCConnman::AddWhitelistPermissionFlagsCConnman::CreateNodeFromAcceptedSocketTor/onion inbound connection handlingInspect captured patch +8 / −5
diff --git a/src/net.cpp b/src/net.cpp
index aab8782f..50988114 100644
--- a/src/net.cpp
+++ b/src/net.cpp
@@ -574,9 +574,9 @@ void CNode::CloseSocketDisconnect()
m_i2p_sam_session.reset();
}
-void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr, const std::vector<NetWhitelistPermissions>& ranges) const {
+void CConnman::AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const {
for (const auto& subnet : ranges) {
- if (subnet.m_subnet.Match(addr)) {
+ if (addr.has_value() && subnet.m_subnet.Match(addr.value())) {
NetPermissions::AddFlag(flags, subnet.m_flags);
}
}
@@ -1768,7 +1768,11 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
{
int nInbound = 0;
- AddWhitelistPermissionFlags(permission_flags, addr, vWhitelistedRangeIncoming);
+ const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
+
+ // Tor inbound connections do not reveal the peer's actual network address.
+ // Therefore do not apply address-based whitelist permissions to them.
+ AddWhitelistPermissionFlags(permission_flags, inbound_onion ? std::optional<CNetAddr>{} : addr, vWhitelistedRangeIncoming);
{
LOCK(m_nodes_mutex);
@@ -1823,7 +1827,6 @@ void CConnman::CreateNodeFromAcceptedSocket(std::unique_ptr<Sock>&& sock,
NodeId id = GetNewNodeId();
uint64_t nonce = GetDeterministicRandomizer(RANDOMIZER_ID_LOCALHOSTNONCE).Write(id).Finalize();
- const bool inbound_onion = std::find(m_onion_binds.begin(), m_onion_binds.end(), addr_bind) != m_onion_binds.end();
// The V2Transport transparently falls back to V1 behavior when an incoming V1 connection is
// detected, so use it whenever we signal NODE_P2P_V2.
ServiceFlags local_services = GetLocalServices();
diff --git a/src/net.h b/src/net.h
index eefcd322..afbcc52b 100644
--- a/src/net.h
+++ b/src/net.h
@@ -1377,7 +1377,7 @@ private:
bool AttemptToEvictConnection();
CNode* ConnectNode(CAddress addrConnect, const char *pszDest, bool fCountFailure, ConnectionType conn_type, bool use_v2transport) EXCLUSIVE_LOCKS_REQUIRED(!m_unused_i2p_sessions_mutex);
- void AddWhitelistPermissionFlags(NetPermissionFlags& flags, const CNetAddr &addr, const std::vector<NetWhitelistPermissions>& ranges) const;
+ void AddWhitelistPermissionFlags(NetPermissionFlags& flags, std::optional<CNetAddr> addr, const std::vector<NetWhitelistPermissions>& ranges) const;
void DeleteNode(CNode* pnode);
Why this scored 52/100
Community notes
Notes can correct, qualify, or add evidence to the AI analysis. Every note shown here has been validated by a human moderator.
The AI analysis stands alone for now. Submit a note if you can add evidence or important context.