diff --git a/webrtc/p2p/base/turnport.cc b/webrtc/p2p/base/turnport.cc index 2d5e30e663..a34d25b612 100644 --- a/webrtc/p2p/base/turnport.cc +++ b/webrtc/p2p/base/turnport.cc @@ -639,6 +639,14 @@ bool TurnPort::SetAlternateServer(const rtc::SocketAddress& address) { return false; } + // Block redirects to a loopback address. + // See: https://bugs.chromium.org/p/chromium/issues/detail?id=649118 + if (address.IsLoopbackIP()) { + LOG_J(LS_WARNING, this) + << "Blocking attempted redirect to loopback address."; + return false; + } + LOG_J(LS_INFO, this) << "Redirecting from TURN server [" << server_address_.address.ToSensitiveString() << "] to TURN server [" diff --git a/webrtc/p2p/base/turnport_unittest.cc b/webrtc/p2p/base/turnport_unittest.cc index edb345447b..a8153eed26 100644 --- a/webrtc/p2p/base/turnport_unittest.cc +++ b/webrtc/p2p/base/turnport_unittest.cc @@ -29,6 +29,7 @@ #include "webrtc/base/helpers.h" #include "webrtc/base/logging.h" #include "webrtc/base/physicalsocketserver.h" +#include "webrtc/base/socketadapters.h" #include "webrtc/base/socketaddress.h" #include "webrtc/base/ssladapter.h" #include "webrtc/base/thread.h" @@ -441,6 +442,57 @@ class TurnPortTest : public testing::Test, ASSERT_EQ(0U, turn_port_->Candidates().size()); } + // A certain security exploit works by redirecting to a loopback address, + // which doesn't ever actually make sense. So redirects to loopback should + // be treated as errors. + // See: https://bugs.chromium.org/p/chromium/issues/detail?id=649118 + void TestTurnAlternateServerLoopback(ProtocolType protocol_type, bool ipv6) { + const SocketAddress& local_address = ipv6 ? kLocalIPv6Addr : kLocalAddr1; + const SocketAddress& server_address = + ipv6 ? kTurnIPv6IntAddr : kTurnIntAddr; + + std::vector redirect_addresses; + SocketAddress loopback_address(ipv6 ? "::1" : "127.0.0.1", + TURN_SERVER_PORT); + redirect_addresses.push_back(loopback_address); + + // Make a socket and bind it to the local port, to make extra sure no + // packet is sent to this address. + std::unique_ptr loopback_socket(ss_->CreateSocket( + protocol_type == PROTO_UDP ? SOCK_DGRAM : SOCK_STREAM)); + ASSERT_NE(nullptr, loopback_socket.get()); + ASSERT_EQ(0, loopback_socket->Bind(loopback_address)); + if (protocol_type == PROTO_TCP) { + ASSERT_EQ(0, loopback_socket->Listen(1)); + } + + TestTurnRedirector redirector(redirect_addresses); + + turn_server_.AddInternalSocket(server_address, protocol_type); + turn_server_.set_redirect_hook(&redirector); + CreateTurnPort(local_address, kTurnUsername, kTurnPassword, + ProtocolAddress(server_address, protocol_type)); + + turn_port_->PrepareAddress(); + EXPECT_TRUE_SIMULATED_WAIT( + turn_error_, + (protocol_type == PROTO_TCP ? kSimulatedRtt * 3 : kSimulatedRtt * 2), + fake_clock_); + + // Wait for some extra time, and make sure no packets were received on the + // loopback port we created (or in the case of TCP, no connection attempt + // occurred). + SIMULATED_WAIT(false, kSimulatedRtt, fake_clock_); + if (protocol_type == PROTO_UDP) { + char buf[1]; + EXPECT_EQ(-1, loopback_socket->Recv(&buf, 1, nullptr)); + } else { + std::unique_ptr accepted_socket( + loopback_socket->Accept(nullptr)); + EXPECT_EQ(nullptr, accepted_socket.get()); + } + } + void TestTurnConnection(ProtocolType protocol_type) { // Create ports and prepare addresses. PrepareTurnAndUdpPorts(protocol_type); @@ -917,6 +969,23 @@ TEST_F(TurnPortTest, TestTurnAlternateServerDetectRepetitionTCP) { TestTurnAlternateServerDetectRepetition(PROTO_TCP); } +// Test catching the case of a redirect to loopback. +TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackUdpIpv4) { + TestTurnAlternateServerLoopback(PROTO_UDP, false); +} + +TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackUdpIpv6) { + TestTurnAlternateServerLoopback(PROTO_UDP, true); +} + +TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackTcpIpv4) { + TestTurnAlternateServerLoopback(PROTO_TCP, false); +} + +TEST_F(TurnPortTest, TestTurnAlternateServerLoopbackTcpIpv6) { + TestTurnAlternateServerLoopback(PROTO_TCP, true); +} + // Do a TURN allocation and try to send a packet to it from the outside. // The packet should be dropped. Then, try to send a packet from TURN to the // outside. It should reach its destination. Finally, try again from the