mirror of
https://github.com/mollyim/webrtc.git
synced 2025-05-13 05:40:42 +01:00

In the example below, the association is being established between peer A and Z, and A is the initiating party. Before this CL, when an association was about to be established, Z would after having received the INIT chunk, persist state in the socket about which verification tag and initial TSN that was picked. These would be re-generated on every incoming INIT (that's fine), but when A had extracted the cookie from INIT_ACK and sent a reply (COOKIE_ECHO) with the state cookie, that could fail validation when it's received by Z, if the sent cookie was not the most recent one or if the COOKIE_ECHO had a verification tag coming not from the most recent INIT_ACK, because Z had replaced the state in the socket with the one generated when the second INIT_ACK chunk was generated - state it used for validation of future received data. In other words: A -> INIT 1 <timeout> A -> INIT 2 (retransmission of INIT 1) INIT 1 -> Z - sends INIT_ACK 1 with verification_tag=1, initial_tsn=1, cookie 1 (and records these to socket state) INIT 2 -> Z - sends INIT_ACK 2 with verification_tag=2, initial_tsn=2, cookie 2 (replaces socket state with the new data) INIT_ACK 1 -> A -> sends COOKIE_ECHO with verification_tag=1, cookie 1 COOKIE_ECHO (cookie 1) -> Z <FAILS, as the state isn't as expected>. The solution is really to do what RFC4960 says, to not maintain any state as the receiving peer until COOKIE_ECHO has been received. This was initially not done because the underlying reason why this is important in SCTP is to avoid denial of service, and this is why SCTP has the four-way handshake. But for Data Channels - SCTP over DTLS - this attack vector isn't available. So the implementation was "simplified" by keeping socket state instead of encoding it in the state cookie, but that obviously had downsides. So with this CL, the non-initiating peer in connection establishment doesn't keep any socket state, and puts all that state in the state cookie instead. This allows any COOKIE_ECHO to be received by Z. Bug: webrtc:15712 Change-Id: I596c7330ce27292612d3c9f86b21c712f6f4e408 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/330440 Commit-Queue: Victor Boivie <boivie@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#41340}
67 lines
3 KiB
C++
67 lines
3 KiB
C++
/*
|
|
* Copyright (c) 2021 The WebRTC project authors. All Rights Reserved.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license
|
|
* that can be found in the LICENSE file in the root of the source
|
|
* tree. An additional intellectual property rights grant can be found
|
|
* in the file PATENTS. All contributing project authors may
|
|
* be found in the AUTHORS file in the root of the source tree.
|
|
*/
|
|
#include "net/dcsctp/socket/state_cookie.h"
|
|
|
|
#include "net/dcsctp/testing/testing_macros.h"
|
|
#include "rtc_base/gunit.h"
|
|
#include "test/gmock.h"
|
|
|
|
namespace dcsctp {
|
|
namespace {
|
|
using ::testing::SizeIs;
|
|
|
|
TEST(StateCookieTest, SerializeAndDeserialize) {
|
|
Capabilities capabilities = {.partial_reliability = true,
|
|
.message_interleaving = false,
|
|
.reconfig = true,
|
|
.zero_checksum = true,
|
|
.negotiated_maximum_incoming_streams = 123,
|
|
.negotiated_maximum_outgoing_streams = 234};
|
|
StateCookie cookie(/*peer_tag=*/VerificationTag(123),
|
|
/*my_tag=*/VerificationTag(321),
|
|
/*peer_initial_tsn=*/TSN(456), /*my_initial_tsn=*/TSN(654),
|
|
/*a_rwnd=*/789, TieTag(101112), capabilities);
|
|
std::vector<uint8_t> serialized = cookie.Serialize();
|
|
EXPECT_THAT(serialized, SizeIs(StateCookie::kCookieSize));
|
|
ASSERT_HAS_VALUE_AND_ASSIGN(StateCookie deserialized,
|
|
StateCookie::Deserialize(serialized));
|
|
EXPECT_EQ(deserialized.peer_tag(), VerificationTag(123));
|
|
EXPECT_EQ(deserialized.my_tag(), VerificationTag(321));
|
|
EXPECT_EQ(deserialized.peer_initial_tsn(), TSN(456));
|
|
EXPECT_EQ(deserialized.my_initial_tsn(), TSN(654));
|
|
EXPECT_EQ(deserialized.a_rwnd(), 789u);
|
|
EXPECT_EQ(deserialized.tie_tag(), TieTag(101112));
|
|
EXPECT_TRUE(deserialized.capabilities().partial_reliability);
|
|
EXPECT_FALSE(deserialized.capabilities().message_interleaving);
|
|
EXPECT_TRUE(deserialized.capabilities().reconfig);
|
|
EXPECT_TRUE(deserialized.capabilities().zero_checksum);
|
|
EXPECT_EQ(deserialized.capabilities().negotiated_maximum_incoming_streams,
|
|
123);
|
|
EXPECT_EQ(deserialized.capabilities().negotiated_maximum_outgoing_streams,
|
|
234);
|
|
}
|
|
|
|
TEST(StateCookieTest, ValidateMagicValue) {
|
|
Capabilities capabilities = {.partial_reliability = true,
|
|
.message_interleaving = false,
|
|
.reconfig = true};
|
|
StateCookie cookie(/*peer_tag=*/VerificationTag(123),
|
|
/*my_tag=*/VerificationTag(321),
|
|
/*peer_initial_tsn=*/TSN(456), /*my_initial_tsn=*/TSN(654),
|
|
/*a_rwnd=*/789, TieTag(101112), capabilities);
|
|
std::vector<uint8_t> serialized = cookie.Serialize();
|
|
ASSERT_THAT(serialized, SizeIs(StateCookie::kCookieSize));
|
|
|
|
absl::string_view magic(reinterpret_cast<const char*>(serialized.data()), 8);
|
|
EXPECT_EQ(magic, "dcSCTP00");
|
|
}
|
|
|
|
} // namespace
|
|
} // namespace dcsctp
|