Support a user-provided string for the TLS ALPN extension.
Fix source formatting
Add TLS ALPN extension.
Bug: webrtc:8086
Change-Id: I1f28ccd78760d3415e465f734744d2c2f93845e2
Reviewed-on: https://chromium-review.googlesource.com/611150
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Magnus Jedvert <magjed@webrtc.org>
Reviewed-by: Justin Uberti <juberti@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Peter Thatcher <pthatcher@webrtc.org>
Commit-Queue: Diogo Real <diogor@google.com>
Cr-Commit-Position: refs/heads/master@{#19588}
diff --git a/webrtc/rtc_base/BUILD.gn b/webrtc/rtc_base/BUILD.gn
index 01a5679..03e8ea6 100644
--- a/webrtc/rtc_base/BUILD.gn
+++ b/webrtc/rtc_base/BUILD.gn
@@ -1038,6 +1038,7 @@
}
if (is_posix) {
sources += [
+ "openssladapter_unittest.cc",
"ssladapter_unittest.cc",
"sslidentity_unittest.cc",
"sslstreamadapter_unittest.cc",
@@ -1056,6 +1057,14 @@
# Suppress warnings from the Chromium Clang plugin (bugs.webrtc.org/163).
suppressed_configs += [ "//build/config/clang:find_bad_constructs" ]
}
+ if (build_with_chromium) {
+ include_dirs = [ "../../boringssl/src/include" ]
+ }
+ if (rtc_build_ssl) {
+ deps += [ "//third_party/boringssl" ]
+ } else {
+ configs += [ ":external_ssl_library" ]
+ }
}
}
diff --git a/webrtc/rtc_base/openssladapter.cc b/webrtc/rtc_base/openssladapter.cc
index 64eb0ab..9164258 100644
--- a/webrtc/rtc_base/openssladapter.cc
+++ b/webrtc/rtc_base/openssladapter.cc
@@ -286,6 +286,7 @@
ssl_(nullptr),
ssl_ctx_(nullptr),
ssl_mode_(SSL_MODE_TLS),
+ ignore_bad_cert_(false),
custom_verification_succeeded_(false) {
// If a factory is used, take a reference on the factory's SSL_CTX.
// Otherwise, we'll create our own later.
@@ -302,6 +303,14 @@
Cleanup();
}
+void OpenSSLAdapter::SetIgnoreBadCert(bool ignore) {
+ ignore_bad_cert_ = ignore;
+}
+
+void OpenSSLAdapter::SetAlpnProtocols(const std::vector<std::string>& protos) {
+ alpn_protocols_ = protos;
+}
+
void OpenSSLAdapter::SetMode(SSLMode mode) {
RTC_DCHECK(!ssl_ctx_);
RTC_DCHECK(state_ == SSL_NONE);
@@ -327,7 +336,7 @@
SSLAdapter* adapter = SSLAdapter::Create(socket);
adapter->SetIdentity(identity_->GetReference());
adapter->SetRole(rtc::SSL_SERVER);
- adapter->set_ignore_bad_cert(ignore_bad_cert());
+ adapter->SetIgnoreBadCert(ignore_bad_cert_);
adapter->StartSSL("", false);
return adapter;
}
@@ -424,10 +433,18 @@
}
// Set a couple common TLS extensions; even though we don't use them yet.
- // TODO(emadomara) Add ALPN extension.
SSL_enable_ocsp_stapling(ssl_);
SSL_enable_signed_cert_timestamps(ssl_);
+ if (!alpn_protocols_.empty()) {
+ std::string tls_alpn_string = TransformAlpnProtocols(alpn_protocols_);
+ if (!tls_alpn_string.empty()) {
+ SSL_set_alpn_protos(
+ ssl_, reinterpret_cast<const unsigned char*>(tls_alpn_string.data()),
+ tls_alpn_string.size());
+ }
+ }
+
// Now that the initial config is done, transfer ownership of |bio| to the
// SSL object. If ContinueSSL() fails, the bio will be freed in Cleanup().
SSL_set_bio(ssl_, bio, bio);
@@ -927,14 +944,14 @@
}
bool OpenSSLAdapter::SSLPostConnectionCheck(SSL* ssl, const char* host) {
- bool ok = VerifyServerName(ssl, host, ignore_bad_cert());
+ bool ok = VerifyServerName(ssl, host, ignore_bad_cert_);
if (ok) {
ok = (SSL_get_verify_result(ssl) == X509_V_OK ||
custom_verification_succeeded_);
}
- if (!ok && ignore_bad_cert()) {
+ if (!ok && ignore_bad_cert_) {
LOG(LS_INFO) << "Other TLS post connection checks failed.";
ok = true;
}
@@ -1009,7 +1026,7 @@
}
// Should only be used for debugging and development.
- if (!ok && stream->ignore_bad_cert()) {
+ if (!ok && stream->ignore_bad_cert_) {
LOG(LS_WARNING) << "Ignoring cert error while verifying cert chain";
ok = 1;
}
@@ -1096,6 +1113,27 @@
return ctx;
}
+std::string TransformAlpnProtocols(
+ const std::vector<std::string>& alpn_protocols) {
+ // Transforms the alpn_protocols list to the format expected by
+ // Open/BoringSSL. This requires joining the protocols into a single string
+ // and prepending a character with the size of the protocol string before
+ // each protocol.
+ std::string transformed_alpn;
+ for (const std::string& proto : alpn_protocols) {
+ if (proto.size() == 0 || proto.size() > 0xFF) {
+ LOG(LS_ERROR) << "OpenSSLAdapter::Error("
+ << "TransformAlpnProtocols received proto with size "
+ << proto.size() << ")";
+ return "";
+ }
+ transformed_alpn += static_cast<char>(proto.size());
+ transformed_alpn += proto;
+ LOG(LS_VERBOSE) << "TransformAlpnProtocols: Adding proto: " << proto;
+ }
+ return transformed_alpn;
+}
+
//////////////////////////////////////////////////////////////////////
// OpenSSLAdapterFactory
//////////////////////////////////////////////////////////////////////
diff --git a/webrtc/rtc_base/openssladapter.h b/webrtc/rtc_base/openssladapter.h
index b57ea8f..9c6c344 100644
--- a/webrtc/rtc_base/openssladapter.h
+++ b/webrtc/rtc_base/openssladapter.h
@@ -38,6 +38,9 @@
OpenSSLAdapterFactory* factory = nullptr);
~OpenSSLAdapter() override;
+ void SetIgnoreBadCert(bool ignore) override;
+ void SetAlpnProtocols(const std::vector<std::string>& protos) override;
+
void SetMode(SSLMode mode) override;
void SetIdentity(SSLIdentity* identity) override;
void SetRole(SSLRole role) override;
@@ -129,10 +132,17 @@
std::string ssl_host_name_;
// Do DTLS or not
SSLMode ssl_mode_;
+ // If true, the server certificate need not match the configured hostname.
+ bool ignore_bad_cert_;
+ // List of protocols to be used in the TLS ALPN extension.
+ std::vector<std::string> alpn_protocols_;
bool custom_verification_succeeded_;
};
+std::string TransformAlpnProtocols(const std::vector<std::string>& protos);
+
+/////////////////////////////////////////////////////////////////////////////
class OpenSSLAdapterFactory : public SSLAdapterFactory {
public:
OpenSSLAdapterFactory();
diff --git a/webrtc/rtc_base/openssladapter_unittest.cc b/webrtc/rtc_base/openssladapter_unittest.cc
new file mode 100644
index 0000000..e443266
--- /dev/null
+++ b/webrtc/rtc_base/openssladapter_unittest.cc
@@ -0,0 +1,41 @@
+/*
+ * Copyright 2017 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 <sstream>
+#include <string>
+#include <vector>
+
+#include "webrtc/rtc_base/gunit.h"
+#include "webrtc/rtc_base/openssladapter.h"
+
+namespace rtc {
+
+TEST(OpenSSLAdapterTest, TestTransformAlpnProtocols) {
+ EXPECT_EQ("", TransformAlpnProtocols(std::vector<std::string>()));
+
+ // Protocols larger than 255 characters (whose size can't be fit in a byte),
+ // can't be converted, and an empty string will be returned.
+ std::string large_protocol(256, 'a');
+ EXPECT_EQ("",
+ TransformAlpnProtocols(std::vector<std::string>{large_protocol}));
+
+ // One protocol test.
+ std::vector<std::string> alpn_protos{"h2"};
+ std::stringstream expected_response;
+ expected_response << static_cast<char>(2) << "h2";
+ EXPECT_EQ(expected_response.str(), TransformAlpnProtocols(alpn_protos));
+
+ // Standard protocols test (h2,http/1.1).
+ alpn_protos.push_back("http/1.1");
+ expected_response << static_cast<char>(8) << "http/1.1";
+ EXPECT_EQ(expected_response.str(), TransformAlpnProtocols(alpn_protos));
+}
+
+} // namespace rtc
diff --git a/webrtc/rtc_base/ssladapter.h b/webrtc/rtc_base/ssladapter.h
index 87e7deb..b30e176 100644
--- a/webrtc/rtc_base/ssladapter.h
+++ b/webrtc/rtc_base/ssladapter.h
@@ -47,8 +47,8 @@
// Do not call these methods in production code.
// TODO(juberti): Remove the opportunistic encryption mechanism in
// BasicPacketSocketFactory that uses this function.
- bool ignore_bad_cert() const { return ignore_bad_cert_; }
- void set_ignore_bad_cert(bool ignore) { ignore_bad_cert_ = ignore; }
+ virtual void SetIgnoreBadCert(bool ignore) = 0;
+ virtual void SetAlpnProtocols(const std::vector<std::string>& protos) = 0;
// Do DTLS or TLS (default is TLS, if unspecified)
virtual void SetMode(SSLMode mode) = 0;
@@ -76,10 +76,6 @@
// and deletes |socket|. Otherwise, the returned SSLAdapter takes ownership
// of |socket|.
static SSLAdapter* Create(AsyncSocket* socket);
-
- private:
- // If true, the server certificate need not match the configured hostname.
- bool ignore_bad_cert_ = false;
};
///////////////////////////////////////////////////////////////////////////////
diff --git a/webrtc/rtc_base/ssladapter_unittest.cc b/webrtc/rtc_base/ssladapter_unittest.cc
index 929b14f..5c61f6a 100644
--- a/webrtc/rtc_base/ssladapter_unittest.cc
+++ b/webrtc/rtc_base/ssladapter_unittest.cc
@@ -52,7 +52,7 @@
// Ignore any certificate errors for the purpose of testing.
// Note: We do this only because we don't have a real certificate.
// NEVER USE THIS IN PRODUCTION CODE!
- ssl_adapter_->set_ignore_bad_cert(true);
+ ssl_adapter_->SetIgnoreBadCert(true);
ssl_adapter_->SignalReadEvent.connect(this,
&SSLAdapterTestDummyClient::OnSSLAdapterReadEvent);
@@ -60,6 +60,10 @@
&SSLAdapterTestDummyClient::OnSSLAdapterCloseEvent);
}
+ void SetAlpnProtocols(const std::vector<std::string>& protos) {
+ ssl_adapter_->SetAlpnProtocols(protos);
+ }
+
rtc::SocketAddress GetAddress() const {
return ssl_adapter_->GetLocalAddress();
}
@@ -282,6 +286,10 @@
handshake_wait_ = wait;
}
+ void SetAlpnProtocols(const std::vector<std::string>& protos) {
+ client_->SetAlpnProtocols(protos);
+ }
+
void TestHandshake(bool expect_success) {
int rv;
@@ -434,6 +442,14 @@
TestTransfer("Hello, world!");
}
+// Test transfer using ALPN with protos as h2 and http/1.1
+TEST_F(SSLAdapterTestTLS_ECDSA, TestTLSALPN) {
+ std::vector<std::string> alpn_protos{"h2", "http/1.1"};
+ SetAlpnProtocols(alpn_protos);
+ TestHandshake(true);
+ TestTransfer("Hello, world!");
+}
+
// Basic tests: DTLS
// Test that handshake works, using RSA