~ubuntu-branches/debian/experimental/kopete/experimental

« back to all changes in this revision

Viewing changes to protocols/jabber/googletalk/libjingle/talk/session/tunnel/securetunnelsessionclient.cc

  • Committer: Package Import Robot
  • Author(s): Maximiliano Curia
  • Date: 2015-02-24 11:32:57 UTC
  • mfrom: (1.1.41 vivid)
  • Revision ID: package-import@ubuntu.com-20150224113257-gnupg4v7lzz18ij0
Tags: 4:14.12.2-1
* New upstream release (14.12.2).
* Bump Standards-Version to 3.9.6, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/*
2
 
 * libjingle
3
 
 * Copyright 2004--2008, Google Inc.
4
 
 *
5
 
 * Redistribution and use in source and binary forms, with or without
6
 
 * modification, are permitted provided that the following conditions are met:
7
 
 *
8
 
 *  1. Redistributions of source code must retain the above copyright notice,
9
 
 *     this list of conditions and the following disclaimer.
10
 
 *  2. Redistributions in binary form must reproduce the above copyright notice,
11
 
 *     this list of conditions and the following disclaimer in the documentation
12
 
 *     and/or other materials provided with the distribution.
13
 
 *  3. The name of the author may not be used to endorse or promote products
14
 
 *     derived from this software without specific prior written permission.
15
 
 *
16
 
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
17
 
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
18
 
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
19
 
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
20
 
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
21
 
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
22
 
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
23
 
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
24
 
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
25
 
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26
 
 */
27
 
 
28
 
// SecureTunnelSessionClient and SecureTunnelSession implementation.
29
 
 
30
 
#include "talk/session/tunnel/securetunnelsessionclient.h"
31
 
#include "talk/base/basicdefs.h"
32
 
#include "talk/base/basictypes.h"
33
 
#include "talk/base/common.h"
34
 
#include "talk/base/helpers.h"
35
 
#include "talk/base/logging.h"
36
 
#include "talk/base/stringutils.h"
37
 
#include "talk/base/sslidentity.h"
38
 
#include "talk/base/sslstreamadapter.h"
39
 
#include "talk/p2p/base/transportchannel.h"
40
 
#include "talk/xmllite/xmlelement.h"
41
 
#include "talk/session/tunnel/pseudotcpchannel.h"
42
 
 
43
 
namespace cricket {
44
 
 
45
 
// XML elements and namespaces for XMPP stanzas used in content exchanges.
46
 
 
47
 
const char NS_SECURE_TUNNEL[] = "http://www.google.com/talk/securetunnel";
48
 
const buzz::StaticQName QN_SECURE_TUNNEL_DESCRIPTION =
49
 
    { NS_SECURE_TUNNEL, "description" };
50
 
const buzz::StaticQName QN_SECURE_TUNNEL_TYPE =
51
 
    { NS_SECURE_TUNNEL, "type" };
52
 
const buzz::StaticQName QN_SECURE_TUNNEL_CLIENT_CERT =
53
 
    { NS_SECURE_TUNNEL, "client-cert" };
54
 
const buzz::StaticQName QN_SECURE_TUNNEL_SERVER_CERT =
55
 
    { NS_SECURE_TUNNEL, "server-cert" };
56
 
const char CN_SECURE_TUNNEL[] = "securetunnel";
57
 
 
58
 
// SecureTunnelContentDescription
59
 
 
60
 
// TunnelContentDescription is extended to hold string forms of the
61
 
// client and server certificate, PEM encoded.
62
 
 
63
 
struct SecureTunnelContentDescription : public ContentDescription {
64
 
  std::string description;
65
 
  std::string client_pem_certificate;
66
 
  std::string server_pem_certificate;
67
 
 
68
 
  SecureTunnelContentDescription(const std::string& desc,
69
 
                                 const std::string& client_pem_cert,
70
 
                                 const std::string& server_pem_cert)
71
 
      : description(desc),
72
 
        client_pem_certificate(client_pem_cert),
73
 
        server_pem_certificate(server_pem_cert) {
74
 
  }
75
 
  virtual ContentDescription* Copy() const {
76
 
    return new SecureTunnelContentDescription(*this);
77
 
  }
78
 
};
79
 
 
80
 
// SecureTunnelSessionClient
81
 
 
82
 
SecureTunnelSessionClient::SecureTunnelSessionClient(
83
 
    const buzz::Jid& jid, SessionManager* manager)
84
 
    : TunnelSessionClient(jid, manager, NS_SECURE_TUNNEL) {
85
 
}
86
 
 
87
 
void SecureTunnelSessionClient::SetIdentity(talk_base::SSLIdentity* identity) {
88
 
  ASSERT(identity_.get() == NULL);
89
 
  identity_.reset(identity);
90
 
}
91
 
 
92
 
bool SecureTunnelSessionClient::GenerateIdentity() {
93
 
  ASSERT(identity_.get() == NULL);
94
 
  identity_.reset(talk_base::SSLIdentity::Generate(
95
 
      // The name on the certificate does not matter: the peer will
96
 
      // make sure the cert it gets during SSL negotiation matches the
97
 
      // one it got from XMPP. It would be neat to put something
98
 
      // recognizable in there such as the JID, except this will show
99
 
      // in clear during the SSL negotiation and so it could be a
100
 
      // privacy issue. Specifying an empty string here causes
101
 
      // it to use a random string.
102
 
#ifdef _DEBUG
103
 
      jid().Str()
104
 
#else
105
 
      ""
106
 
#endif
107
 
      ));
108
 
  if (identity_.get() == NULL) {
109
 
    LOG(LS_ERROR) << "Failed to generate SSL identity";
110
 
    return false;
111
 
  }
112
 
  return true;
113
 
}
114
 
 
115
 
talk_base::SSLIdentity& SecureTunnelSessionClient::GetIdentity() const {
116
 
  ASSERT(identity_.get() != NULL);
117
 
  return *identity_;
118
 
}
119
 
 
120
 
// Parses a certificate from a PEM encoded string.
121
 
// Returns NULL on failure.
122
 
// The caller is responsible for freeing the returned object.
123
 
static talk_base::SSLCertificate* ParseCertificate(
124
 
    const std::string& pem_cert) {
125
 
  if (pem_cert.empty())
126
 
    return NULL;
127
 
  return talk_base::SSLCertificate::FromPEMString(pem_cert, NULL);
128
 
}
129
 
 
130
 
TunnelSession* SecureTunnelSessionClient::MakeTunnelSession(
131
 
    Session* session, talk_base::Thread* stream_thread,
132
 
    TunnelSessionRole role) {
133
 
  return new SecureTunnelSession(this, session, stream_thread, role);
134
 
}
135
 
 
136
 
bool FindSecureTunnelContent(const cricket::SessionDescription* sdesc,
137
 
                             std::string* name,
138
 
                             const SecureTunnelContentDescription** content) {
139
 
  const ContentInfo* cinfo = sdesc->FirstContentByType(NS_SECURE_TUNNEL);
140
 
  if (cinfo == NULL)
141
 
    return false;
142
 
 
143
 
  *name = cinfo->name;
144
 
  *content = static_cast<const SecureTunnelContentDescription*>(
145
 
      cinfo->description);
146
 
  return true;
147
 
}
148
 
 
149
 
void SecureTunnelSessionClient::OnIncomingTunnel(const buzz::Jid &jid,
150
 
                                                 Session *session) {
151
 
  std::string content_name;
152
 
  const SecureTunnelContentDescription* content = NULL;
153
 
  if (!FindSecureTunnelContent(session->remote_description(),
154
 
                               &content_name, &content)) {
155
 
    ASSERT(false);
156
 
  }
157
 
 
158
 
  // Validate the certificate
159
 
  talk_base::scoped_ptr<talk_base::SSLCertificate> peer_cert(
160
 
      ParseCertificate(content->client_pem_certificate));
161
 
  if (peer_cert.get() == NULL) {
162
 
    LOG(LS_ERROR)
163
 
        << "Rejecting incoming secure tunnel with invalid cetificate";
164
 
    DeclineTunnel(session);
165
 
    return;
166
 
  }
167
 
  // If there were a convenient place we could have cached the
168
 
  // peer_cert so as not to have to parse it a second time when
169
 
  // configuring the tunnel.
170
 
  SignalIncomingTunnel(this, jid, content->description, session);
171
 
}
172
 
 
173
 
// The XML representation of a session initiation request (XMPP IQ),
174
 
// containing the initiator's SecureTunnelContentDescription,
175
 
// looks something like this:
176
 
// <iq from="INITIATOR@gmail.com/pcpE101B7F4"
177
 
//       to="RECIPIENT@gmail.com/pcp8B87F0A3"
178
 
//       type="set" id="3">
179
 
//   <session xmlns="http://www.google.com/session"
180
 
//       type="initiate" id="2508605813"
181
 
//       initiator="INITIATOR@gmail.com/pcpE101B7F4">
182
 
//     <description xmlns="http://www.google.com/talk/securetunnel">
183
 
//       <type>send:filename</type>
184
 
//       <client-cert>
185
 
//         -----BEGIN CERTIFICATE-----
186
 
//         INITIATOR'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
187
 
//         -----END CERTIFICATE-----
188
 
//       </client-cert>
189
 
//     </description>
190
 
//     <transport xmlns="http://www.google.com/transport/p2p"/>
191
 
//   </session>
192
 
// </iq>
193
 
 
194
 
// The session accept iq, containing the recipient's certificate and
195
 
// echoing the initiator's certificate, looks something like this:
196
 
// <iq from="RECIPIENT@gmail.com/pcpE101B7F4"
197
 
//     to="INITIATOR@gmail.com/pcpE101B7F4"
198
 
//     type="set" id="5">
199
 
//   <session xmlns="http://www.google.com/session"
200
 
//       type="accept" id="2508605813"
201
 
//       initiator="sdoyon911@gmail.com/pcpE101B7F4">
202
 
//     <description xmlns="http://www.google.com/talk/securetunnel">
203
 
//       <type>send:FILENAME</type>
204
 
//       <client-cert>
205
 
//         -----BEGIN CERTIFICATE-----
206
 
//         INITIATOR'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
207
 
//         -----END CERTIFICATE-----
208
 
//       </client-cert>
209
 
//       <server-cert>
210
 
//         -----BEGIN CERTIFICATE-----
211
 
//         RECIPIENT'S CERTIFICATE IN PERM FORMAT (ASCII GIBBERISH)
212
 
//         -----END CERTIFICATE-----
213
 
//       </server-cert>
214
 
//     </description>
215
 
//   </session>
216
 
// </iq>
217
 
 
218
 
 
219
 
bool SecureTunnelSessionClient::ParseContent(SignalingProtocol protocol,
220
 
                                             const buzz::XmlElement* elem,
221
 
                                             const ContentDescription** content,
222
 
                                             ParseError* error) {
223
 
  const buzz::XmlElement* type_elem = elem->FirstNamed(QN_SECURE_TUNNEL_TYPE);
224
 
 
225
 
  if (type_elem == NULL)
226
 
    // Missing mandatory XML element.
227
 
    return false;
228
 
 
229
 
  // Here we consider the certificate components to be optional. In
230
 
  // practice the client certificate is always present, and the server
231
 
  // certificate is initially missing from the session description
232
 
  // sent during session initiation. OnAccept() will enforce that we
233
 
  // have a certificate for our peer.
234
 
  const buzz::XmlElement* client_cert_elem =
235
 
      elem->FirstNamed(QN_SECURE_TUNNEL_CLIENT_CERT);
236
 
  const buzz::XmlElement* server_cert_elem =
237
 
      elem->FirstNamed(QN_SECURE_TUNNEL_SERVER_CERT);
238
 
  *content = new SecureTunnelContentDescription(
239
 
      type_elem->BodyText(),
240
 
      client_cert_elem ? client_cert_elem->BodyText() : "",
241
 
      server_cert_elem ? server_cert_elem->BodyText() : "");
242
 
  return true;
243
 
}
244
 
 
245
 
bool SecureTunnelSessionClient::WriteContent(
246
 
    SignalingProtocol protocol, const ContentDescription* untyped_content,
247
 
    buzz::XmlElement** elem, WriteError* error) {
248
 
  const SecureTunnelContentDescription* content =
249
 
      static_cast<const SecureTunnelContentDescription*>(untyped_content);
250
 
 
251
 
  buzz::XmlElement* root =
252
 
      new buzz::XmlElement(QN_SECURE_TUNNEL_DESCRIPTION, true);
253
 
  buzz::XmlElement* type_elem = new buzz::XmlElement(QN_SECURE_TUNNEL_TYPE);
254
 
  type_elem->SetBodyText(content->description);
255
 
  root->AddElement(type_elem);
256
 
  if (!content->client_pem_certificate.empty()) {
257
 
    buzz::XmlElement* client_cert_elem =
258
 
        new buzz::XmlElement(QN_SECURE_TUNNEL_CLIENT_CERT);
259
 
    client_cert_elem->SetBodyText(content->client_pem_certificate);
260
 
    root->AddElement(client_cert_elem);
261
 
  }
262
 
  if (!content->server_pem_certificate.empty()) {
263
 
    buzz::XmlElement* server_cert_elem =
264
 
        new buzz::XmlElement(QN_SECURE_TUNNEL_SERVER_CERT);
265
 
    server_cert_elem->SetBodyText(content->server_pem_certificate);
266
 
    root->AddElement(server_cert_elem);
267
 
  }
268
 
  *elem = root;
269
 
  return true;
270
 
}
271
 
 
272
 
SessionDescription* NewSecureTunnelSessionDescription(
273
 
    const std::string& content_name, const ContentDescription* content) {
274
 
  SessionDescription* sdesc = new SessionDescription();
275
 
  sdesc->AddContent(content_name, NS_SECURE_TUNNEL, content);
276
 
  return sdesc;
277
 
}
278
 
 
279
 
SessionDescription* SecureTunnelSessionClient::CreateOffer(
280
 
    const buzz::Jid &jid, const std::string &description) {
281
 
  // We are the initiator so we are the client. Put our cert into the
282
 
  // description.
283
 
  std::string pem_cert = GetIdentity().certificate().ToPEMString();
284
 
  return NewSecureTunnelSessionDescription(
285
 
      CN_SECURE_TUNNEL,
286
 
      new SecureTunnelContentDescription(description, pem_cert, ""));
287
 
}
288
 
 
289
 
SessionDescription* SecureTunnelSessionClient::CreateAnswer(
290
 
    const SessionDescription* offer) {
291
 
  std::string content_name;
292
 
  const SecureTunnelContentDescription* offer_tunnel = NULL;
293
 
  if (!FindSecureTunnelContent(offer, &content_name, &offer_tunnel))
294
 
    return NULL;
295
 
 
296
 
  // We are accepting a session request. We need to add our cert, the
297
 
  // server cert, into the description. The client cert was validated
298
 
  // in OnIncomingTunnel().
299
 
  ASSERT(!offer_tunnel->client_pem_certificate.empty());
300
 
  return NewSecureTunnelSessionDescription(
301
 
      content_name,
302
 
      new SecureTunnelContentDescription(
303
 
          offer_tunnel->description,
304
 
          offer_tunnel->client_pem_certificate,
305
 
          GetIdentity().certificate().ToPEMString()));
306
 
}
307
 
 
308
 
// SecureTunnelSession
309
 
 
310
 
SecureTunnelSession::SecureTunnelSession(
311
 
    SecureTunnelSessionClient* client, Session* session,
312
 
    talk_base::Thread* stream_thread, TunnelSessionRole role)
313
 
    : TunnelSession(client, session, stream_thread),
314
 
      role_(role) {
315
 
}
316
 
 
317
 
talk_base::StreamInterface* SecureTunnelSession::MakeSecureStream(
318
 
    talk_base::StreamInterface* stream) {
319
 
  talk_base::SSLStreamAdapter* ssl_stream =
320
 
      talk_base::SSLStreamAdapter::Create(stream);
321
 
  talk_base::SSLIdentity* identity =
322
 
      static_cast<SecureTunnelSessionClient*>(client_)->
323
 
      GetIdentity().GetReference();
324
 
  ssl_stream->SetIdentity(identity);
325
 
  if (role_ == RESPONDER)
326
 
    ssl_stream->SetServerRole();
327
 
  ssl_stream->StartSSLWithPeer();
328
 
 
329
 
  // SSL negotiation will start on the stream as soon as it
330
 
  // opens. However our SSLStreamAdapter still hasn't been told what
331
 
  // certificate to allow for our peer. If we are the initiator, we do
332
 
  // not have the peer's certificate yet: we will obtain it from the
333
 
  // session accept message which we will receive later (see
334
 
  // OnAccept()). We won't Connect() the PseudoTcpChannel until we get
335
 
  // that, so the stream will stay closed until then.  Keep a handle
336
 
  // on the streem so we can configure the peer certificate later.
337
 
  ssl_stream_reference_.reset(new talk_base::StreamReference(ssl_stream));
338
 
  return ssl_stream_reference_->NewReference();
339
 
}
340
 
 
341
 
talk_base::StreamInterface* SecureTunnelSession::GetStream() {
342
 
  ASSERT(channel_ != NULL);
343
 
  ASSERT(ssl_stream_reference_.get() == NULL);
344
 
  return MakeSecureStream(channel_->GetStream());
345
 
}
346
 
 
347
 
void SecureTunnelSession::OnAccept() {
348
 
  // We have either sent or received a session accept: it's time to
349
 
  // connect the tunnel. First we must set the peer certificate.
350
 
  ASSERT(channel_ != NULL);
351
 
  ASSERT(session_ != NULL);
352
 
  std::string content_name;
353
 
  const SecureTunnelContentDescription* remote_tunnel = NULL;
354
 
  if (!FindSecureTunnelContent(session_->remote_description(),
355
 
                               &content_name, &remote_tunnel)) {
356
 
    session_->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
357
 
    return;
358
 
  }
359
 
 
360
 
  const std::string& cert_pem =
361
 
      role_ == INITIATOR ? remote_tunnel->server_pem_certificate :
362
 
                           remote_tunnel->client_pem_certificate;
363
 
  talk_base::SSLCertificate* peer_cert =
364
 
      ParseCertificate(cert_pem);
365
 
  if (peer_cert == NULL) {
366
 
    ASSERT(role_ == INITIATOR);  // when RESPONDER we validated it earlier
367
 
    LOG(LS_ERROR)
368
 
        << "Rejecting secure tunnel accept with invalid cetificate";
369
 
    session_->Reject(STR_TERMINATE_INCOMPATIBLE_PARAMETERS);
370
 
    return;
371
 
  }
372
 
  ASSERT(ssl_stream_reference_.get() != NULL);
373
 
  talk_base::SSLStreamAdapter* ssl_stream =
374
 
      static_cast<talk_base::SSLStreamAdapter*>(
375
 
          ssl_stream_reference_->GetStream());
376
 
  ssl_stream->SetPeerCertificate(peer_cert);  // pass ownership of certificate.
377
 
  // We no longer need our handle to the ssl stream.
378
 
  ssl_stream_reference_.reset();
379
 
  LOG(LS_INFO) << "Connecting tunnel";
380
 
  // This will try to connect the PseudoTcpChannel. If and when that
381
 
  // succeeds, then ssl negotiation will take place, and when that
382
 
  // succeeds, the tunnel stream will finally open.
383
 
  VERIFY(channel_->Connect(content_name, "tcp"));
384
 
}
385
 
 
386
 
}  // namespace cricket