Update To 11.40.268.0
[platform/framework/web/crosswalk.git] / src / third_party / libjingle / source / talk / session / tunnel / securetunnelsessionclient.cc
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 "webrtc/p2p/base/transportchannel.h"
31 #include "talk/session/tunnel/pseudotcpchannel.h"
32 #include "talk/session/tunnel/securetunnelsessionclient.h"
33 #include "webrtc/libjingle/xmllite/xmlelement.h"
34 #include "webrtc/base/basicdefs.h"
35 #include "webrtc/base/basictypes.h"
36 #include "webrtc/base/common.h"
37 #include "webrtc/base/helpers.h"
38 #include "webrtc/base/logging.h"
39 #include "webrtc/base/sslidentity.h"
40 #include "webrtc/base/sslstreamadapter.h"
41 #include "webrtc/base/stringutils.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(rtc::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(rtc::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 rtc::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 rtc::SSLCertificate* ParseCertificate(
124     const std::string& pem_cert) {
125   if (pem_cert.empty())
126     return NULL;
127   return rtc::SSLCertificate::FromPEMString(pem_cert);
128 }
129
130 TunnelSession* SecureTunnelSessionClient::MakeTunnelSession(
131     Session* session, rtc::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   rtc::scoped_ptr<rtc::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                                              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, 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     rtc::Thread* stream_thread, TunnelSessionRole role)
313     : TunnelSession(client, session, stream_thread),
314       role_(role) {
315 }
316
317 rtc::StreamInterface* SecureTunnelSession::MakeSecureStream(
318     rtc::StreamInterface* stream) {
319   rtc::SSLStreamAdapter* ssl_stream =
320       rtc::SSLStreamAdapter::Create(stream);
321   rtc::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 rtc::StreamReference(ssl_stream));
338   return ssl_stream_reference_->NewReference();
339 }
340
341 rtc::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   rtc::scoped_ptr<rtc::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   rtc::SSLStreamAdapter* ssl_stream =
374       static_cast<rtc::SSLStreamAdapter*>(
375           ssl_stream_reference_->GetStream());
376
377   std::string algorithm;
378   if (!peer_cert->GetSignatureDigestAlgorithm(&algorithm)) {
379     LOG(LS_ERROR) << "Failed to get the algorithm for the peer cert signature";
380     return;
381   }
382   unsigned char digest[rtc::MessageDigest::kMaxSize];
383   size_t digest_len;
384   peer_cert->ComputeDigest(algorithm, digest, ARRAY_SIZE(digest), &digest_len);
385   ssl_stream->SetPeerCertificateDigest(algorithm, digest, digest_len);
386
387   // We no longer need our handle to the ssl stream.
388   ssl_stream_reference_.reset();
389   LOG(LS_INFO) << "Connecting tunnel";
390   // This will try to connect the PseudoTcpChannel. If and when that
391   // succeeds, then ssl negotiation will take place, and when that
392   // succeeds, the tunnel stream will finally open.
393   VERIFY(channel_->Connect(
394       content_name, "tcp", ICE_CANDIDATE_COMPONENT_DEFAULT));
395 }
396
397 }  // namespace cricket