Add disabled certificate check support to IceServer PeerConnection API.

Refactor "OPT_SSLTCP" renaming it to "OPT_TLS_FAKE", making it clear
that it's not actually some kind of SSL over TCP. Also making it clear
that it's mutually exclusive with OPT_TLS.

Add "OPT_TLS_INSECURE" that implements the new certificate-check
disabled TLS mode, which is also mutually exclusive with the other
TLS options.

PortAllocator: Add a new TLS policy enum TlsCertPolicy which defines
the new insecure mode and added it as a RelayCredentials member.

TurnPort: Add new TLS policy member with appropriate getter and setter
to avoid constructor bloat. Initialize it from the RelayCredentials
after the TurnPort is created.

Expose the new feature in the PeerConnection API via
IceServer.tls_certificate_policy as well as via the Android JNI
PeerConnection API.

For security reasons we ensure that:

	1) The policy is always explicitly initialized to secure.
        2) API users have to explicitly integrate with the feature to
           use it, and will otherwise get no change in behavior.
	3) The feature is not immediately exposed in non-native
	   contexts. For example, disabling of certificate validation
           is not implemented via URI parsing since this would
           immediately allow it to be used from a web page.

BUG=webrtc:6840

Review-Url: https://codereview.webrtc.org/2557803002
Cr-Commit-Position: refs/heads/master@{#15670}
This commit is contained in:
hnsl
2016-12-19 04:10:30 -08:00
committed by Commit bot
parent b6c456b772
commit b0f04fdb9e
14 changed files with 136 additions and 18 deletions

View File

@ -306,8 +306,14 @@ bool ParseIceServerUrl(const PeerConnectionInterface::IceServer& server,
break; break;
case TURN: case TURN:
case TURNS: { case TURNS: {
turn_servers->push_back(cricket::RelayServerConfig( cricket::RelayServerConfig config = cricket::RelayServerConfig(
address, port, username, server.password, turn_transport_type)); 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; break;
} }
case INVALID: case INVALID:

View File

@ -40,6 +40,7 @@
#include "webrtc/base/virtualsocketserver.h" #include "webrtc/base/virtualsocketserver.h"
#include "webrtc/media/engine/fakewebrtcvideoengine.h" #include "webrtc/media/engine/fakewebrtcvideoengine.h"
#include "webrtc/p2p/base/p2pconstants.h" #include "webrtc/p2p/base/p2pconstants.h"
#include "webrtc/p2p/base/portinterface.h"
#include "webrtc/p2p/base/sessiondescription.h" #include "webrtc/p2p/base/sessiondescription.h"
#include "webrtc/p2p/base/testturnserver.h" #include "webrtc/p2p/base/testturnserver.h"
#include "webrtc/p2p/client/basicportallocator.h" #include "webrtc/p2p/client/basicportallocator.h"
@ -2619,11 +2620,21 @@ class IceServerParsingTest : public testing::Test {
bool ParseUrl(const std::string& url, bool ParseUrl(const std::string& url,
const std::string& username, const std::string& username,
const std::string& password) { 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::IceServers servers;
PeerConnectionInterface::IceServer server; PeerConnectionInterface::IceServer server;
server.urls.push_back(url); server.urls.push_back(url);
server.username = username; server.username = username;
server.password = password; server.password = password;
server.tls_cert_policy = tls_certificate_policy;
servers.push_back(server); servers.push_back(server);
return webrtc::ParseIceServers(servers, &stun_servers_, &turn_servers_); return webrtc::ParseIceServers(servers, &stun_servers_, &turn_servers_);
} }
@ -2655,6 +2666,18 @@ TEST_F(IceServerParsingTest, ParseStunPrefixes) {
EXPECT_EQ(0U, stun_servers_.size()); EXPECT_EQ(0U, stun_servers_.size());
EXPECT_EQ(1U, turn_servers_.size()); EXPECT_EQ(1U, turn_servers_.size());
EXPECT_EQ(cricket::PROTO_TLS, turn_servers_[0].ports[0].proto); 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(); turn_servers_.clear();
// invalid prefixes // invalid prefixes

View File

@ -209,15 +209,28 @@ class PeerConnectionInterface : public rtc::RefCountInterface {
kIceConnectionMax, 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 { struct IceServer {
// TODO(jbauch): Remove uri when all code using it has switched to urls. // TODO(jbauch): Remove uri when all code using it has switched to urls.
std::string uri; std::string uri;
std::vector<std::string> urls; std::vector<std::string> urls;
std::string username; std::string username;
std::string password; std::string password;
TlsCertPolicy tls_cert_policy = kTlsCertPolicySecure;
bool operator==(const IceServer& o) const { bool operator==(const IceServer& o) const {
return uri == o.uri && urls == o.urls && username == o.username && 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); } bool operator!=(const IceServer& o) const { return !(*this == o); }
}; };

View File

@ -87,8 +87,8 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateServerTcpSocket(
return NULL; return NULL;
} }
// If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket. // If using fake TLS, wrap the TCP socket in a pseudo-SSL socket.
if (opts & PacketSocketFactory::OPT_SSLTCP) { if (opts & PacketSocketFactory::OPT_TLS_FAKE) {
ASSERT(!(opts & PacketSocketFactory::OPT_TLS)); ASSERT(!(opts & PacketSocketFactory::OPT_TLS));
socket = new AsyncSSLSocket(socket); socket = new AsyncSSLSocket(socket);
} }
@ -129,15 +129,24 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
proxy_info.username, proxy_info.password); proxy_info.username, proxy_info.password);
} }
// If using TLS, wrap the socket in an SSL adapter. // Assert that at most one TLS option is used.
if (opts & PacketSocketFactory::OPT_TLS) { int tlsOpts =
ASSERT(!(opts & PacketSocketFactory::OPT_SSLTCP)); 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); SSLAdapter* ssl_adapter = SSLAdapter::Create(socket);
if (!ssl_adapter) { if (!ssl_adapter) {
return NULL; return NULL;
} }
if (tlsOpts & PacketSocketFactory::OPT_TLS_INSECURE) {
ssl_adapter->set_ignore_bad_cert(true);
}
socket = ssl_adapter; socket = ssl_adapter;
if (ssl_adapter->StartSSL(remote_address.hostname().c_str(), false) != 0) { if (ssl_adapter->StartSSL(remote_address.hostname().c_str(), false) != 0) {
@ -145,9 +154,8 @@ AsyncPacketSocket* BasicPacketSocketFactory::CreateClientTcpSocket(
return NULL; return NULL;
} }
// If using SSLTCP, wrap the TCP socket in a pseudo-SSL socket. } else if (tlsOpts & PacketSocketFactory::OPT_TLS_FAKE) {
} else if (opts & PacketSocketFactory::OPT_SSLTCP) { // Using fake TLS, wrap the TCP socket in a pseudo-SSL socket.
ASSERT(!(opts & PacketSocketFactory::OPT_TLS));
socket = new AsyncSSLSocket(socket); socket = new AsyncSSLSocket(socket);
} }

View File

@ -22,9 +22,12 @@ class AsyncResolverInterface;
class PacketSocketFactory { class PacketSocketFactory {
public: public:
enum Options { enum Options {
OPT_SSLTCP = 0x01, // Pseudo-TLS.
OPT_TLS = 0x02,
OPT_STUN = 0x04, 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() { } PacketSocketFactory() { }

View File

@ -95,6 +95,17 @@ enum {
CF_ALL = 0x7, 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). // TODO(deadbeef): Rename to TurnCredentials (and username to ufrag).
struct RelayCredentials { struct RelayCredentials {
RelayCredentials() {} RelayCredentials() {}
@ -147,6 +158,7 @@ struct RelayServerConfig {
PortList ports; PortList ports;
RelayCredentials credentials; RelayCredentials credentials;
int priority = 0; int priority = 0;
TlsCertPolicy tls_cert_policy = TlsCertPolicy::TLS_CERT_POLICY_SECURE;
}; };
class PortAllocatorSession : public sigslot::has_slots<> { class PortAllocatorSession : public sigslot::has_slots<> {

View File

@ -491,8 +491,9 @@ void RelayEntry::Connect() {
rtc::SocketAddress(port_->ip(), 0), rtc::SocketAddress(port_->ip(), 0),
port_->min_port(), port_->max_port()); port_->min_port(), port_->max_port());
} else if (ra->proto == PROTO_TCP || ra->proto == PROTO_SSLTCP) { } else if (ra->proto == PROTO_TCP || ra->proto == PROTO_SSLTCP) {
int opts = (ra->proto == PROTO_SSLTCP) ? int opts = (ra->proto == PROTO_SSLTCP)
rtc::PacketSocketFactory::OPT_SSLTCP : 0; ? rtc::PacketSocketFactory::OPT_TLS_FAKE
: 0;
socket = port_->socket_factory()->CreateClientTcpSocket( socket = port_->socket_factory()->CreateClientTcpSocket(
rtc::SocketAddress(port_->ip(), 0), ra->address, rtc::SocketAddress(port_->ip(), 0), ra->address,
port_->proxy(), port_->user_agent(), opts); port_->proxy(), port_->user_agent(), opts);

View File

@ -491,7 +491,7 @@ void TCPConnection::CreateOutgoingTcpSocket() {
ASSERT(outgoing_); ASSERT(outgoing_);
// TODO(guoweis): Handle failures here (unlikely since TCP). // TODO(guoweis): Handle failures here (unlikely since TCP).
int opts = (remote_candidate().protocol() == SSLTCP_PROTOCOL_NAME) int opts = (remote_candidate().protocol() == SSLTCP_PROTOCOL_NAME)
? rtc::PacketSocketFactory::OPT_SSLTCP ? rtc::PacketSocketFactory::OPT_TLS_FAKE
: 0; : 0;
socket_.reset(port()->socket_factory()->CreateClientTcpSocket( socket_.reset(port()->socket_factory()->CreateClientTcpSocket(
rtc::SocketAddress(port()->ip(), 0), remote_candidate().address(), rtc::SocketAddress(port()->ip(), 0), remote_candidate().address(),

View File

@ -329,8 +329,14 @@ bool TurnPort::CreateTurnClientSocket() {
// Apply server address TLS and insecure bits to options. // Apply server address TLS and insecure bits to options.
if (server_address_.proto == PROTO_TLS) { 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( socket_ = socket_factory()->CreateClientTcpSocket(
rtc::SocketAddress(ip(), 0), server_address_.address, rtc::SocketAddress(ip(), 0), server_address_.address,
proxy(), user_agent(), opts); proxy(), user_agent(), opts);

View File

@ -87,6 +87,12 @@ class TurnPort : public Port {
virtual ProtocolType GetProtocol() const { return server_address_.proto; } 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 void PrepareAddress();
virtual Connection* CreateConnection( virtual Connection* CreateConnection(
const Candidate& c, PortInterface::CandidateOrigin origin); const Candidate& c, PortInterface::CandidateOrigin origin);
@ -255,6 +261,7 @@ class TurnPort : public Port {
bool FailAndPruneConnection(const rtc::SocketAddress& address); bool FailAndPruneConnection(const rtc::SocketAddress& address);
ProtocolAddress server_address_; ProtocolAddress server_address_;
TlsCertPolicy tls_cert_policy_ = TlsCertPolicy::TLS_CERT_POLICY_SECURE;
RelayCredentials credentials_; RelayCredentials credentials_;
AttemptedServerSet attempted_server_addresses_; AttemptedServerSet attempted_server_addresses_;

View File

@ -1388,6 +1388,7 @@ void AllocationSequence::CreateTurnPort(const RelayServerConfig& config) {
session_->allocator()->origin()); session_->allocator()->origin());
} }
ASSERT(port != NULL); ASSERT(port != NULL);
port->SetTlsCertPolicy(config.tls_cert_policy);
session_->AddAllocatedPort(port, this, true); session_->AddAllocatedPort(port, this, true);
} }
} }

View File

@ -39,6 +39,12 @@ public class PeerConnection {
CLOSED CLOSED
} }
/** Tracks PeerConnectionInterface::TlsCertPolicy */
public enum TlsCertPolicy {
TLS_CERT_POLICY_SECURE,
TLS_CERT_POLICY_INSECURE_NO_CHECK,
}
/** Tracks PeerConnectionInterface::SignalingState */ /** Tracks PeerConnectionInterface::SignalingState */
public enum SignalingState { public enum SignalingState {
STABLE, STABLE,
@ -87,6 +93,7 @@ public class PeerConnection {
public final String uri; public final String uri;
public final String username; public final String username;
public final String password; public final String password;
public final TlsCertPolicy tlsCertPolicy;
/** Convenience constructor for STUN servers. */ /** Convenience constructor for STUN servers. */
public IceServer(String uri) { public IceServer(String uri) {
@ -94,13 +101,18 @@ public class PeerConnection {
} }
public IceServer(String uri, String username, String password) { 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.uri = uri;
this.username = username; this.username = username;
this.password = password; this.password = password;
this.tlsCertPolicy = tlsCertPolicy;
} }
public String toString() { public String toString() {
return uri + "[" + username + ":" + password + "]"; return uri + " [" + username + ":" + password + "] [" + tlsCertPolicy + "]";
} }
} }

View File

@ -82,6 +82,7 @@ ClassReferenceHolder::ClassReferenceHolder(JNIEnv* jni) {
LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState"); LoadClass(jni, "org/webrtc/PeerConnection$IceGatheringState");
LoadClass(jni, "org/webrtc/PeerConnection$IceTransportsType"); LoadClass(jni, "org/webrtc/PeerConnection$IceTransportsType");
LoadClass(jni, "org/webrtc/PeerConnection$TcpCandidatePolicy"); LoadClass(jni, "org/webrtc/PeerConnection$TcpCandidatePolicy");
LoadClass(jni, "org/webrtc/PeerConnection$TlsCertPolicy");
LoadClass(jni, "org/webrtc/PeerConnection$CandidateNetworkPolicy"); LoadClass(jni, "org/webrtc/PeerConnection$CandidateNetworkPolicy");
LoadClass(jni, "org/webrtc/PeerConnection$KeyType"); LoadClass(jni, "org/webrtc/PeerConnection$KeyType");
LoadClass(jni, "org/webrtc/PeerConnection$SignalingState"); LoadClass(jni, "org/webrtc/PeerConnection$SignalingState");

View File

@ -1566,6 +1566,23 @@ static PeerConnectionInterface::ContinualGatheringPolicy
return PeerConnectionInterface::GATHER_ONCE; 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( static void JavaIceServersToJsepIceServers(
JNIEnv* jni, jobject j_ice_servers, JNIEnv* jni, jobject j_ice_servers,
PeerConnectionInterface::IceServers* ice_servers) { PeerConnectionInterface::IceServers* ice_servers) {
@ -1577,16 +1594,24 @@ static void JavaIceServersToJsepIceServers(
GetFieldID(jni, j_ice_server_class, "username", "Ljava/lang/String;"); GetFieldID(jni, j_ice_server_class, "username", "Ljava/lang/String;");
jfieldID j_ice_server_password_id = jfieldID j_ice_server_password_id =
GetFieldID(jni, j_ice_server_class, "password", "Ljava/lang/String;"); 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<jstring>( jstring uri = reinterpret_cast<jstring>(
GetObjectField(jni, j_ice_server, j_ice_server_uri_id)); GetObjectField(jni, j_ice_server, j_ice_server_uri_id));
jstring username = reinterpret_cast<jstring>( jstring username = reinterpret_cast<jstring>(
GetObjectField(jni, j_ice_server, j_ice_server_username_id)); GetObjectField(jni, j_ice_server, j_ice_server_username_id));
jstring password = reinterpret_cast<jstring>( jstring password = reinterpret_cast<jstring>(
GetObjectField(jni, j_ice_server, j_ice_server_password_id)); 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; PeerConnectionInterface::IceServer server;
server.uri = JavaToStdString(jni, uri); server.uri = JavaToStdString(jni, uri);
server.username = JavaToStdString(jni, username); server.username = JavaToStdString(jni, username);
server.password = JavaToStdString(jni, password); server.password = JavaToStdString(jni, password);
server.tls_cert_policy = tls_cert_policy;
ice_servers->push_back(server); ice_servers->push_back(server);
} }
} }