Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(596)

Side by Side Diff: webrtc/libjingle/session/tunnel/securetunnelsessionclient.cc

Issue 1175243003: Remove webrtc/libjingle/{examples,session}. (Closed) Base URL: https://chromium.googlesource.com/external/webrtc.git@master
Patch Set: Created 5 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
OLDNEW
(Empty)
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 "webrtc/libjingle/session/tunnel/pseudotcpchannel.h"
32 #include "webrtc/libjingle/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
OLDNEW
« no previous file with comments | « webrtc/libjingle/session/tunnel/securetunnelsessionclient.h ('k') | webrtc/libjingle/session/tunnel/tunnelsessionclient.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698