diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc index ebce40ca5e..80c8a11a39 100644 --- a/webrtc/api/peerconnection.cc +++ b/webrtc/api/peerconnection.cc @@ -306,8 +306,14 @@ bool ParseIceServerUrl(const PeerConnectionInterface::IceServer& server, break; case TURN: case TURNS: { - turn_servers->push_back(cricket::RelayServerConfig( - address, port, username, server.password, turn_transport_type)); + cricket::RelayServerConfig config = cricket::RelayServerConfig( + address, port, username, server.password, turn_transport_type); + if (server.tls_cert_policy == + PeerConnectionInterface::kTlsCertPolicyInsecureNoCheck) { + config.tls_cert_policy = + cricket::TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK; + } + turn_servers->push_back(config); break; } case INVALID: diff --git a/webrtc/api/peerconnection_unittest.cc b/webrtc/api/peerconnection_unittest.cc index b213ecb00f..4876b40bc8 100644 --- a/webrtc/api/peerconnection_unittest.cc +++ b/webrtc/api/peerconnection_unittest.cc @@ -40,6 +40,7 @@ #include "webrtc/base/virtualsocketserver.h" #include "webrtc/media/engine/fakewebrtcvideoengine.h" #include "webrtc/p2p/base/p2pconstants.h" +#include "webrtc/p2p/base/portinterface.h" #include "webrtc/p2p/base/sessiondescription.h" #include "webrtc/p2p/base/testturnserver.h" #include "webrtc/p2p/client/basicportallocator.h" @@ -2619,11 +2620,21 @@ class IceServerParsingTest : public testing::Test { bool ParseUrl(const std::string& url, const std::string& username, const std::string& password) { + return ParseUrl( + url, username, password, + PeerConnectionInterface::TlsCertPolicy::kTlsCertPolicySecure); + } + + bool ParseUrl(const std::string& url, + const std::string& username, + const std::string& password, + PeerConnectionInterface::TlsCertPolicy tls_certificate_policy) { PeerConnectionInterface::IceServers servers; PeerConnectionInterface::IceServer server; server.urls.push_back(url); server.username = username; server.password = password; + server.tls_cert_policy = tls_certificate_policy; servers.push_back(server); return webrtc::ParseIceServers(servers, &stun_servers_, &turn_servers_); } @@ -2655,6 +2666,18 @@ TEST_F(IceServerParsingTest, ParseStunPrefixes) { EXPECT_EQ(0U, stun_servers_.size()); EXPECT_EQ(1U, turn_servers_.size()); EXPECT_EQ(cricket::PROTO_TLS, turn_servers_[0].ports[0].proto); + EXPECT_TRUE(turn_servers_[0].tls_cert_policy == + cricket::TlsCertPolicy::TLS_CERT_POLICY_SECURE); + turn_servers_.clear(); + + EXPECT_TRUE(ParseUrl( + "turns:hostname", "", "", + PeerConnectionInterface::TlsCertPolicy::kTlsCertPolicyInsecureNoCheck)); + EXPECT_EQ(0U, stun_servers_.size()); + EXPECT_EQ(1U, turn_servers_.size()); + EXPECT_TRUE(turn_servers_[0].tls_cert_policy == + cricket::TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK); + EXPECT_EQ(cricket::PROTO_TLS, turn_servers_[0].ports[0].proto); turn_servers_.clear(); // invalid prefixes diff --git a/webrtc/api/peerconnectioninterface.h b/webrtc/api/peerconnectioninterface.h index d4daf2435e..078db8a2ba 100644 --- a/webrtc/api/peerconnectioninterface.h +++ b/webrtc/api/peerconnectioninterface.h @@ -209,15 +209,28 @@ class PeerConnectionInterface : public rtc::RefCountInterface { kIceConnectionMax, }; + // TLS certificate policy. + enum TlsCertPolicy { + // For TLS based protocols, ensure the connection is secure by not + // circumventing certificate validation. + kTlsCertPolicySecure, + // For TLS based protocols, disregard security completely by skipping + // certificate validation. This is insecure and should never be used unless + // security is irrelevant in that particular context. + kTlsCertPolicyInsecureNoCheck, + }; + struct IceServer { // TODO(jbauch): Remove uri when all code using it has switched to urls. std::string uri; std::vector urls; std::string username; std::string password; + TlsCertPolicy tls_cert_policy = kTlsCertPolicySecure; + bool operator==(const IceServer& o) const { return uri == o.uri && urls == o.urls && username == o.username && - password == o.password; + password == o.password && tls_cert_policy == o.tls_cert_policy; } bool operator!=(const IceServer& o) const { return !(*this == o); } }; diff --git a/webrtc/p2p/base/basicpacketsocketfactory.cc b/webrtc/p2p/base/basicpacketsocketfactory.cc index 51e9b07fc0..b794904411 100644 --- a/webrtc/p2p/base/basicpacketsocketfactory.cc +++ b/webrtc/p2p/base/basicpacketsocketfactory.cc @@ -87,8 +87,8 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket( return NULL; } - // If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket. - if (opts & PacketSocketFactory::OPT_SSLTCP) { + // If using fake TLS, wrap the TCP socket in a pseudo-SSL socket. + if (opts & PacketSocketFactory::OPT_TLS_FAKE) { ASSERT(!(opts & PacketSocketFactory::OPT_TLS)); socket = new AsyncSSLSocket(socket); } @@ -129,15 +129,24 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket( proxy_info.username, proxy_info.password); } - // If using TLS, wrap the socket in an SSL adapter. - if (opts & PacketSocketFactory::OPT_TLS) { - ASSERT(!(opts & PacketSocketFactory::OPT_SSLTCP)); + // Assert that at most one TLS option is used. + int tlsOpts = + opts & (PacketSocketFactory::OPT_TLS | PacketSocketFactory::OPT_TLS_FAKE | + PacketSocketFactory::OPT_TLS_INSECURE); + ASSERT((tlsOpts & (tlsOpts - 1)) == 0); + if ((tlsOpts & PacketSocketFactory::OPT_TLS) || + (tlsOpts & PacketSocketFactory::OPT_TLS_INSECURE)) { + // Using TLS, wrap the socket in an SSL adapter. SSLAdapter* ssl_adapter = SSLAdapter::Create(socket); if (!ssl_adapter) { return NULL; } + if (tlsOpts & PacketSocketFactory::OPT_TLS_INSECURE) { + ssl_adapter->set_ignore_bad_cert(true); + } + socket = ssl_adapter; if (ssl_adapter->StartSSL(remote_address.hostname().c_str(), false) != 0) { @@ -145,9 +154,8 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket( return NULL; } - // If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket. - } else if (opts & PacketSocketFactory::OPT_SSLTCP) { - ASSERT(!(opts & PacketSocketFactory::OPT_TLS)); + } else if (tlsOpts & PacketSocketFactory::OPT_TLS_FAKE) { + // Using fake TLS, wrap the TCP socket in a pseudo-SSL socket. socket = new AsyncSSLSocket(socket); } diff --git a/webrtc/p2p/base/packetsocketfactory.h b/webrtc/p2p/base/packetsocketfactory.h index 290d9ca844..da0f8b75b2 100644 --- a/webrtc/p2p/base/packetsocketfactory.h +++ b/webrtc/p2p/base/packetsocketfactory.h @@ -22,9 +22,12 @@ class AsyncResolverInterface; class PacketSocketFactory { public: enum Options { - OPT_SSLTCP = 0x01, // Pseudo-TLS. - OPT_TLS = 0x02, OPT_STUN = 0x04, + + // The TLS options below are mutually exclusive. + OPT_TLS = 0x02, // Real and secure TLS. + OPT_TLS_FAKE = 0x01, // Fake TLS with a dummy SSL handshake. + OPT_TLS_INSECURE = 0x08, // Insecure TLS without certificate validation. }; PacketSocketFactory() { } diff --git a/webrtc/p2p/base/portallocator.h b/webrtc/p2p/base/portallocator.h index 93de0e141f..51474297f7 100644 --- a/webrtc/p2p/base/portallocator.h +++ b/webrtc/p2p/base/portallocator.h @@ -95,6 +95,17 @@ enum { CF_ALL = 0x7, }; +// TLS certificate policy. +enum class TlsCertPolicy { + // For TLS based protocols, ensure the connection is secure by not + // circumventing certificate validation. + TLS_CERT_POLICY_SECURE, + // For TLS based protocols, disregard security completely by skipping + // certificate validation. This is insecure and should never be used unless + // security is irrelevant in that particular context. + TLS_CERT_POLICY_INSECURE_NO_CHECK, +}; + // TODO(deadbeef): Rename to TurnCredentials (and username to ufrag). struct RelayCredentials { RelayCredentials() {} @@ -147,6 +158,7 @@ struct RelayServerConfig { PortList ports; RelayCredentials credentials; int priority = 0; + TlsCertPolicy tls_cert_policy = TlsCertPolicy::TLS_CERT_POLICY_SECURE; }; class PortAllocatorSession : public sigslot::has_slots<> { diff --git a/webrtc/p2p/base/relayport.cc b/webrtc/p2p/base/relayport.cc index 5887aa0b9c..5d851a1195 100644 --- a/webrtc/p2p/base/relayport.cc +++ b/webrtc/p2p/base/relayport.cc @@ -491,8 +491,9 @@ void RelayEntry::Connect() { rtc::SocketAddress(port_->ip(), 0), port_->min_port(), port_->max_port()); } else if (ra->proto == PROTO_TCP || ra->proto == PROTO_SSLTCP) { - int opts = (ra->proto == PROTO_SSLTCP) ? - rtc::PacketSocketFactory::OPT_SSLTCP : 0; + int opts = (ra->proto == PROTO_SSLTCP) + ? rtc::PacketSocketFactory::OPT_TLS_FAKE + : 0; socket = port_->socket_factory()->CreateClientTcpSocket( rtc::SocketAddress(port_->ip(), 0), ra->address, port_->proxy(), port_->user_agent(), opts); diff --git a/webrtc/p2p/base/tcpport.cc b/webrtc/p2p/base/tcpport.cc index 9a788706aa..ada88fb1d8 100644 --- a/webrtc/p2p/base/tcpport.cc +++ b/webrtc/p2p/base/tcpport.cc @@ -491,7 +491,7 @@ void TCPConnection::CreateOutgoingTcpSocket() { ASSERT(outgoing_); // TODO(guoweis): Handle failures here (unlikely since TCP). int opts = (remote_candidate().protocol() == SSLTCP_PROTOCOL_NAME) - ? rtc::PacketSocketFactory::OPT_SSLTCP + ? rtc::PacketSocketFactory::OPT_TLS_FAKE : 0; socket_.reset(port()->socket_factory()->CreateClientTcpSocket( rtc::SocketAddress(port()->ip(), 0), remote_candidate().address(), diff --git a/webrtc/p2p/base/turnport.cc b/webrtc/p2p/base/turnport.cc index d2d9126c7a..f97642e6bd 100644 --- a/webrtc/p2p/base/turnport.cc +++ b/webrtc/p2p/base/turnport.cc @@ -329,8 +329,14 @@ bool TurnPort::CreateTurnClientSocket() { // Apply server address TLS and insecure bits to options. if (server_address_.proto == PROTO_TLS) { - opts |= rtc::PacketSocketFactory::OPT_TLS; + if (tls_cert_policy_ == + TlsCertPolicy::TLS_CERT_POLICY_INSECURE_NO_CHECK) { + opts |= rtc::PacketSocketFactory::OPT_TLS_INSECURE; + } else { + opts |= rtc::PacketSocketFactory::OPT_TLS; + } } + socket_ = socket_factory()->CreateClientTcpSocket( rtc::SocketAddress(ip(), 0), server_address_.address, proxy(), user_agent(), opts); diff --git a/webrtc/p2p/base/turnport.h b/webrtc/p2p/base/turnport.h index 3bb09bc062..b421453636 100644 --- a/webrtc/p2p/base/turnport.h +++ b/webrtc/p2p/base/turnport.h @@ -87,6 +87,12 @@ class TurnPort : public Port { virtual ProtocolType GetProtocol() const { return server_address_.proto; } + virtual TlsCertPolicy GetTlsCertPolicy() const { return tls_cert_policy_; } + + virtual void SetTlsCertPolicy(TlsCertPolicy tls_cert_policy) { + tls_cert_policy_ = tls_cert_policy; + } + virtual void PrepareAddress(); virtual Connection* CreateConnection( const Candidate& c, PortInterface::CandidateOrigin origin); @@ -255,6 +261,7 @@ class TurnPort : public Port { bool FailAndPruneConnection(const rtc::SocketAddress& address); ProtocolAddress server_address_; + TlsCertPolicy tls_cert_policy_ = TlsCertPolicy::TLS_CERT_POLICY_SECURE; RelayCredentials credentials_; AttemptedServerSet attempted_server_addresses_; diff --git a/webrtc/p2p/client/basicportallocator.cc b/webrtc/p2p/client/basicportallocator.cc index dbac0d3822..7cc1ea9645 100644 --- a/webrtc/p2p/client/basicportallocator.cc +++ b/webrtc/p2p/client/basicportallocator.cc @@ -1388,6 +1388,7 @@ void AllocationSequence::CreateTurnPort(const RelayServerConfig& config) { session_->allocator()->origin()); } ASSERT(port != NULL); + port->SetTlsCertPolicy(config.tls_cert_policy); session_->AddAllocatedPort(port, this, true); } } diff --git a/webrtc/sdk/android/api/org/webrtc/PeerConnection.java b/webrtc/sdk/android/api/org/webrtc/PeerConnection.java index ab08598519..61162440b3 100644 --- a/webrtc/sdk/android/api/org/webrtc/PeerConnection.java +++ b/webrtc/sdk/android/api/org/webrtc/PeerConnection.java @@ -39,6 +39,12 @@ public class PeerConnection { CLOSED } + /** Tracks PeerConnectionInterface::TlsCertPolicy */ + public enum TlsCertPolicy { + TLS_CERT_POLICY_SECURE, + TLS_CERT_POLICY_INSECURE_NO_CHECK, + } + /** Tracks PeerConnectionInterface::SignalingState */ public enum SignalingState { STABLE, @@ -87,6 +93,7 @@ public class PeerConnection { public final String uri; public final String username; public final String password; + public final TlsCertPolicy tlsCertPolicy; /** Convenience constructor for STUN servers. */ public IceServer(String uri) { @@ -94,13 +101,18 @@ public class PeerConnection { } public IceServer(String uri, String username, String password) { + this(uri, username, password, TlsCertPolicy.TLS_CERT_POLICY_SECURE); + } + + public IceServer(String uri, String username, String password, TlsCertPolicy tlsCertPolicy) { this.uri = uri; this.username = username; this.password = password; + this.tlsCertPolicy = tlsCertPolicy; } public String toString() { - return uri + "[" + username + ":" + password + "]"; + return uri + " [" + username + ":" + password + "] [" + tlsCertPolicy + "]"; } } diff --git a/webrtc/sdk/android/src/jni/classreferenceholder.cc b/webrtc/sdk/android/src/jni/classreferenceholder.cc index 565f47f4b4..b163be1841 100644 --- a/webrtc/sdk/android/src/jni/classreferenceholder.cc +++ b/webrtc/sdk/android/src/jni/classreferenceholder.cc @@ -82,6 +82,7 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) { LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState"); LoadClass(jni, "org/webrtc/PeerConnection$IceTransportsType"); LoadClass(jni, "org/webrtc/PeerConnection$TcpCandidatePolicy"); + LoadClass(jni, "org/webrtc/PeerConnection$TlsCertPolicy"); LoadClass(jni, "org/webrtc/PeerConnection$CandidateNetworkPolicy"); LoadClass(jni, "org/webrtc/PeerConnection$KeyType"); LoadClass(jni, "org/webrtc/PeerConnection$SignalingState"); diff --git a/webrtc/sdk/android/src/jni/peerconnection_jni.cc b/webrtc/sdk/android/src/jni/peerconnection_jni.cc index da55299775..f4174ceb88 100644 --- a/webrtc/sdk/android/src/jni/peerconnection_jni.cc +++ b/webrtc/sdk/android/src/jni/peerconnection_jni.cc @@ -1566,6 +1566,23 @@ static PeerConnectionInterface::ContinualGatheringPolicy return PeerConnectionInterface::GATHER_ONCE; } +static PeerConnectionInterface::TlsCertPolicy JavaTlsCertPolicyTypeToNativeType( + JNIEnv* jni, + jobject j_ice_server_tls_cert_policy) { + std::string enum_name = + GetJavaEnumName(jni, "org/webrtc/PeerConnection$TlsCertPolicy", + j_ice_server_tls_cert_policy); + + if (enum_name == "TLS_CERT_POLICY_SECURE") + return PeerConnectionInterface::kTlsCertPolicySecure; + + if (enum_name == "TLS_CERT_POLICY_INSECURE_NO_CHECK") + return PeerConnectionInterface::kTlsCertPolicyInsecureNoCheck; + + RTC_CHECK(false) << "Unexpected TlsCertPolicy enum_name " << enum_name; + return PeerConnectionInterface::kTlsCertPolicySecure; +} + static void JavaIceServersToJsepIceServers( JNIEnv* jni, jobject j_ice_servers, PeerConnectionInterface::IceServers* ice_servers) { @@ -1577,16 +1594,24 @@ static void JavaIceServersToJsepIceServers( GetFieldID(jni, j_ice_server_class, "username", "Ljava/lang/String;"); jfieldID j_ice_server_password_id = GetFieldID(jni, j_ice_server_class, "password", "Ljava/lang/String;"); + jfieldID j_ice_server_tls_cert_policy_id = + GetFieldID(jni, j_ice_server_class, "tlsCertPolicy", + "Lorg/webrtc/PeerConnection$TlsCertPolicy;"); + jobject j_ice_server_tls_cert_policy = + GetObjectField(jni, j_ice_server, j_ice_server_tls_cert_policy_id); jstring uri = reinterpret_cast( GetObjectField(jni, j_ice_server, j_ice_server_uri_id)); jstring username = reinterpret_cast( GetObjectField(jni, j_ice_server, j_ice_server_username_id)); jstring password = reinterpret_cast( GetObjectField(jni, j_ice_server, j_ice_server_password_id)); + PeerConnectionInterface::TlsCertPolicy tls_cert_policy = + JavaTlsCertPolicyTypeToNativeType(jni, j_ice_server_tls_cert_policy); PeerConnectionInterface::IceServer server; server.uri = JavaToStdString(jni, uri); server.username = JavaToStdString(jni, username); server.password = JavaToStdString(jni, password); + server.tls_cert_policy = tls_cert_policy; ice_servers->push_back(server); } }