Validate ICE ufrag/pwd according to the spec
https://tools.ietf.org/html/draft-ietf-mmusic-ice-sip-sdp-39#section-5.4
Bug: chromium:1044521
Change-Id: Ia95718437dfc270b52cdf822e861a3da7cbbab76
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/167281
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Qingsi Wang <qingsi@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30375}
diff --git a/p2p/BUILD.gn b/p2p/BUILD.gn
index f3b5dd4..ae49deb 100644
--- a/p2p/BUILD.gn
+++ b/p2p/BUILD.gn
@@ -193,6 +193,7 @@
"base/stun_server_unittest.cc",
"base/tcp_port_unittest.cc",
"base/transport_description_factory_unittest.cc",
+ "base/transport_description_unittest.cc",
"base/turn_port_unittest.cc",
"base/turn_server_unittest.cc",
"client/basic_port_allocator_unittest.cc",
diff --git a/p2p/base/transport_description.cc b/p2p/base/transport_description.cc
index b0a21d6d..dd7e38e 100644
--- a/p2p/base/transport_description.cc
+++ b/p2p/base/transport_description.cc
@@ -10,10 +10,86 @@
#include "p2p/base/transport_description.h"
+#include "absl/strings/ascii.h"
#include "absl/strings/match.h"
+#include "p2p/base/p2p_constants.h"
#include "rtc_base/arraysize.h"
+#include "rtc_base/strings/string_builder.h"
+
+using webrtc::RTCError;
+using webrtc::RTCErrorOr;
+using webrtc::RTCErrorType;
namespace cricket {
+namespace {
+
+bool IsIceChar(char c) {
+ return absl::ascii_isalnum(c) || c == '+' || c == '/';
+}
+
+RTCErrorOr<std::string> ParseIceUfrag(absl::string_view raw_ufrag) {
+ if (!(ICE_UFRAG_MIN_LENGTH <= raw_ufrag.size() &&
+ raw_ufrag.size() <= ICE_UFRAG_MAX_LENGTH)) {
+ rtc::StringBuilder sb;
+ sb << "ICE ufrag must be between " << ICE_UFRAG_MIN_LENGTH << " and "
+ << ICE_UFRAG_MAX_LENGTH << " characters long.";
+ return RTCError(RTCErrorType::SYNTAX_ERROR, sb.Release());
+ }
+
+ if (!absl::c_all_of(raw_ufrag, IsIceChar)) {
+ return RTCError(
+ RTCErrorType::SYNTAX_ERROR,
+ "ICE ufrag must contain only alphanumeric characters, '+', and '/'.");
+ }
+
+ return std::string(raw_ufrag);
+}
+
+RTCErrorOr<std::string> ParseIcePwd(absl::string_view raw_pwd) {
+ if (!(ICE_PWD_MIN_LENGTH <= raw_pwd.size() &&
+ raw_pwd.size() <= ICE_PWD_MAX_LENGTH)) {
+ rtc::StringBuilder sb;
+ sb << "ICE pwd must be between " << ICE_PWD_MIN_LENGTH << " and "
+ << ICE_PWD_MAX_LENGTH << " characters long.";
+ return RTCError(RTCErrorType::SYNTAX_ERROR, sb.Release());
+ }
+
+ if (!absl::c_all_of(raw_pwd, IsIceChar)) {
+ return RTCError(
+ RTCErrorType::SYNTAX_ERROR,
+ "ICE pwd must contain only alphanumeric characters, '+', and '/'.");
+ }
+
+ return std::string(raw_pwd);
+}
+
+} // namespace
+
+// static
+RTCErrorOr<IceParameters> IceParameters::Parse(absl::string_view raw_ufrag,
+ absl::string_view raw_pwd) {
+ // For legacy protocols.
+ // TODO(zhihuang): Remove this once the legacy protocol is no longer
+ // supported.
+ if (raw_ufrag.empty() && raw_pwd.empty()) {
+ return IceParameters();
+ }
+
+ auto ufrag_result = ParseIceUfrag(raw_ufrag);
+ if (!ufrag_result.ok()) {
+ return ufrag_result.MoveError();
+ }
+
+ auto pwd_result = ParseIcePwd(raw_pwd);
+ if (!pwd_result.ok()) {
+ return pwd_result.MoveError();
+ }
+
+ IceParameters parameters;
+ parameters.ufrag = ufrag_result.MoveValue();
+ parameters.pwd = pwd_result.MoveValue();
+ return parameters;
+}
bool StringToConnectionRole(const std::string& role_str, ConnectionRole* role) {
const char* const roles[] = {
diff --git a/p2p/base/transport_description.h b/p2p/base/transport_description.h
index 15e2e91..e7934ba 100644
--- a/p2p/base/transport_description.h
+++ b/p2p/base/transport_description.h
@@ -17,6 +17,7 @@
#include "absl/algorithm/container.h"
#include "absl/types/optional.h"
+#include "api/rtc_error.h"
#include "p2p/base/p2p_constants.h"
#include "rtc_base/ssl_fingerprint.h"
@@ -56,6 +57,11 @@
};
struct IceParameters {
+ // Constructs an IceParameters from a user-provided ufrag/pwd combination.
+ // Returns a SyntaxError if the ufrag or pwd are malformed.
+ static webrtc::RTCErrorOr<IceParameters> Parse(absl::string_view raw_ufrag,
+ absl::string_view raw_pwd);
+
// TODO(honghaiz): Include ICE mode in this structure to match the ORTC
// struct:
// http://ortc.org/wp-content/uploads/2016/03/ortc.html#idl-def-RTCIceParameters
diff --git a/p2p/base/transport_description_unittest.cc b/p2p/base/transport_description_unittest.cc
new file mode 100644
index 0000000..41d7336
--- /dev/null
+++ b/p2p/base/transport_description_unittest.cc
@@ -0,0 +1,58 @@
+/*
+ * Copyright 2020 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 "p2p/base/transport_description.h"
+#include "test/gtest.h"
+
+using webrtc::RTCErrorType;
+
+namespace cricket {
+
+TEST(IceParameters, SuccessfulParse) {
+ auto result = IceParameters::Parse("ufrag", "22+characters+long+pwd");
+ ASSERT_TRUE(result.ok());
+ IceParameters parameters = result.MoveValue();
+ EXPECT_EQ("ufrag", parameters.ufrag);
+ EXPECT_EQ("22+characters+long+pwd", parameters.pwd);
+}
+
+TEST(IceParameters, FailedParseShortUfrag) {
+ auto result = IceParameters::Parse("3ch", "22+characters+long+pwd");
+ EXPECT_EQ(RTCErrorType::SYNTAX_ERROR, result.error().type());
+}
+
+TEST(IceParameters, FailedParseLongUfrag) {
+ std::string ufrag(257, '+');
+ auto result = IceParameters::Parse(ufrag, "22+characters+long+pwd");
+ EXPECT_EQ(RTCErrorType::SYNTAX_ERROR, result.error().type());
+}
+
+TEST(IceParameters, FailedParseShortPwd) {
+ auto result = IceParameters::Parse("ufrag", "21+character+long+pwd");
+ EXPECT_EQ(RTCErrorType::SYNTAX_ERROR, result.error().type());
+}
+
+TEST(IceParameters, FailedParseLongPwd) {
+ std::string pwd(257, '+');
+ auto result = IceParameters::Parse("ufrag", pwd);
+ EXPECT_EQ(RTCErrorType::SYNTAX_ERROR, result.error().type());
+}
+
+TEST(IceParameters, FailedParseBadUfragChar) {
+ auto result = IceParameters::Parse("ufrag\r\n", "22+characters+long+pwd");
+ EXPECT_EQ(RTCErrorType::SYNTAX_ERROR, result.error().type());
+}
+
+TEST(IceParameters, FailedParseBadPwdChar) {
+ auto result = IceParameters::Parse("ufrag", "22+characters+long+pwd\r\n");
+ EXPECT_EQ(RTCErrorType::SYNTAX_ERROR, result.error().type());
+}
+
+} // namespace cricket
diff --git a/pc/jsep_transport.cc b/pc/jsep_transport.cc
index 37f3162..8a555f2 100644
--- a/pc/jsep_transport.cc
+++ b/pc/jsep_transport.cc
@@ -31,28 +31,6 @@
namespace cricket {
-static bool VerifyIceParams(const JsepTransportDescription& jsep_description) {
- // For legacy protocols.
- // TODO(zhihuang): Remove this once the legacy protocol is no longer
- // supported.
- if (jsep_description.transport_desc.ice_ufrag.empty() &&
- jsep_description.transport_desc.ice_pwd.empty()) {
- return true;
- }
-
- if (jsep_description.transport_desc.ice_ufrag.length() <
- ICE_UFRAG_MIN_LENGTH ||
- jsep_description.transport_desc.ice_ufrag.length() >
- ICE_UFRAG_MAX_LENGTH) {
- return false;
- }
- if (jsep_description.transport_desc.ice_pwd.length() < ICE_PWD_MIN_LENGTH ||
- jsep_description.transport_desc.ice_pwd.length() > ICE_PWD_MAX_LENGTH) {
- return false;
- }
- return true;
-}
-
JsepTransportDescription::JsepTransportDescription() {}
JsepTransportDescription::JsepTransportDescription(
@@ -199,10 +177,17 @@
webrtc::RTCError error;
RTC_DCHECK_RUN_ON(network_thread_);
- if (!VerifyIceParams(jsep_description)) {
+
+ webrtc::RTCErrorOr<IceParameters> ice_parameters_result =
+ IceParameters::Parse(jsep_description.transport_desc.ice_ufrag,
+ jsep_description.transport_desc.ice_pwd);
+ if (!ice_parameters_result.ok()) {
+ rtc::StringBuilder sb;
+ sb << "Invalid ICE parameters: " << ice_parameters_result.error().message();
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
- "Invalid ice-ufrag or ice-pwd length.");
+ sb.Release());
}
+ IceParameters ice_parameters = ice_parameters_result.MoveValue();
if (!SetRtcpMux(jsep_description.rtcp_mux_enabled, type,
ContentSource::CS_LOCAL)) {
@@ -233,8 +218,7 @@
local_description_ != nullptr &&
IceCredentialsChanged(local_description_->transport_desc.ice_ufrag,
local_description_->transport_desc.ice_pwd,
- jsep_description.transport_desc.ice_ufrag,
- jsep_description.transport_desc.ice_pwd);
+ ice_parameters.ufrag, ice_parameters.pwd);
local_description_.reset(new JsepTransportDescription(jsep_description));
rtc::SSLFingerprint* local_fp =
@@ -252,11 +236,13 @@
{
rtc::CritScope scope(&accessor_lock_);
RTC_DCHECK(rtp_dtls_transport_->internal());
- SetLocalIceParameters(rtp_dtls_transport_->internal()->ice_transport());
+ rtp_dtls_transport_->internal()->ice_transport()->SetIceParameters(
+ ice_parameters);
if (rtcp_dtls_transport_) {
RTC_DCHECK(rtcp_dtls_transport_->internal());
- SetLocalIceParameters(rtcp_dtls_transport_->internal()->ice_transport());
+ rtcp_dtls_transport_->internal()->ice_transport()->SetIceParameters(
+ ice_parameters);
}
}
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
@@ -286,11 +272,18 @@
webrtc::RTCError error;
RTC_DCHECK_RUN_ON(network_thread_);
- if (!VerifyIceParams(jsep_description)) {
+
+ webrtc::RTCErrorOr<IceParameters> ice_parameters_result =
+ IceParameters::Parse(jsep_description.transport_desc.ice_ufrag,
+ jsep_description.transport_desc.ice_pwd);
+ if (!ice_parameters_result.ok()) {
remote_description_.reset();
+ rtc::StringBuilder sb;
+ sb << "Invalid ICE parameters: " << ice_parameters_result.error().message();
return webrtc::RTCError(webrtc::RTCErrorType::INVALID_PARAMETER,
- "Invalid ice-ufrag or ice-pwd length.");
+ sb.Release());
}
+ IceParameters ice_parameters = ice_parameters_result.MoveValue();
if (!SetRtcpMux(jsep_description.rtcp_mux_enabled, type,
ContentSource::CS_REMOTE)) {
@@ -324,10 +317,11 @@
remote_description_.reset(new JsepTransportDescription(jsep_description));
RTC_DCHECK(rtp_dtls_transport());
- SetRemoteIceParameters(rtp_dtls_transport()->ice_transport());
+ SetRemoteIceParameters(ice_parameters, rtp_dtls_transport()->ice_transport());
if (rtcp_dtls_transport()) {
- SetRemoteIceParameters(rtcp_dtls_transport()->ice_transport());
+ SetRemoteIceParameters(ice_parameters,
+ rtcp_dtls_transport()->ice_transport());
}
// If PRANSWER/ANSWER is set, we should decide transport protocol type.
@@ -456,21 +450,13 @@
}
}
-void JsepTransport::SetLocalIceParameters(IceTransportInternal* ice_transport) {
- RTC_DCHECK_RUN_ON(network_thread_);
- RTC_DCHECK(ice_transport);
- RTC_DCHECK(local_description_);
- ice_transport->SetIceParameters(
- local_description_->transport_desc.GetIceParameters());
-}
-
void JsepTransport::SetRemoteIceParameters(
+ const IceParameters& ice_parameters,
IceTransportInternal* ice_transport) {
RTC_DCHECK_RUN_ON(network_thread_);
RTC_DCHECK(ice_transport);
RTC_DCHECK(remote_description_);
- ice_transport->SetRemoteIceParameters(
- remote_description_->transport_desc.GetIceParameters());
+ ice_transport->SetRemoteIceParameters(ice_parameters);
ice_transport->SetRemoteIceMode(remote_description_->transport_desc.ice_mode);
}
diff --git a/pc/jsep_transport.h b/pc/jsep_transport.h
index 6edf0ae..6d88def 100644
--- a/pc/jsep_transport.h
+++ b/pc/jsep_transport.h
@@ -297,12 +297,9 @@
ConnectionRole remote_connection_role,
absl::optional<rtc::SSLRole>* negotiated_dtls_role);
- // Pushes down the ICE parameters from the local description, such
- // as the ICE ufrag and pwd.
- void SetLocalIceParameters(IceTransportInternal* ice);
-
// Pushes down the ICE parameters from the remote description.
- void SetRemoteIceParameters(IceTransportInternal* ice);
+ void SetRemoteIceParameters(const IceParameters& ice_parameters,
+ IceTransportInternal* ice);
// Pushes down the DTLS parameters obtained via negotiation.
webrtc::RTCError SetNegotiatedDtlsParameters(
diff --git a/pc/peer_connection_ice_unittest.cc b/pc/peer_connection_ice_unittest.cc
index 7d8ce57..ab5a8f4 100644
--- a/pc/peer_connection_ice_unittest.cc
+++ b/pc/peer_connection_ice_unittest.cc
@@ -1202,10 +1202,10 @@
auto offer = caller->CreateOffer();
auto* offer_transport_desc = GetFirstTransportDescription(offer.get());
if (offer_new_ufrag_) {
- offer_transport_desc->ice_ufrag += "_new";
+ offer_transport_desc->ice_ufrag += "+new";
}
if (offer_new_pwd_) {
- offer_transport_desc->ice_pwd += "_new";
+ offer_transport_desc->ice_pwd += "+new";
}
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));
@@ -1248,8 +1248,8 @@
// Signal ICE restart on the first media section.
auto* offer_transport_desc = GetFirstTransportDescription(offer.get());
- offer_transport_desc->ice_ufrag += "_new";
- offer_transport_desc->ice_pwd += "_new";
+ offer_transport_desc->ice_ufrag += "+new";
+ offer_transport_desc->ice_pwd += "+new";
ASSERT_TRUE(callee->SetRemoteDescription(std::move(offer)));