Index: webrtc/p2p/base/turnport_unittest.cc |
diff --git a/webrtc/p2p/base/turnport_unittest.cc b/webrtc/p2p/base/turnport_unittest.cc |
index edb345447bf592187f7da4857ff823cd7ec9039d..a8153eed268a8ec851bb0a213b7453fc4f251f4e 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<rtc::SocketAddress> 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<rtc::Socket> 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<rtc::Socket> 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 |