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