| Index: webrtc/libjingle/session/tunnel/securetunnelsessionclient.cc
|
| diff --git a/webrtc/libjingle/session/tunnel/securetunnelsessionclient.cc b/webrtc/libjingle/session/tunnel/securetunnelsessionclient.cc
|
| deleted file mode 100644
|
| index 5f6f9dfdf95a234bd5a0fe20a93ec009fcb56399..0000000000000000000000000000000000000000
|
| --- a/webrtc/libjingle/session/tunnel/securetunnelsessionclient.cc
|
| +++ /dev/null
|
| @@ -1,397 +0,0 @@
|
| -/*
|
| - * libjingle
|
| - * Copyright 2004--2008, Google Inc.
|
| - *
|
| - * Redistribution and use in source and binary forms, with or without
|
| - * modification, are permitted provided that the following conditions are met:
|
| - *
|
| - * 1. Redistributions of source code must retain the above copyright notice,
|
| - * this list of conditions and the following disclaimer.
|
| - * 2. Redistributions in binary form must reproduce the above copyright notice,
|
| - * this list of conditions and the following disclaimer in the documentation
|
| - * and/or other materials provided with the distribution.
|
| - * 3. The name of the author may not be used to endorse or promote products
|
| - * derived from this software without specific prior written permission.
|
| - *
|
| - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
| - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
| - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
| - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
| - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
| - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
| - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
| - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
| - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
| - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
| - */
|
| -
|
| -// SecureTunnelSessionClient and SecureTunnelSession implementation.
|
| -
|
| -#include "webrtc/p2p/base/transportchannel.h"
|
| -#include "webrtc/libjingle/session/tunnel/pseudotcpchannel.h"
|
| -#include "webrtc/libjingle/session/tunnel/securetunnelsessionclient.h"
|
| -#include "webrtc/libjingle/xmllite/xmlelement.h"
|
| -#include "webrtc/base/basicdefs.h"
|
| -#include "webrtc/base/basictypes.h"
|
| -#include "webrtc/base/common.h"
|
| -#include "webrtc/base/helpers.h"
|
| -#include "webrtc/base/logging.h"
|
| -#include "webrtc/base/sslidentity.h"
|
| -#include "webrtc/base/sslstreamadapter.h"
|
| -#include "webrtc/base/stringutils.h"
|
| -
|
| -namespace cricket {
|
| -
|
| -// XML elements and namespaces for XMPP stanzas used in content exchanges.
|
| -
|
| -const char NS_SECURE_TUNNEL[] = "http://www.google.com/talk/securetunnel";
|
| -const buzz::StaticQName QN_SECURE_TUNNEL_DESCRIPTION =
|
| - { NS_SECURE_TUNNEL, "description" };
|
| -const buzz::StaticQName QN_SECURE_TUNNEL_TYPE =
|
| - { NS_SECURE_TUNNEL, "type" };
|
| -const buzz::StaticQName QN_SECURE_TUNNEL_CLIENT_CERT =
|
| - { NS_SECURE_TUNNEL, "client-cert" };
|
| -const buzz::StaticQName QN_SECURE_TUNNEL_SERVER_CERT =
|
| - { NS_SECURE_TUNNEL, "server-cert" };
|
| -const char CN_SECURE_TUNNEL[] = "securetunnel";
|
| -
|
| -// SecureTunnelContentDescription
|
| -
|
| -// TunnelContentDescription is extended to hold string forms of the
|
| -// client and server certificate, PEM encoded.
|
| -
|
| -struct SecureTunnelContentDescription : public ContentDescription {
|
| - std::string description;
|
| - std::string client_pem_certificate;
|
| - std::string server_pem_certificate;
|
| -
|
| - SecureTunnelContentDescription(const std::string& desc,
|
| - const std::string& client_pem_cert,
|
| - const std::string& server_pem_cert)
|
| - : description(desc),
|
| - client_pem_certificate(client_pem_cert),
|
| - server_pem_certificate(server_pem_cert) {
|
| - }
|
| - virtual ContentDescription* Copy() const {
|
| - return new SecureTunnelContentDescription(*this);
|
| - }
|
| -};
|
| -
|
| -// SecureTunnelSessionClient
|
| -
|
| -SecureTunnelSessionClient::SecureTunnelSessionClient(
|
| - const buzz::Jid& jid, SessionManager* manager)
|
| - : TunnelSessionClient(jid, manager, NS_SECURE_TUNNEL) {
|
| -}
|
| -
|
| -void SecureTunnelSessionClient::SetIdentity(rtc::SSLIdentity* identity) {
|
| - ASSERT(identity_.get() == NULL);
|
| - identity_.reset(identity);
|
| -}
|
| -
|
| -bool SecureTunnelSessionClient::GenerateIdentity() {
|
| - ASSERT(identity_.get() == NULL);
|
| - identity_.reset(rtc::SSLIdentity::Generate(
|
| - // The name on the certificate does not matter: the peer will
|
| - // make sure the cert it gets during SSL negotiation matches the
|
| - // one it got from XMPP. It would be neat to put something
|
| - // recognizable in there such as the JID, except this will show
|
| - // in clear during the SSL negotiation and so it could be a
|
| - // privacy issue. Specifying an empty string here causes
|
| - // it to use a random string.
|
| -#ifdef _DEBUG
|
| - jid().Str()
|
| -#else
|
| - ""
|
| -#endif
|
| - ));
|
| - if (identity_.get() == NULL) {
|
| - LOG(LS_ERROR) << "Failed to generate SSL identity";
|
| - return false;
|
| - }
|
| - return true;
|
| -}
|
| -
|
| -rtc::SSLIdentity& SecureTunnelSessionClient::GetIdentity() const {
|
| - ASSERT(identity_.get() != NULL);
|
| - return *identity_;
|
| -}
|
| -
|
| -// Parses a certificate from a PEM encoded string.
|
| -// Returns NULL on failure.
|
| -// The caller is responsible for freeing the returned object.
|
| -static rtc::SSLCertificate* ParseCertificate(
|
| - const std::string& pem_cert) {
|
| - if (pem_cert.empty())
|
| - return NULL;
|
| - return rtc::SSLCertificate::FromPEMString(pem_cert);
|
| -}
|
| -
|
| -TunnelSession* SecureTunnelSessionClient::MakeTunnelSession(
|
| - Session* session, rtc::Thread* stream_thread,
|
| - TunnelSessionRole role) {
|
| - return new SecureTunnelSession(this, session, stream_thread, role);
|
| -}
|
| -
|
| -bool FindSecureTunnelContent(const cricket::SessionDescription* sdesc,
|
| - std::string* name,
|
| - const SecureTunnelContentDescription** content) {
|
| - const ContentInfo* cinfo = sdesc->FirstContentByType(NS_SECURE_TUNNEL);
|
| - if (cinfo == NULL)
|
| - return false;
|
| -
|
| - *name = cinfo->name;
|
| - *content = static_cast<const SecureTunnelContentDescription*>(
|
| - cinfo->description);
|
| - return true;
|
| -}
|
| -
|
| -void SecureTunnelSessionClient::OnIncomingTunnel(const buzz::Jid &jid,
|
| - Session *session) {
|
| - std::string content_name;
|
| - const SecureTunnelContentDescription* content = NULL;
|
| - if (!FindSecureTunnelContent(session->remote_description(),
|
| - &content_name, &content)) {
|
| - ASSERT(false);
|
| - }
|
| -
|
| - // Validate the certificate
|
| - rtc::scoped_ptr<rtc::SSLCertificate> peer_cert(
|
| - ParseCertificate(content->client_pem_certificate));
|
| - if (peer_cert.get() == NULL) {
|
| - LOG(LS_ERROR)
|
| - << "Rejecting incoming secure tunnel with invalid cetificate";
|
| - DeclineTunnel(session);
|
| - return;
|
| - }
|
| - // If there were a convenient place we could have cached the
|
| - // peer_cert so as not to have to parse it a second time when
|
| - // configuring the tunnel.
|
| - SignalIncomingTunnel(this, jid, content->description, session);
|
| -}
|
| -
|
| -// The XML representation of a session initiation request (XMPP IQ),
|
| -// containing the initiator's SecureTunnelContentDescription,
|
| -// looks something like this:
|
| -// <iq from="INITIATOR@gmail.com/pcpE101B7F4"
|
| -// to="RECIPIENT@gmail.com/pcp8B87F0A3"
|
| -// type="set" id="3">
|
| -// <session xmlns="http://www.google.com/session"
|
| -// type="initiate" id="2508605813"
|
| -// initiator="INITIATOR@gmail.com/pcpE101B7F4">
|
| -// <description xmlns="http://www.google.com/talk/securetunnel">
|
| -// <type>send:filename</type>
|
| -// <client-cert>
|
| -// -----BEGIN CERTIFICATE-----
|
| -// INITIATOR'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
|
| -// -----END CERTIFICATE-----
|
| -// </client-cert>
|
| -// </description>
|
| -// <transport xmlns="http://www.google.com/transport/p2p"/>
|
| -// </session>
|
| -// </iq>
|
| -
|
| -// The session accept iq, containing the recipient's certificate and
|
| -// echoing the initiator's certificate, looks something like this:
|
| -// <iq from="RECIPIENT@gmail.com/pcpE101B7F4"
|
| -// to="INITIATOR@gmail.com/pcpE101B7F4"
|
| -// type="set" id="5">
|
| -// <session xmlns="http://www.google.com/session"
|
| -// type="accept" id="2508605813"
|
| -// initiator="sdoyon911@gmail.com/pcpE101B7F4">
|
| -// <description xmlns="http://www.google.com/talk/securetunnel">
|
| -// <type>send:FILENAME</type>
|
| -// <client-cert>
|
| -// -----BEGIN CERTIFICATE-----
|
| -// INITIATOR'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
|
| -// -----END CERTIFICATE-----
|
| -// </client-cert>
|
| -// <server-cert>
|
| -// -----BEGIN CERTIFICATE-----
|
| -// RECIPIENT'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
|
| -// -----END CERTIFICATE-----
|
| -// </server-cert>
|
| -// </description>
|
| -// </session>
|
| -// </iq>
|
| -
|
| -
|
| -bool SecureTunnelSessionClient::ParseContent(SignalingProtocol protocol,
|
| - const buzz::XmlElement* elem,
|
| - ContentDescription** content,
|
| - ParseError* error) {
|
| - const buzz::XmlElement* type_elem = elem->FirstNamed(QN_SECURE_TUNNEL_TYPE);
|
| -
|
| - if (type_elem == NULL)
|
| - // Missing mandatory XML element.
|
| - return false;
|
| -
|
| - // Here we consider the certificate components to be optional. In
|
| - // practice the client certificate is always present, and the server
|
| - // certificate is initially missing from the session description
|
| - // sent during session initiation. OnAccept() will enforce that we
|
| - // have a certificate for our peer.
|
| - const buzz::XmlElement* client_cert_elem =
|
| - elem->FirstNamed(QN_SECURE_TUNNEL_CLIENT_CERT);
|
| - const buzz::XmlElement* server_cert_elem =
|
| - elem->FirstNamed(QN_SECURE_TUNNEL_SERVER_CERT);
|
| - *content = new SecureTunnelContentDescription(
|
| - type_elem->BodyText(),
|
| - client_cert_elem ? client_cert_elem->BodyText() : "",
|
| - server_cert_elem ? server_cert_elem->BodyText() : "");
|
| - return true;
|
| -}
|
| -
|
| -bool SecureTunnelSessionClient::WriteContent(
|
| - SignalingProtocol protocol, const ContentDescription* untyped_content,
|
| - buzz::XmlElement** elem, WriteError* error) {
|
| - const SecureTunnelContentDescription* content =
|
| - static_cast<const SecureTunnelContentDescription*>(untyped_content);
|
| -
|
| - buzz::XmlElement* root =
|
| - new buzz::XmlElement(QN_SECURE_TUNNEL_DESCRIPTION, true);
|
| - buzz::XmlElement* type_elem = new buzz::XmlElement(QN_SECURE_TUNNEL_TYPE);
|
| - type_elem->SetBodyText(content->description);
|
| - root->AddElement(type_elem);
|
| - if (!content->client_pem_certificate.empty()) {
|
| - buzz::XmlElement* client_cert_elem =
|
| - new buzz::XmlElement(QN_SECURE_TUNNEL_CLIENT_CERT);
|
| - client_cert_elem->SetBodyText(content->client_pem_certificate);
|
| - root->AddElement(client_cert_elem);
|
| - }
|
| - if (!content->server_pem_certificate.empty()) {
|
| - buzz::XmlElement* server_cert_elem =
|
| - new buzz::XmlElement(QN_SECURE_TUNNEL_SERVER_CERT);
|
| - server_cert_elem->SetBodyText(content->server_pem_certificate);
|
| - root->AddElement(server_cert_elem);
|
| - }
|
| - *elem = root;
|
| - return true;
|
| -}
|
| -
|
| -SessionDescription* NewSecureTunnelSessionDescription(
|
| - const std::string& content_name, ContentDescription* content) {
|
| - SessionDescription* sdesc = new SessionDescription();
|
| - sdesc->AddContent(content_name, NS_SECURE_TUNNEL, content);
|
| - return sdesc;
|
| -}
|
| -
|
| -SessionDescription* SecureTunnelSessionClient::CreateOffer(
|
| - const buzz::Jid &jid, const std::string &description) {
|
| - // We are the initiator so we are the client. Put our cert into the
|
| - // description.
|
| - std::string pem_cert = GetIdentity().certificate().ToPEMString();
|
| - return NewSecureTunnelSessionDescription(
|
| - CN_SECURE_TUNNEL,
|
| - new SecureTunnelContentDescription(description, pem_cert, ""));
|
| -}
|
| -
|
| -SessionDescription* SecureTunnelSessionClient::CreateAnswer(
|
| - const SessionDescription* offer) {
|
| - std::string content_name;
|
| - const SecureTunnelContentDescription* offer_tunnel = NULL;
|
| - if (!FindSecureTunnelContent(offer, &content_name, &offer_tunnel))
|
| - return NULL;
|
| -
|
| - // We are accepting a session request. We need to add our cert, the
|
| - // server cert, into the description. The client cert was validated
|
| - // in OnIncomingTunnel().
|
| - ASSERT(!offer_tunnel->client_pem_certificate.empty());
|
| - return NewSecureTunnelSessionDescription(
|
| - content_name,
|
| - new SecureTunnelContentDescription(
|
| - offer_tunnel->description,
|
| - offer_tunnel->client_pem_certificate,
|
| - GetIdentity().certificate().ToPEMString()));
|
| -}
|
| -
|
| -// SecureTunnelSession
|
| -
|
| -SecureTunnelSession::SecureTunnelSession(
|
| - SecureTunnelSessionClient* client, Session* session,
|
| - rtc::Thread* stream_thread, TunnelSessionRole role)
|
| - : TunnelSession(client, session, stream_thread),
|
| - role_(role) {
|
| -}
|
| -
|
| -rtc::StreamInterface* SecureTunnelSession::MakeSecureStream(
|
| - rtc::StreamInterface* stream) {
|
| - rtc::SSLStreamAdapter* ssl_stream =
|
| - rtc::SSLStreamAdapter::Create(stream);
|
| - rtc::SSLIdentity* identity =
|
| - static_cast<SecureTunnelSessionClient*>(client_)->
|
| - GetIdentity().GetReference();
|
| - ssl_stream->SetIdentity(identity);
|
| - if (role_ == RESPONDER)
|
| - ssl_stream->SetServerRole();
|
| - ssl_stream->StartSSLWithPeer();
|
| -
|
| - // SSL negotiation will start on the stream as soon as it
|
| - // opens. However our SSLStreamAdapter still hasn't been told what
|
| - // certificate to allow for our peer. If we are the initiator, we do
|
| - // not have the peer's certificate yet: we will obtain it from the
|
| - // session accept message which we will receive later (see
|
| - // OnAccept()). We won't Connect() the PseudoTcpChannel until we get
|
| - // that, so the stream will stay closed until then. Keep a handle
|
| - // on the streem so we can configure the peer certificate later.
|
| - ssl_stream_reference_.reset(new rtc::StreamReference(ssl_stream));
|
| - return ssl_stream_reference_->NewReference();
|
| -}
|
| -
|
| -rtc::StreamInterface* SecureTunnelSession::GetStream() {
|
| - ASSERT(channel_ != NULL);
|
| - ASSERT(ssl_stream_reference_.get() == NULL);
|
| - return MakeSecureStream(channel_->GetStream());
|
| -}
|
| -
|
| -void SecureTunnelSession::OnAccept() {
|
| - // We have either sent or received a session accept: it's time to
|
| - // connect the tunnel. First we must set the peer certificate.
|
| - ASSERT(channel_ != NULL);
|
| - ASSERT(session_ != NULL);
|
| - std::string content_name;
|
| - const SecureTunnelContentDescription* remote_tunnel = NULL;
|
| - if (!FindSecureTunnelContent(session_->remote_description(),
|
| - &content_name, &remote_tunnel)) {
|
| - session_->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
|
| - return;
|
| - }
|
| -
|
| - const std::string& cert_pem =
|
| - role_ == INITIATOR ? remote_tunnel->server_pem_certificate :
|
| - remote_tunnel->client_pem_certificate;
|
| - rtc::scoped_ptr<rtc::SSLCertificate> peer_cert(
|
| - ParseCertificate(cert_pem));
|
| - if (peer_cert == NULL) {
|
| - ASSERT(role_ == INITIATOR); // when RESPONDER we validated it earlier
|
| - LOG(LS_ERROR)
|
| - << "Rejecting secure tunnel accept with invalid cetificate";
|
| - session_->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
|
| - return;
|
| - }
|
| - ASSERT(ssl_stream_reference_.get() != NULL);
|
| - rtc::SSLStreamAdapter* ssl_stream =
|
| - static_cast<rtc::SSLStreamAdapter*>(
|
| - ssl_stream_reference_->GetStream());
|
| -
|
| - std::string algorithm;
|
| - if (!peer_cert->GetSignatureDigestAlgorithm(&algorithm)) {
|
| - LOG(LS_ERROR) << "Failed to get the algorithm for the peer cert signature";
|
| - return;
|
| - }
|
| - unsigned char digest[rtc::MessageDigest::kMaxSize];
|
| - size_t digest_len;
|
| - peer_cert->ComputeDigest(algorithm, digest, ARRAY_SIZE(digest), &digest_len);
|
| - ssl_stream->SetPeerCertificateDigest(algorithm, digest, digest_len);
|
| -
|
| - // We no longer need our handle to the ssl stream.
|
| - ssl_stream_reference_.reset();
|
| - LOG(LS_INFO) << "Connecting tunnel";
|
| - // This will try to connect the PseudoTcpChannel. If and when that
|
| - // succeeds, then ssl negotiation will take place, and when that
|
| - // succeeds, the tunnel stream will finally open.
|
| - VERIFY(channel_->Connect(
|
| - content_name, "tcp", ICE_CANDIDATE_COMPONENT_DEFAULT));
|
| -}
|
| -
|
| -} // namespace cricket
|
|
|