diff --git a/cfg/rippled-example.cfg b/cfg/rippled-example.cfg index 8bffc150c1..93866777c3 100644 --- a/cfg/rippled-example.cfg +++ b/cfg/rippled-example.cfg @@ -527,6 +527,17 @@ # # The current default (which is subject to change) is 300 seconds. # +# verify_endpoints = <0 | 1> +# +# If set to 0, the server will skip validation of endpoint +# addresses received in TMEndpoints peer protocol messages, +# allowing addresses that are not publicly routable or have a +# port of 0. The default is 1 (verification enabled). +# +# WARNING: Disabling this option is a security risk and should +# only be used for local testing and debugging. Do not disable +# on mainnet. +# # # [transaction_queue] EXPERIMENTAL # diff --git a/src/libxrpl/beast/net/IPAddressV4.cpp b/src/libxrpl/beast/net/IPAddressV4.cpp index 29455024f6..00b57c3283 100644 --- a/src/libxrpl/beast/net/IPAddressV4.cpp +++ b/src/libxrpl/beast/net/IPAddressV4.cpp @@ -37,7 +37,45 @@ is_private(AddressV4 const& addr) bool is_public(AddressV4 const& addr) { - return !is_private(addr) && !addr.is_multicast(); + if (is_private(addr)) + return false; + if (addr.is_multicast()) + return false; + + auto const ip = addr.to_uint(); + + // 0.0.0.0/8 "This network" + if ((ip & 0xff000000) == 0x00000000) + return false; + // 100.64.0.0/10 Shared Address Space (CGNAT) - RFC 6598 + if ((ip & 0xffc00000) == 0x64400000) + return false; + // 169.254.0.0/16 Link-local + if ((ip & 0xffff0000) == 0xa9fe0000) + return false; + // 192.0.0.0/24 IETF Protocol Assignments - RFC 6890 + if ((ip & 0xffffff00) == 0xc0000000) + return false; + // 192.0.2.0/24 TEST-NET-1 (documentation) - RFC 5737 + if ((ip & 0xffffff00) == 0xc0000200) + return false; + // 192.88.99.0/24 6to4 Relay Anycast (deprecated) - RFC 7526 + if ((ip & 0xffffff00) == 0xc0586300) + return false; + // 198.18.0.0/15 Benchmarking - RFC 2544 + if ((ip & 0xfffe0000) == 0xc6120000) + return false; + // 198.51.100.0/24 TEST-NET-2 (documentation) - RFC 5737 + if ((ip & 0xffffff00) == 0xc6336400) + return false; + // 203.0.113.0/24 TEST-NET-3 (documentation) - RFC 5737 + if ((ip & 0xffffff00) == 0xcb007100) + return false; + // 240.0.0.0/4 Reserved for future use - RFC 1112 + if ((ip & 0xf0000000) == 0xf0000000) + return false; + + return true; } char diff --git a/src/libxrpl/beast/net/IPAddressV6.cpp b/src/libxrpl/beast/net/IPAddressV6.cpp index f90a6d066b..762da26ee6 100644 --- a/src/libxrpl/beast/net/IPAddressV6.cpp +++ b/src/libxrpl/beast/net/IPAddressV6.cpp @@ -26,16 +26,52 @@ namespace IP { bool is_private(AddressV6 const& addr) { - return ( - (addr.to_bytes()[0] & 0xfd) || // TODO fc00::/8 too ? - (addr.is_v4_mapped() && is_private(addr.to_v4()))); + // fc00::/7 - Unique Local Address (ULA), covers fc00:: and fd00:: + if ((addr.to_bytes()[0] & 0xfe) == 0xfc) + return true; + if (addr.is_v4_mapped()) + return is_private(addr.to_v4()); + return false; } bool is_public(AddressV6 const& addr) { - // TODO is this correct? - return !is_private(addr) && !addr.is_multicast(); + if (addr.is_loopback()) + return false; + if (addr.is_v4_mapped()) + return is_public( + boost::asio::ip::make_address_v4(boost::asio::ip::v4_mapped, addr)); + if (is_private(addr)) + return false; + if (addr.is_multicast()) + return false; + + auto const b = addr.to_bytes(); + + // fe80::/10 - Link-local + if (b[0] == 0xfe && (b[1] & 0xc0) == 0x80) + return false; + // 100::/64 - Discard prefix (RFC 6666) + if (b[0] == 0x01 && b[1] == 0x00 && b[2] == 0 && b[3] == 0 && b[4] == 0 && + b[5] == 0 && b[6] == 0 && b[7] == 0) + return false; + // 2001:db8::/32 - Documentation (RFC 3849) + if (b[0] == 0x20 && b[1] == 0x01 && b[2] == 0x0d && b[3] == 0xb8) + return false; + // 2001::/32 - IETF Protocol Assignments / Teredo (RFC 4380) + if (b[0] == 0x20 && b[1] == 0x01 && b[2] == 0x00 && b[3] == 0x00) + return false; + // 2001:20::/28 - ORCHIDv2 (RFC 7343) + // 28-bit prefix: 0x2001002 => b[0]=0x20, b[1]=0x01, b[2]=0x00, + // top nibble of b[3]=0x2 + if (b[0] == 0x20 && b[1] == 0x01 && b[2] == 0x00 && (b[3] & 0xf0) == 0x20) + return false; + // 2002::/16 - 6to4 (RFC 3056, deprecated by RFC 7526) + if (b[0] == 0x20 && b[1] == 0x02) + return false; + + return true; } } // namespace IP diff --git a/src/test/peerfinder/PeerFinder_test.cpp b/src/test/peerfinder/PeerFinder_test.cpp index 64a1eb5091..f16b63cee7 100644 --- a/src/test/peerfinder/PeerFinder_test.cpp +++ b/src/test/peerfinder/PeerFinder_test.cpp @@ -440,7 +440,7 @@ public: c.PEERS_OUT_MAX == 0) || (c.PEERS_IN_MAX == *maxIn && c.PEERS_OUT_MAX == *maxOut)); - Config config = Config::makeConfig(c, port, false, 0); + Config config = Config::makeConfig(c, port, false, 0, true); Counts counts; counts.onConfig(config); diff --git a/src/xrpld/app/main/Application.cpp b/src/xrpld/app/main/Application.cpp index 8c2ee43205..7b719e4298 100644 --- a/src/xrpld/app/main/Application.cpp +++ b/src/xrpld/app/main/Application.cpp @@ -1394,7 +1394,7 @@ ApplicationImp::setup(boost::program_options::variables_map const& cmdline) // if (!config_.standalone()) overlay_ = make_Overlay( *this, - setup_Overlay(*config_), + setup_Overlay(*config_, m_journal), *serverHandler_, *m_resourceManager, *m_resolver, diff --git a/src/xrpld/overlay/Overlay.h b/src/xrpld/overlay/Overlay.h index 7f5c858f67..3f22fd9a50 100644 --- a/src/xrpld/overlay/Overlay.h +++ b/src/xrpld/overlay/Overlay.h @@ -71,6 +71,7 @@ public: std::uint32_t crawlOptions = 0; std::optional networkID; bool vlEnabled = true; + bool verifyEndpoints = false; }; using PeerSequence = std::vector>; diff --git a/src/xrpld/overlay/detail/OverlayImpl.cpp b/src/xrpld/overlay/detail/OverlayImpl.cpp index 9b713d8ce0..6fae514c6e 100644 --- a/src/xrpld/overlay/detail/OverlayImpl.cpp +++ b/src/xrpld/overlay/detail/OverlayImpl.cpp @@ -483,7 +483,8 @@ OverlayImpl::start() app_.config(), serverHandler_.setup().overlay.port(), app_.getValidationPublicKey().has_value(), - setup_.ipLimit); + setup_.ipLimit, + setup_.verifyEndpoints); m_peerFinder->setConfig(config); m_peerFinder->start(); @@ -1493,7 +1494,7 @@ OverlayImpl::deleteIdlePeers() //------------------------------------------------------------------------------ Overlay::Setup -setup_Overlay(BasicConfig const& config) +setup_Overlay(BasicConfig const& config, beast::Journal j) { Overlay::Setup setup; @@ -1514,6 +1515,14 @@ setup_Overlay(BasicConfig const& config) if (ec || beast::IP::is_private(setup.public_ip)) Throw("Configured public IP is invalid"); } + + set(setup.verifyEndpoints, true, "verify_endpoints", section); + if (!setup.verifyEndpoints) + { + JLOG(j.warn()) << "Endpoint verification is disabled. This is a " + "security risk and should only be used for " + "testing."; + } } { diff --git a/src/xrpld/overlay/make_Overlay.h b/src/xrpld/overlay/make_Overlay.h index 3476026562..d208940671 100644 --- a/src/xrpld/overlay/make_Overlay.h +++ b/src/xrpld/overlay/make_Overlay.h @@ -30,7 +30,7 @@ namespace ripple { Overlay::Setup -setup_Overlay(BasicConfig const& config); +setup_Overlay(BasicConfig const& config, beast::Journal j); /** Creates the implementation of Overlay. */ std::unique_ptr diff --git a/src/xrpld/peerfinder/PeerfinderManager.h b/src/xrpld/peerfinder/PeerfinderManager.h index b7ea738a81..552c74e198 100644 --- a/src/xrpld/peerfinder/PeerfinderManager.h +++ b/src/xrpld/peerfinder/PeerfinderManager.h @@ -79,6 +79,9 @@ struct Config /** Limit how many incoming connections we allow per IP */ int ipLimit; + /** `true` if we want to verify endpoints in TMEndpoints messages */ + bool verifyEndpoints = false; + //-------------------------------------------------------------------------- /** Create a configuration with default values. */ @@ -101,6 +104,8 @@ struct Config * @param port server's listening port * @param validationPublicKey true if validation public key is not empty * @param ipLimit limit of incoming connections per IP + * @param verifyEndpoints `true` if we want to verify endpoints in + * TMEndpoints messages * @return PeerFinder::Config */ static Config @@ -108,10 +113,11 @@ struct Config ripple::Config const& config, std::uint16_t port, bool validationPublicKey, - int ipLimit); + int ipLimit, + bool verifyEndpoints); friend bool - operator==(Config const& lhs, Config const& rhs); + operator==(Config const& lhs, Config const& rhs) = default; }; //------------------------------------------------------------------------------ diff --git a/src/xrpld/peerfinder/detail/Logic.h b/src/xrpld/peerfinder/detail/Logic.h index 74bec8431e..56077303a3 100644 --- a/src/xrpld/peerfinder/detail/Logic.h +++ b/src/xrpld/peerfinder/detail/Logic.h @@ -754,7 +754,7 @@ public: } // Discard invalid addresses - if (!is_valid_address(ep.address)) + if (!is_valid_address(ep.address) && config_.verifyEndpoints) { JLOG(m_journal.debug()) << beast::leftw(18) << "Endpoints drop " << ep.address << " as invalid"; @@ -1152,6 +1152,8 @@ public: { if (is_unspecified(address)) return false; + if (is_loopback(address)) + return false; if (!is_public(address)) return false; if (address.port() == 0) diff --git a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp index 30eb778770..79ee65bf0a 100644 --- a/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp +++ b/src/xrpld/peerfinder/detail/PeerfinderConfig.cpp @@ -34,17 +34,6 @@ Config::Config() { } -bool -operator==(Config const& lhs, Config const& rhs) -{ - return lhs.autoConnect == rhs.autoConnect && - lhs.peerPrivate == rhs.peerPrivate && - lhs.wantIncoming == rhs.wantIncoming && lhs.inPeers == rhs.inPeers && - lhs.maxPeers == rhs.maxPeers && lhs.outPeers == rhs.outPeers && - lhs.features == lhs.features && lhs.ipLimit == rhs.ipLimit && - lhs.listeningPort == rhs.listeningPort; -} - std::size_t Config::calcOutPeers() const { @@ -83,6 +72,7 @@ Config::onWrite(beast::PropertyStream::Map& map) map["port"] = listeningPort; map["features"] = features; map["ip_limit"] = ipLimit; + map["verify_endpoints"] = verifyEndpoints; } Config @@ -90,7 +80,8 @@ Config::makeConfig( ripple::Config const& cfg, std::uint16_t port, bool validationPublicKey, - int ipLimit) + int ipLimit, + bool verifyEndpoints) { PeerFinder::Config config; @@ -140,6 +131,7 @@ Config::makeConfig( config.listeningPort = port; config.features = ""; config.ipLimit = ipLimit; + config.verifyEndpoints = verifyEndpoints; // Enforce business rules config.applyTuning();