10e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org/*
20e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * libjingle
30e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Copyright 2004--2008, Google Inc.
40e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
50e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * Redistribution and use in source and binary forms, with or without
60e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * modification, are permitted provided that the following conditions are met:
70e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
80e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *  1. Redistributions of source code must retain the above copyright notice,
90e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     this list of conditions and the following disclaimer.
100e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *  2. Redistributions in binary form must reproduce the above copyright notice,
110e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     this list of conditions and the following disclaimer in the documentation
120e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     and/or other materials provided with the distribution.
130e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *  3. The name of the author may not be used to endorse or promote products
140e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *     derived from this software without specific prior written permission.
150e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org *
160e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
170e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
180e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
190e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
200e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
210e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
220e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
230e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
240e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
250e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
260e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org */
270e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
280e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// SecureTunnelSessionClient and SecureTunnelSession implementation.
290e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
30cf81adffe15fa8ea0f333432e41f6d504148f18abuildbot@webrtc.org#include "talk/p2p/base/transportchannel.h"
31cf81adffe15fa8ea0f333432e41f6d504148f18abuildbot@webrtc.org#include "talk/session/tunnel/pseudotcpchannel.h"
320e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org#include "talk/session/tunnel/securetunnelsessionclient.h"
3313b2d035e2e7f2f18e3a4d3377bc1a09f43a4ff9buildbot@webrtc.org#include "webrtc/libjingle/xmllite/xmlelement.h"
342a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org#include "webrtc/base/basicdefs.h"
352a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org#include "webrtc/base/basictypes.h"
362a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org#include "webrtc/base/common.h"
372a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org#include "webrtc/base/helpers.h"
382a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org#include "webrtc/base/logging.h"
392a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org#include "webrtc/base/sslidentity.h"
402a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org#include "webrtc/base/sslstreamadapter.h"
41cf81adffe15fa8ea0f333432e41f6d504148f18abuildbot@webrtc.org#include "webrtc/base/stringutils.h"
420e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
430e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgnamespace cricket {
440e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
450e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// XML elements and namespaces for XMPP stanzas used in content exchanges.
460e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
470e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgconst char NS_SECURE_TUNNEL[] = "http://www.google.com/talk/securetunnel";
480e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgconst buzz::StaticQName QN_SECURE_TUNNEL_DESCRIPTION =
490e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    { NS_SECURE_TUNNEL, "description" };
500e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgconst buzz::StaticQName QN_SECURE_TUNNEL_TYPE =
510e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    { NS_SECURE_TUNNEL, "type" };
520e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgconst buzz::StaticQName QN_SECURE_TUNNEL_CLIENT_CERT =
530e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    { NS_SECURE_TUNNEL, "client-cert" };
540e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgconst buzz::StaticQName QN_SECURE_TUNNEL_SERVER_CERT =
550e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    { NS_SECURE_TUNNEL, "server-cert" };
560e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgconst char CN_SECURE_TUNNEL[] = "securetunnel";
570e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
580e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// SecureTunnelContentDescription
590e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
600e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// TunnelContentDescription is extended to hold string forms of the
610e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// client and server certificate, PEM encoded.
620e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
630e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgstruct SecureTunnelContentDescription : public ContentDescription {
640e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  std::string description;
650e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  std::string client_pem_certificate;
660e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  std::string server_pem_certificate;
670e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
680e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  SecureTunnelContentDescription(const std::string& desc,
690e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                                 const std::string& client_pem_cert,
700e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                                 const std::string& server_pem_cert)
710e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      : description(desc),
720e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        client_pem_certificate(client_pem_cert),
730e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        server_pem_certificate(server_pem_cert) {
740e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  }
750e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  virtual ContentDescription* Copy() const {
760e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    return new SecureTunnelContentDescription(*this);
770e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  }
780e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org};
790e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
800e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// SecureTunnelSessionClient
810e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
820e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgSecureTunnelSessionClient::SecureTunnelSessionClient(
830e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    const buzz::Jid& jid, SessionManager* manager)
840e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    : TunnelSessionClient(jid, manager, NS_SECURE_TUNNEL) {
850e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
860e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
872a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.orgvoid SecureTunnelSessionClient::SetIdentity(rtc::SSLIdentity* identity) {
880e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ASSERT(identity_.get() == NULL);
890e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  identity_.reset(identity);
900e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
910e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
920e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgbool SecureTunnelSessionClient::GenerateIdentity() {
930e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ASSERT(identity_.get() == NULL);
942a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org  identity_.reset(rtc::SSLIdentity::Generate(
950e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      // The name on the certificate does not matter: the peer will
960e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      // make sure the cert it gets during SSL negotiation matches the
970e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      // one it got from XMPP. It would be neat to put something
980e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      // recognizable in there such as the JID, except this will show
990e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      // in clear during the SSL negotiation and so it could be a
1000e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      // privacy issue. Specifying an empty string here causes
1010e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      // it to use a random string.
1020e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org#ifdef _DEBUG
1030e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      jid().Str()
1040e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org#else
1050e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      ""
1060e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org#endif
1070e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      ));
1080e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (identity_.get() == NULL) {
1090e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    LOG(LS_ERROR) << "Failed to generate SSL identity";
1100e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    return false;
1110e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  }
1120e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return true;
1130e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
1140e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1152a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.orgrtc::SSLIdentity& SecureTunnelSessionClient::GetIdentity() const {
1160e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ASSERT(identity_.get() != NULL);
1170e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return *identity_;
1180e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
1190e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1200e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// Parses a certificate from a PEM encoded string.
1210e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// Returns NULL on failure.
1220e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// The caller is responsible for freeing the returned object.
1232a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.orgstatic rtc::SSLCertificate* ParseCertificate(
1240e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    const std::string& pem_cert) {
1250e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (pem_cert.empty())
1260e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    return NULL;
1272a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org  return rtc::SSLCertificate::FromPEMString(pem_cert);
1280e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
1290e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1300e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgTunnelSession* SecureTunnelSessionClient::MakeTunnelSession(
1312a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org    Session* session, rtc::Thread* stream_thread,
1320e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    TunnelSessionRole role) {
1330e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return new SecureTunnelSession(this, session, stream_thread, role);
1340e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
1350e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1360e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgbool FindSecureTunnelContent(const cricket::SessionDescription* sdesc,
1370e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                             std::string* name,
1380e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                             const SecureTunnelContentDescription** content) {
1390e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  const ContentInfo* cinfo = sdesc->FirstContentByType(NS_SECURE_TUNNEL);
1400e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (cinfo == NULL)
1410e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    return false;
1420e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1430e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  *name = cinfo->name;
1440e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  *content = static_cast<const SecureTunnelContentDescription*>(
1450e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      cinfo->description);
1460e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return true;
1470e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
1480e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1490e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgvoid SecureTunnelSessionClient::OnIncomingTunnel(const buzz::Jid &jid,
1500e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                                                 Session *session) {
1510e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  std::string content_name;
1520e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  const SecureTunnelContentDescription* content = NULL;
1530e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (!FindSecureTunnelContent(session->remote_description(),
1540e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                               &content_name, &content)) {
1550e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    ASSERT(false);
1560e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  }
1570e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1580e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // Validate the certificate
1592a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org  rtc::scoped_ptr<rtc::SSLCertificate> peer_cert(
1600e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      ParseCertificate(content->client_pem_certificate));
1610e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (peer_cert.get() == NULL) {
1620e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    LOG(LS_ERROR)
1630e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        << "Rejecting incoming secure tunnel with invalid cetificate";
1640e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    DeclineTunnel(session);
1650e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    return;
1660e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  }
1670e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // If there were a convenient place we could have cached the
1680e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // peer_cert so as not to have to parse it a second time when
1690e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // configuring the tunnel.
1700e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  SignalIncomingTunnel(this, jid, content->description, session);
1710e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
1720e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1730e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// The XML representation of a session initiation request (XMPP IQ),
1740e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// containing the initiator's SecureTunnelContentDescription,
1750e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// looks something like this:
1760e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// <iq from="INITIATOR@gmail.com/pcpE101B7F4"
1770e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       to="RECIPIENT@gmail.com/pcp8B87F0A3"
1780e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       type="set" id="3">
1790e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//   <session xmlns="http://www.google.com/session"
1800e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       type="initiate" id="2508605813"
1810e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       initiator="INITIATOR@gmail.com/pcpE101B7F4">
1820e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//     <description xmlns="http://www.google.com/talk/securetunnel">
1830e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       <type>send:filename</type>
1840e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       <client-cert>
1850e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//         -----BEGIN CERTIFICATE-----
1860e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//         INITIATOR'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
1870e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//         -----END CERTIFICATE-----
1880e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       </client-cert>
1890e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//     </description>
1900e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//     <transport xmlns="http://www.google.com/transport/p2p"/>
1910e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//   </session>
1920e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// </iq>
1930e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
1940e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// The session accept iq, containing the recipient's certificate and
1950e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// echoing the initiator's certificate, looks something like this:
1960e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// <iq from="RECIPIENT@gmail.com/pcpE101B7F4"
1970e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//     to="INITIATOR@gmail.com/pcpE101B7F4"
1980e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//     type="set" id="5">
1990e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//   <session xmlns="http://www.google.com/session"
2000e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       type="accept" id="2508605813"
2010e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       initiator="sdoyon911@gmail.com/pcpE101B7F4">
2020e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//     <description xmlns="http://www.google.com/talk/securetunnel">
2030e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       <type>send:FILENAME</type>
2040e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       <client-cert>
2050e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//         -----BEGIN CERTIFICATE-----
2060e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//         INITIATOR'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
2070e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//         -----END CERTIFICATE-----
2080e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       </client-cert>
2090e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       <server-cert>
2100e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//         -----BEGIN CERTIFICATE-----
2110e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//         RECIPIENT'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
2120e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//         -----END CERTIFICATE-----
2130e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//       </server-cert>
2140e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//     </description>
2150e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org//   </session>
2160e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// </iq>
2170e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2180e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2190e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgbool SecureTunnelSessionClient::ParseContent(SignalingProtocol protocol,
2200e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                                             const buzz::XmlElement* elem,
2210e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                                             ContentDescription** content,
2220e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                                             ParseError* error) {
2230e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  const buzz::XmlElement* type_elem = elem->FirstNamed(QN_SECURE_TUNNEL_TYPE);
2240e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2250e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (type_elem == NULL)
2260e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    // Missing mandatory XML element.
2270e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    return false;
2280e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2290e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // Here we consider the certificate components to be optional. In
2300e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // practice the client certificate is always present, and the server
2310e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // certificate is initially missing from the session description
2320e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // sent during session initiation. OnAccept() will enforce that we
2330e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // have a certificate for our peer.
2340e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  const buzz::XmlElement* client_cert_elem =
2350e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      elem->FirstNamed(QN_SECURE_TUNNEL_CLIENT_CERT);
2360e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  const buzz::XmlElement* server_cert_elem =
2370e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      elem->FirstNamed(QN_SECURE_TUNNEL_SERVER_CERT);
2380e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  *content = new SecureTunnelContentDescription(
2390e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      type_elem->BodyText(),
2400e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      client_cert_elem ? client_cert_elem->BodyText() : "",
2410e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      server_cert_elem ? server_cert_elem->BodyText() : "");
2420e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return true;
2430e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
2440e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2450e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgbool SecureTunnelSessionClient::WriteContent(
2460e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    SignalingProtocol protocol, const ContentDescription* untyped_content,
2470e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    buzz::XmlElement** elem, WriteError* error) {
2480e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  const SecureTunnelContentDescription* content =
2490e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      static_cast<const SecureTunnelContentDescription*>(untyped_content);
2500e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2510e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  buzz::XmlElement* root =
2520e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      new buzz::XmlElement(QN_SECURE_TUNNEL_DESCRIPTION, true);
2530e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  buzz::XmlElement* type_elem = new buzz::XmlElement(QN_SECURE_TUNNEL_TYPE);
2540e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  type_elem->SetBodyText(content->description);
2550e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  root->AddElement(type_elem);
2560e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (!content->client_pem_certificate.empty()) {
2570e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    buzz::XmlElement* client_cert_elem =
2580e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        new buzz::XmlElement(QN_SECURE_TUNNEL_CLIENT_CERT);
2590e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    client_cert_elem->SetBodyText(content->client_pem_certificate);
2600e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    root->AddElement(client_cert_elem);
2610e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  }
2620e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (!content->server_pem_certificate.empty()) {
2630e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    buzz::XmlElement* server_cert_elem =
2640e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        new buzz::XmlElement(QN_SECURE_TUNNEL_SERVER_CERT);
2650e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    server_cert_elem->SetBodyText(content->server_pem_certificate);
2660e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    root->AddElement(server_cert_elem);
2670e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  }
2680e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  *elem = root;
2690e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return true;
2700e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
2710e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2720e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgSessionDescription* NewSecureTunnelSessionDescription(
2730e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    const std::string& content_name, ContentDescription* content) {
2740e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  SessionDescription* sdesc = new SessionDescription();
2750e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  sdesc->AddContent(content_name, NS_SECURE_TUNNEL, content);
2760e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return sdesc;
2770e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
2780e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2790e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgSessionDescription* SecureTunnelSessionClient::CreateOffer(
2800e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    const buzz::Jid &jid, const std::string &description) {
2810e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // We are the initiator so we are the client. Put our cert into the
2820e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // description.
2830e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  std::string pem_cert = GetIdentity().certificate().ToPEMString();
2840e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return NewSecureTunnelSessionDescription(
2850e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      CN_SECURE_TUNNEL,
2860e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      new SecureTunnelContentDescription(description, pem_cert, ""));
2870e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
2880e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2890e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgSessionDescription* SecureTunnelSessionClient::CreateAnswer(
2900e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    const SessionDescription* offer) {
2910e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  std::string content_name;
2920e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  const SecureTunnelContentDescription* offer_tunnel = NULL;
2930e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (!FindSecureTunnelContent(offer, &content_name, &offer_tunnel))
2940e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    return NULL;
2950e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
2960e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // We are accepting a session request. We need to add our cert, the
2970e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // server cert, into the description. The client cert was validated
2980e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // in OnIncomingTunnel().
2990e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ASSERT(!offer_tunnel->client_pem_certificate.empty());
3000e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return NewSecureTunnelSessionDescription(
3010e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      content_name,
3020e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      new SecureTunnelContentDescription(
3030e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org          offer_tunnel->description,
3040e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org          offer_tunnel->client_pem_certificate,
3050e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org          GetIdentity().certificate().ToPEMString()));
3060e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
3070e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
3080e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org// SecureTunnelSession
3090e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
3100e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgSecureTunnelSession::SecureTunnelSession(
3110e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    SecureTunnelSessionClient* client, Session* session,
3122a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org    rtc::Thread* stream_thread, TunnelSessionRole role)
3130e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    : TunnelSession(client, session, stream_thread),
3140e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      role_(role) {
3150e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
3160e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
3172a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.orgrtc::StreamInterface* SecureTunnelSession::MakeSecureStream(
3182a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org    rtc::StreamInterface* stream) {
3192a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org  rtc::SSLStreamAdapter* ssl_stream =
3202a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org      rtc::SSLStreamAdapter::Create(stream);
3212a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org  rtc::SSLIdentity* identity =
3220e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      static_cast<SecureTunnelSessionClient*>(client_)->
3230e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      GetIdentity().GetReference();
3240e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ssl_stream->SetIdentity(identity);
3250e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (role_ == RESPONDER)
3260e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    ssl_stream->SetServerRole();
3270e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ssl_stream->StartSSLWithPeer();
3280e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
3290e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // SSL negotiation will start on the stream as soon as it
3300e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // opens. However our SSLStreamAdapter still hasn't been told what
3310e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // certificate to allow for our peer. If we are the initiator, we do
3320e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // not have the peer's certificate yet: we will obtain it from the
3330e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // session accept message which we will receive later (see
3340e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // OnAccept()). We won't Connect() the PseudoTcpChannel until we get
3350e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // that, so the stream will stay closed until then.  Keep a handle
3360e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // on the streem so we can configure the peer certificate later.
3372a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org  ssl_stream_reference_.reset(new rtc::StreamReference(ssl_stream));
3380e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return ssl_stream_reference_->NewReference();
3390e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
3400e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
3412a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.orgrtc::StreamInterface* SecureTunnelSession::GetStream() {
3420e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ASSERT(channel_ != NULL);
3430e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ASSERT(ssl_stream_reference_.get() == NULL);
3440e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  return MakeSecureStream(channel_->GetStream());
3450e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
3460e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
3470e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.orgvoid SecureTunnelSession::OnAccept() {
3480e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // We have either sent or received a session accept: it's time to
3490e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // connect the tunnel. First we must set the peer certificate.
3500e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ASSERT(channel_ != NULL);
3510e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ASSERT(session_ != NULL);
3520e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  std::string content_name;
3530e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  const SecureTunnelContentDescription* remote_tunnel = NULL;
3540e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (!FindSecureTunnelContent(session_->remote_description(),
3550e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                               &content_name, &remote_tunnel)) {
3560e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    session_->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
3570e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    return;
3580e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  }
3590e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
3600e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  const std::string& cert_pem =
3610e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      role_ == INITIATOR ? remote_tunnel->server_pem_certificate :
3620e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org                           remote_tunnel->client_pem_certificate;
3632a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org  rtc::scoped_ptr<rtc::SSLCertificate> peer_cert(
3642a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org      ParseCertificate(cert_pem));
3650e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  if (peer_cert == NULL) {
3660e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    ASSERT(role_ == INITIATOR);  // when RESPONDER we validated it earlier
3670e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    LOG(LS_ERROR)
3680e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org        << "Rejecting secure tunnel accept with invalid cetificate";
3690e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    session_->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
3700e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org    return;
3710e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  }
3720e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ASSERT(ssl_stream_reference_.get() != NULL);
3732a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org  rtc::SSLStreamAdapter* ssl_stream =
3742a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org      static_cast<rtc::SSLStreamAdapter*>(
3750e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org          ssl_stream_reference_->GetStream());
3762a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org
3772a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org  std::string algorithm;
3782a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org  if (!peer_cert->GetSignatureDigestAlgorithm(&algorithm)) {
3792a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org    LOG(LS_ERROR) << "Failed to get the algorithm for the peer cert signature";
3802a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org    return;
3812a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org  }
3822a86ce22ccc387dfa6f8a98ce3eba5c1e6f9e538buildbot@webrtc.org  unsigned char digest[rtc::MessageDigest::kMaxSize];
3832a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org  size_t digest_len;
3842a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org  peer_cert->ComputeDigest(algorithm, digest, ARRAY_SIZE(digest), &digest_len);
3852a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org  ssl_stream->SetPeerCertificateDigest(algorithm, digest, digest_len);
3862a81a3893cea812cfa676ff7553038078c17f56cwu@webrtc.org
3870e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // We no longer need our handle to the ssl stream.
3880e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  ssl_stream_reference_.reset();
3890e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  LOG(LS_INFO) << "Connecting tunnel";
3900e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // This will try to connect the PseudoTcpChannel. If and when that
3910e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // succeeds, then ssl negotiation will take place, and when that
3920e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  // succeeds, the tunnel stream will finally open.
3930e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org  VERIFY(channel_->Connect(
3940e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org      content_name, "tcp", ICE_CANDIDATE_COMPONENT_DEFAULT));
3950e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}
3960e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org
3970e118e7129884fbea117e78d6f2068139a414dbhenrike@webrtc.org}  // namespace cricket
398