1f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch/* 2f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * libjingle 3f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * Copyright 2004--2008, Google Inc. 4f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * 5f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * Redistribution and use in source and binary forms, with or without 6f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * modification, are permitted provided that the following conditions are met: 7f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * 8f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * 1. Redistributions of source code must retain the above copyright notice, 9f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * this list of conditions and the following disclaimer. 10f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * 2. Redistributions in binary form must reproduce the above copyright notice, 11f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * this list of conditions and the following disclaimer in the documentation 12f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * and/or other materials provided with the distribution. 13f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * 3. The name of the author may not be used to endorse or promote products 14f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * derived from this software without specific prior written permission. 15f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * 16f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch */ 27f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 28f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// SecureTunnelSessionClient and SecureTunnelSession implementation. 29f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 30f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/session/tunnel/securetunnelsessionclient.h" 31f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/basicdefs.h" 32f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/basictypes.h" 33f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/common.h" 34f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/helpers.h" 35f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/logging.h" 36f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/stringutils.h" 37f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/sslidentity.h" 38f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/base/sslstreamadapter.h" 39f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/p2p/base/transportchannel.h" 40f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/xmllite/xmlelement.h" 41f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#include "talk/session/tunnel/pseudotcpchannel.h" 42f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 43f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochnamespace cricket { 44f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// XML elements and namespaces for XMPP stanzas used in content exchanges. 46f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 47f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochconst std::string NS_SECURE_TUNNEL("http://www.google.com/talk/securetunnel"); 48f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochconst buzz::QName QN_SECURE_TUNNEL_DESCRIPTION(NS_SECURE_TUNNEL, 49f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch "description"); 50f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochconst buzz::QName QN_SECURE_TUNNEL_TYPE(NS_SECURE_TUNNEL, "type"); 51f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochconst buzz::QName QN_SECURE_TUNNEL_CLIENT_CERT(NS_SECURE_TUNNEL, 52f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch "client-cert"); 53f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochconst buzz::QName QN_SECURE_TUNNEL_SERVER_CERT(NS_SECURE_TUNNEL, 54f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch "server-cert"); 553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickconst std::string CN_SECURE_TUNNEL("securetunnel"); 56f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// SecureTunnelContentDescription 58f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 593345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// TunnelContentDescription is extended to hold string forms of the 60f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// client and server certificate, PEM encoded. 61f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 623345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickstruct SecureTunnelContentDescription : public ContentDescription { 63f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch std::string description; 64f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch std::string client_pem_certificate; 65f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch std::string server_pem_certificate; 66f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick SecureTunnelContentDescription(const std::string& desc, 68f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch const std::string& client_pem_cert, 69f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch const std::string& server_pem_cert) 70f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch : description(desc), 71f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch client_pem_certificate(client_pem_cert), 72f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch server_pem_certificate(server_pem_cert) { 73f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch } 74f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch}; 75f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 76f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// SecureTunnelSessionClient 77f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 78f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen MurdochSecureTunnelSessionClient::SecureTunnelSessionClient( 79f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch const buzz::Jid& jid, SessionManager* manager) 80f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch : TunnelSessionClient(jid, manager, NS_SECURE_TUNNEL) { 81f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 82f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 83f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid SecureTunnelSessionClient::SetIdentity(talk_base::SSLIdentity* identity) { 84f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ASSERT(identity_.get() == NULL); 85f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch identity_.reset(identity); 86f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 87f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 88f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochbool SecureTunnelSessionClient::GenerateIdentity() { 89f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ASSERT(identity_.get() == NULL); 90f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch identity_.reset(talk_base::SSLIdentity::Generate( 91f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // The name on the certificate does not matter: the peer will 92f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // make sure the cert it gets during SSL negotiation matches the 93f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // one it got from XMPP. It would be neat to put something 94f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // recognizable in there such as the JID, except this will show 95f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // in clear during the SSL negotiation and so it could be a 96f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // privacy issue. Specifying an empty string here causes 97f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // it to use a random string. 98f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#ifdef _DEBUG 99f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch jid().Str() 100f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#else 101f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch "" 102f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch#endif 103f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch )); 104f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch if (identity_.get() == NULL) { 105f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch LOG(LS_ERROR) << "Failed to generate SSL identity"; 106f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch return false; 107f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch } 108f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch return true; 109f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 110f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 111f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochtalk_base::SSLIdentity& SecureTunnelSessionClient::GetIdentity() const { 112f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ASSERT(identity_.get() != NULL); 113f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch return *identity_; 114f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 115f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 116f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// Parses a certificate from a PEM encoded string. 117f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// Returns NULL on failure. 118f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// The caller is responsible for freeing the returned object. 119f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochstatic talk_base::SSLCertificate* ParseCertificate( 120f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch const std::string& pem_cert) { 121f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch if (pem_cert.empty()) 122f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch return NULL; 123f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch return talk_base::SSLCertificate::FromPEMString(pem_cert, NULL); 124f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 125f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 126f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen MurdochTunnelSession* SecureTunnelSessionClient::MakeTunnelSession( 127f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch Session* session, talk_base::Thread* stream_thread, 128f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch TunnelSessionRole role) { 129f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch return new SecureTunnelSession(this, session, stream_thread, role); 130f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 131f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 132731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickbool FindSecureTunnelContent(const cricket::SessionDescription* sdesc, 133731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::string* name, 134731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick const SecureTunnelContentDescription** content) { 1353345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const ContentInfo* cinfo = sdesc->FirstContentByType(NS_SECURE_TUNNEL); 1363345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (cinfo == NULL) 137731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return false; 1383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 139731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick *name = cinfo->name; 140731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick *content = static_cast<const SecureTunnelContentDescription*>( 1413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick cinfo->description); 142731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return true; 1433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 1443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 145f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid SecureTunnelSessionClient::OnIncomingTunnel(const buzz::Jid &jid, 146f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch Session *session) { 147731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::string content_name; 148731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick const SecureTunnelContentDescription* content = NULL; 149731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!FindSecureTunnelContent(session->remote_description(), 150731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick &content_name, &content)) { 151731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick ASSERT(false); 152731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 1533345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 154f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // Validate the certificate 155f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch talk_base::scoped_ptr<talk_base::SSLCertificate> peer_cert( 1563345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ParseCertificate(content->client_pem_certificate)); 157f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch if (peer_cert.get() == NULL) { 158f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch LOG(LS_ERROR) 159f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch << "Rejecting incoming secure tunnel with invalid cetificate"; 160f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch DeclineTunnel(session); 161f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch return; 162f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch } 163f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // If there were a convenient place we could have cached the 164f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // peer_cert so as not to have to parse it a second time when 165f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // configuring the tunnel. 1663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick SignalIncomingTunnel(this, jid, content->description, session); 167f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 168f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 169f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// The XML representation of a session initiation request (XMPP IQ), 1703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick// containing the initiator's SecureTunnelContentDescription, 171f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// looks something like this: 172f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <iq from="INITIATOR@gmail.com/pcpE101B7F4" 173f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// to="RECIPIENT@gmail.com/pcp8B87F0A3" 174f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// type="set" id="3"> 175f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <session xmlns="http://www.google.com/session" 176f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// type="initiate" id="2508605813" 177f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// initiator="INITIATOR@gmail.com/pcpE101B7F4"> 178f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <description xmlns="http://www.google.com/talk/securetunnel"> 179f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <type>send:filename</type> 180f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <client-cert> 181f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// -----BEGIN CERTIFICATE----- 182f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// INITIATOR'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH) 183f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// -----END CERTIFICATE----- 184f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// </client-cert> 185f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// </description> 186f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <transport xmlns="http://www.google.com/transport/p2p"/> 187f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// </session> 188f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// </iq> 189f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 190f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// The session accept iq, containing the recipient's certificate and 191f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// echoing the initiator's certificate, looks something like this: 192f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <iq from="RECIPIENT@gmail.com/pcpE101B7F4" 193f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// to="INITIATOR@gmail.com/pcpE101B7F4" 194f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// type="set" id="5"> 195f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <session xmlns="http://www.google.com/session" 196f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// type="accept" id="2508605813" 197f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// initiator="sdoyon911@gmail.com/pcpE101B7F4"> 198f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <description xmlns="http://www.google.com/talk/securetunnel"> 199f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <type>send:FILENAME</type> 200f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <client-cert> 201f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// -----BEGIN CERTIFICATE----- 202f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// INITIATOR'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH) 203f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// -----END CERTIFICATE----- 204f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// </client-cert> 205f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// <server-cert> 206f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// -----BEGIN CERTIFICATE----- 207f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// RECIPIENT'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH) 208f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// -----END CERTIFICATE----- 209f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// </server-cert> 210f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// </description> 211f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// </session> 212f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// </iq> 213f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 214f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 215731df977c0511bca2206b5f333555b1205ff1f43Iain Merrickbool SecureTunnelSessionClient::ParseContent(SignalingProtocol protocol, 216731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick const buzz::XmlElement* elem, 2173345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const ContentDescription** content, 2183345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ParseError* error) { 2193345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const buzz::XmlElement* type_elem = elem->FirstNamed(QN_SECURE_TUNNEL_TYPE); 2203345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 221f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch if (type_elem == NULL) 222f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // Missing mandatory XML element. 2233345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return false; 2243345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 225f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // Here we consider the certificate components to be optional. In 226f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // practice the client certificate is always present, and the server 227f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // certificate is initially missing from the session description 228f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // sent during session initiation. OnAccept() will enforce that we 229f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // have a certificate for our peer. 230f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch const buzz::XmlElement* client_cert_elem = 2313345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick elem->FirstNamed(QN_SECURE_TUNNEL_CLIENT_CERT); 232f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch const buzz::XmlElement* server_cert_elem = 2333345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick elem->FirstNamed(QN_SECURE_TUNNEL_SERVER_CERT); 2343345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick *content = new SecureTunnelContentDescription( 235f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch type_elem->BodyText(), 236f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch client_cert_elem ? client_cert_elem->BodyText() : "", 237f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch server_cert_elem ? server_cert_elem->BodyText() : ""); 2383345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return true; 239f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 240f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 2413345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrickbool SecureTunnelSessionClient::WriteContent( 242731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick SignalingProtocol protocol, const ContentDescription* untyped_content, 2433345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick buzz::XmlElement** elem, WriteError* error) { 2443345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const SecureTunnelContentDescription* content = 2453345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick static_cast<const SecureTunnelContentDescription*>(untyped_content); 2463345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 247f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch buzz::XmlElement* root = 248f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch new buzz::XmlElement(QN_SECURE_TUNNEL_DESCRIPTION, true); 249f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch buzz::XmlElement* type_elem = new buzz::XmlElement(QN_SECURE_TUNNEL_TYPE); 2503345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick type_elem->SetBodyText(content->description); 251f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch root->AddElement(type_elem); 2523345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!content->client_pem_certificate.empty()) { 253f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch buzz::XmlElement* client_cert_elem = 254f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch new buzz::XmlElement(QN_SECURE_TUNNEL_CLIENT_CERT); 2553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick client_cert_elem->SetBodyText(content->client_pem_certificate); 256f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch root->AddElement(client_cert_elem); 257f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch } 2583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick if (!content->server_pem_certificate.empty()) { 259f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch buzz::XmlElement* server_cert_elem = 260f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch new buzz::XmlElement(QN_SECURE_TUNNEL_SERVER_CERT); 2613345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick server_cert_elem->SetBodyText(content->server_pem_certificate); 262f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch root->AddElement(server_cert_elem); 263f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch } 2643345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick *elem = root; 2653345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return true; 2663345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick} 2673345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 2683345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickSessionDescription* NewSecureTunnelSessionDescription( 269731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick const std::string& content_name, const ContentDescription* content) { 2703345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick SessionDescription* sdesc = new SessionDescription(); 271731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick sdesc->AddContent(content_name, NS_SECURE_TUNNEL, content); 2723345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return sdesc; 273f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 274f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 2753345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickSessionDescription* SecureTunnelSessionClient::CreateOffer( 276f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch const buzz::Jid &jid, const std::string &description) { 277f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // We are the initiator so we are the client. Put our cert into the 278f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // description. 279f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch std::string pem_cert = GetIdentity().certificate().ToPEMString(); 2803345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return NewSecureTunnelSessionDescription( 281731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick CN_SECURE_TUNNEL, 2823345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick new SecureTunnelContentDescription(description, pem_cert, "")); 283f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 284f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 2853345a6884c488ff3a535c2c9acdd33d74b37e311Iain MerrickSessionDescription* SecureTunnelSessionClient::CreateAnswer( 2863345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick const SessionDescription* offer) { 287731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::string content_name; 288731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick const SecureTunnelContentDescription* offer_tunnel = NULL; 289731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!FindSecureTunnelContent(offer, &content_name, &offer_tunnel)) 2903345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return NULL; 2913345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 292f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // We are accepting a session request. We need to add our cert, the 293f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // server cert, into the description. The client cert was validated 294f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // in OnIncomingTunnel(). 2953345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick ASSERT(!offer_tunnel->client_pem_certificate.empty()); 2963345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick return NewSecureTunnelSessionDescription( 297731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick content_name, 2983345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick new SecureTunnelContentDescription( 2993345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick offer_tunnel->description, 3003345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick offer_tunnel->client_pem_certificate, 3013345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick GetIdentity().certificate().ToPEMString())); 302f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 303f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 304f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch// SecureTunnelSession 305f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 306f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen MurdochSecureTunnelSession::SecureTunnelSession( 307f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch SecureTunnelSessionClient* client, Session* session, 308f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch talk_base::Thread* stream_thread, TunnelSessionRole role) 309f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch : TunnelSession(client, session, stream_thread), 310f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch role_(role) { 311f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 312f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 313f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochtalk_base::StreamInterface* SecureTunnelSession::MakeSecureStream( 314f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch talk_base::StreamInterface* stream) { 315f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch talk_base::SSLStreamAdapter* ssl_stream = 316f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch talk_base::SSLStreamAdapter::Create(stream); 317f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch talk_base::SSLIdentity* identity = 318f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch static_cast<SecureTunnelSessionClient*>(client_)-> 319f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch GetIdentity().GetReference(); 320f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ssl_stream->SetIdentity(identity); 321f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch if (role_ == RESPONDER) 322f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ssl_stream->SetServerRole(); 323f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ssl_stream->StartSSLWithPeer(); 324f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 325f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // SSL negotiation will start on the stream as soon as it 326f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // opens. However our SSLStreamAdapter still hasn't been told what 327f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // certificate to allow for our peer. If we are the initiator, we do 328f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // not have the peer's certificate yet: we will obtain it from the 329f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // session accept message which we will receive later (see 330f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // OnAccept()). We won't Connect() the PseudoTcpChannel until we get 331f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // that, so the stream will stay closed until then. Keep a handle 332f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // on the streem so we can configure the peer certificate later. 333f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ssl_stream_reference_.reset(new talk_base::StreamReference(ssl_stream)); 334f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch return ssl_stream_reference_->NewReference(); 335f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 336f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 337f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochtalk_base::StreamInterface* SecureTunnelSession::GetStream() { 338f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ASSERT(channel_ != NULL); 339f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ASSERT(ssl_stream_reference_.get() == NULL); 340f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch return MakeSecureStream(channel_->GetStream()); 341f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 342f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 343f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdochvoid SecureTunnelSession::OnAccept() { 344f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // We have either sent or received a session accept: it's time to 345f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // connect the tunnel. First we must set the peer certificate. 346f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ASSERT(channel_ != NULL); 347f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ASSERT(session_ != NULL); 348731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick std::string content_name; 349731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick const SecureTunnelContentDescription* remote_tunnel = NULL; 350731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick if (!FindSecureTunnelContent(session_->remote_description(), 351731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick &content_name, &remote_tunnel)) { 352731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick session_->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS); 353731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick return; 354731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick } 3553345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick 356f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch const std::string& cert_pem = 3573345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick role_ == INITIATOR ? remote_tunnel->server_pem_certificate : 3583345a6884c488ff3a535c2c9acdd33d74b37e311Iain Merrick remote_tunnel->client_pem_certificate; 359f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch talk_base::SSLCertificate* peer_cert = 360f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ParseCertificate(cert_pem); 361f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch if (peer_cert == NULL) { 362f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ASSERT(role_ == INITIATOR); // when RESPONDER we validated it earlier 363f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch LOG(LS_ERROR) 364f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch << "Rejecting secure tunnel accept with invalid cetificate"; 365731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick session_->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS); 366f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch return; 367f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch } 368f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ASSERT(ssl_stream_reference_.get() != NULL); 369f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch talk_base::SSLStreamAdapter* ssl_stream = 370f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch static_cast<talk_base::SSLStreamAdapter*>( 371f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ssl_stream_reference_->GetStream()); 372f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ssl_stream->SetPeerCertificate(peer_cert); // pass ownership of certificate. 373f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // We no longer need our handle to the ssl stream. 374f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch ssl_stream_reference_.reset(); 375f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch LOG(LS_INFO) << "Connecting tunnel"; 376f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // This will try to connect the PseudoTcpChannel. If and when that 377f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // succeeds, then ssl negotiation will take place, and when that 378f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch // succeeds, the tunnel stream will finally open. 379731df977c0511bca2206b5f333555b1205ff1f43Iain Merrick VERIFY(channel_->Connect(content_name, "tcp")); 380f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} 381f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch 382f74420b3285b9fe04a7e00aa3b8c0ab07ea344bcBen Murdoch} // namespace cricket 383