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