1/*
2 * libjingle
3 * Copyright 2013 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#include "talk/app/webrtc/webrtcsessiondescriptionfactory.h"
29
30#include <utility>
31
32#include "talk/app/webrtc/dtlsidentitystore.h"
33#include "talk/app/webrtc/jsep.h"
34#include "talk/app/webrtc/jsepsessiondescription.h"
35#include "talk/app/webrtc/mediaconstraintsinterface.h"
36#include "talk/app/webrtc/webrtcsession.h"
37#include "webrtc/base/sslidentity.h"
38
39using cricket::MediaSessionOptions;
40
41namespace webrtc {
42namespace {
43static const char kFailedDueToIdentityFailed[] =
44    " failed because DTLS identity request failed";
45static const char kFailedDueToSessionShutdown[] =
46    " failed because the session was shut down";
47
48static const uint64_t kInitSessionVersion = 2;
49
50static bool CompareStream(const MediaSessionOptions::Stream& stream1,
51                          const MediaSessionOptions::Stream& stream2) {
52  return stream1.id < stream2.id;
53}
54
55static bool SameId(const MediaSessionOptions::Stream& stream1,
56                   const MediaSessionOptions::Stream& stream2) {
57  return stream1.id == stream2.id;
58}
59
60// Checks if each Stream within the |streams| has unique id.
61static bool ValidStreams(const MediaSessionOptions::Streams& streams) {
62  MediaSessionOptions::Streams sorted_streams = streams;
63  std::sort(sorted_streams.begin(), sorted_streams.end(), CompareStream);
64  MediaSessionOptions::Streams::iterator it =
65      std::adjacent_find(sorted_streams.begin(), sorted_streams.end(),
66                         SameId);
67  return it == sorted_streams.end();
68}
69
70enum {
71  MSG_CREATE_SESSIONDESCRIPTION_SUCCESS,
72  MSG_CREATE_SESSIONDESCRIPTION_FAILED,
73  MSG_USE_CONSTRUCTOR_CERTIFICATE
74};
75
76struct CreateSessionDescriptionMsg : public rtc::MessageData {
77  explicit CreateSessionDescriptionMsg(
78      webrtc::CreateSessionDescriptionObserver* observer)
79      : observer(observer) {
80  }
81
82  rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserver> observer;
83  std::string error;
84  rtc::scoped_ptr<webrtc::SessionDescriptionInterface> description;
85};
86}  // namespace
87
88void WebRtcIdentityRequestObserver::OnFailure(int error) {
89  SignalRequestFailed(error);
90}
91
92void WebRtcIdentityRequestObserver::OnSuccess(
93    const std::string& der_cert, const std::string& der_private_key) {
94  std::string pem_cert = rtc::SSLIdentity::DerToPem(
95      rtc::kPemTypeCertificate,
96      reinterpret_cast<const unsigned char*>(der_cert.data()),
97      der_cert.length());
98  std::string pem_key = rtc::SSLIdentity::DerToPem(
99      rtc::kPemTypeRsaPrivateKey,
100      reinterpret_cast<const unsigned char*>(der_private_key.data()),
101      der_private_key.length());
102  rtc::scoped_ptr<rtc::SSLIdentity> identity(
103      rtc::SSLIdentity::FromPEMStrings(pem_key, pem_cert));
104  SignalCertificateReady(rtc::RTCCertificate::Create(std::move(identity)));
105}
106
107void WebRtcIdentityRequestObserver::OnSuccess(
108    rtc::scoped_ptr<rtc::SSLIdentity> identity) {
109  SignalCertificateReady(rtc::RTCCertificate::Create(std::move(identity)));
110}
111
112// static
113void WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
114    const SessionDescriptionInterface* source_desc,
115    SessionDescriptionInterface* dest_desc) {
116  if (!source_desc)
117    return;
118  for (size_t m = 0; m < source_desc->number_of_mediasections() &&
119                     m < dest_desc->number_of_mediasections(); ++m) {
120    const IceCandidateCollection* source_candidates =
121        source_desc->candidates(m);
122    const IceCandidateCollection* dest_candidates = dest_desc->candidates(m);
123    for  (size_t n = 0; n < source_candidates->count(); ++n) {
124      const IceCandidateInterface* new_candidate = source_candidates->at(n);
125      if (!dest_candidates->HasCandidate(new_candidate))
126        dest_desc->AddCandidate(source_candidates->at(n));
127    }
128  }
129}
130
131// Private constructor called by other constructors.
132WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
133    rtc::Thread* signaling_thread,
134    cricket::ChannelManager* channel_manager,
135    rtc::scoped_ptr<DtlsIdentityStoreInterface> dtls_identity_store,
136    const rtc::scoped_refptr<WebRtcIdentityRequestObserver>&
137        identity_request_observer,
138    WebRtcSession* session,
139    const std::string& session_id,
140    bool dtls_enabled)
141    : signaling_thread_(signaling_thread),
142      session_desc_factory_(channel_manager, &transport_desc_factory_),
143      // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
144      // as the session id and session version. To simplify, it should be fine
145      // to just use a random number as session id and start version from
146      // |kInitSessionVersion|.
147      session_version_(kInitSessionVersion),
148      dtls_identity_store_(std::move(dtls_identity_store)),
149      identity_request_observer_(identity_request_observer),
150      session_(session),
151      session_id_(session_id),
152      certificate_request_state_(CERTIFICATE_NOT_NEEDED) {
153  session_desc_factory_.set_add_legacy_streams(false);
154  // SRTP-SDES is disabled if DTLS is on.
155  SetSdesPolicy(dtls_enabled ? cricket::SEC_DISABLED : cricket::SEC_REQUIRED);
156}
157
158WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
159    rtc::Thread* signaling_thread,
160    cricket::ChannelManager* channel_manager,
161    WebRtcSession* session,
162    const std::string& session_id)
163    : WebRtcSessionDescriptionFactory(signaling_thread,
164                                      channel_manager,
165                                      nullptr,
166                                      nullptr,
167                                      session,
168                                      session_id,
169                                      false) {
170  LOG(LS_VERBOSE) << "DTLS-SRTP disabled.";
171}
172
173WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
174    rtc::Thread* signaling_thread,
175    cricket::ChannelManager* channel_manager,
176    rtc::scoped_ptr<DtlsIdentityStoreInterface> dtls_identity_store,
177    WebRtcSession* session,
178    const std::string& session_id)
179    : WebRtcSessionDescriptionFactory(
180          signaling_thread,
181          channel_manager,
182          std::move(dtls_identity_store),
183          new rtc::RefCountedObject<WebRtcIdentityRequestObserver>(),
184          session,
185          session_id,
186          true) {
187  RTC_DCHECK(dtls_identity_store_);
188
189  certificate_request_state_ = CERTIFICATE_WAITING;
190
191  identity_request_observer_->SignalRequestFailed.connect(
192      this, &WebRtcSessionDescriptionFactory::OnIdentityRequestFailed);
193  identity_request_observer_->SignalCertificateReady.connect(
194      this, &WebRtcSessionDescriptionFactory::SetCertificate);
195
196  rtc::KeyType key_type = rtc::KT_DEFAULT;
197  LOG(LS_VERBOSE) << "DTLS-SRTP enabled; sending DTLS identity request (key "
198                  << "type: " << key_type << ").";
199
200  // Request identity. This happens asynchronously, so the caller will have a
201  // chance to connect to SignalIdentityReady.
202  dtls_identity_store_->RequestIdentity(key_type, identity_request_observer_);
203}
204
205WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
206    rtc::Thread* signaling_thread,
207    cricket::ChannelManager* channel_manager,
208    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate,
209    WebRtcSession* session,
210    const std::string& session_id)
211    : WebRtcSessionDescriptionFactory(signaling_thread,
212                                      channel_manager,
213                                      nullptr,
214                                      nullptr,
215                                      session,
216                                      session_id,
217                                      true) {
218  RTC_DCHECK(certificate);
219
220  certificate_request_state_ = CERTIFICATE_WAITING;
221
222  LOG(LS_VERBOSE) << "DTLS-SRTP enabled; has certificate parameter.";
223  // We already have a certificate but we wait to do SetIdentity; if we do
224  // it in the constructor then the caller has not had a chance to connect to
225  // SignalIdentityReady.
226  signaling_thread_->Post(
227      this, MSG_USE_CONSTRUCTOR_CERTIFICATE,
228      new rtc::ScopedRefMessageData<rtc::RTCCertificate>(certificate));
229}
230
231WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() {
232  ASSERT(signaling_thread_->IsCurrent());
233
234  // Fail any requests that were asked for before identity generation completed.
235  FailPendingRequests(kFailedDueToSessionShutdown);
236
237  // Process all pending notifications in the message queue.  If we don't do
238  // this, requests will linger and not know they succeeded or failed.
239  rtc::MessageList list;
240  signaling_thread_->Clear(this, rtc::MQID_ANY, &list);
241  for (auto& msg : list) {
242    if (msg.message_id != MSG_USE_CONSTRUCTOR_CERTIFICATE) {
243      OnMessage(&msg);
244    } else {
245      // Skip MSG_USE_CONSTRUCTOR_CERTIFICATE because we don't want to trigger
246      // SetIdentity-related callbacks in the destructor. This can be a problem
247      // when WebRtcSession listens to the callback but it was the WebRtcSession
248      // destructor that caused WebRtcSessionDescriptionFactory's destruction.
249      // The callback is then ignored, leaking memory allocated by OnMessage for
250      // MSG_USE_CONSTRUCTOR_CERTIFICATE.
251      delete msg.pdata;
252    }
253  }
254}
255
256void WebRtcSessionDescriptionFactory::CreateOffer(
257    CreateSessionDescriptionObserver* observer,
258    const PeerConnectionInterface::RTCOfferAnswerOptions& options,
259    const cricket::MediaSessionOptions& session_options) {
260  std::string error = "CreateOffer";
261  if (certificate_request_state_ == CERTIFICATE_FAILED) {
262    error += kFailedDueToIdentityFailed;
263    LOG(LS_ERROR) << error;
264    PostCreateSessionDescriptionFailed(observer, error);
265    return;
266  }
267
268  if (!ValidStreams(session_options.streams)) {
269    error += " called with invalid media streams.";
270    LOG(LS_ERROR) << error;
271    PostCreateSessionDescriptionFailed(observer, error);
272    return;
273  }
274
275  CreateSessionDescriptionRequest request(
276      CreateSessionDescriptionRequest::kOffer, observer, session_options);
277  if (certificate_request_state_ == CERTIFICATE_WAITING) {
278    create_session_description_requests_.push(request);
279  } else {
280    ASSERT(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
281           certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
282    InternalCreateOffer(request);
283  }
284}
285
286void WebRtcSessionDescriptionFactory::CreateAnswer(
287    CreateSessionDescriptionObserver* observer,
288    const MediaConstraintsInterface* constraints,
289    const cricket::MediaSessionOptions& session_options) {
290  std::string error = "CreateAnswer";
291  if (certificate_request_state_ == CERTIFICATE_FAILED) {
292    error += kFailedDueToIdentityFailed;
293    LOG(LS_ERROR) << error;
294    PostCreateSessionDescriptionFailed(observer, error);
295    return;
296  }
297  if (!session_->remote_description()) {
298    error += " can't be called before SetRemoteDescription.";
299    LOG(LS_ERROR) << error;
300    PostCreateSessionDescriptionFailed(observer, error);
301    return;
302  }
303  if (session_->remote_description()->type() !=
304      JsepSessionDescription::kOffer) {
305    error += " failed because remote_description is not an offer.";
306    LOG(LS_ERROR) << error;
307    PostCreateSessionDescriptionFailed(observer, error);
308    return;
309  }
310
311  if (!ValidStreams(session_options.streams)) {
312    error += " called with invalid media streams.";
313    LOG(LS_ERROR) << error;
314    PostCreateSessionDescriptionFailed(observer, error);
315    return;
316  }
317
318  CreateSessionDescriptionRequest request(
319      CreateSessionDescriptionRequest::kAnswer, observer, session_options);
320  if (certificate_request_state_ == CERTIFICATE_WAITING) {
321    create_session_description_requests_.push(request);
322  } else {
323    ASSERT(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
324           certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
325    InternalCreateAnswer(request);
326  }
327}
328
329void WebRtcSessionDescriptionFactory::SetSdesPolicy(
330    cricket::SecurePolicy secure_policy) {
331  session_desc_factory_.set_secure(secure_policy);
332}
333
334cricket::SecurePolicy WebRtcSessionDescriptionFactory::SdesPolicy() const {
335  return session_desc_factory_.secure();
336}
337
338void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) {
339  switch (msg->message_id) {
340    case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
341      CreateSessionDescriptionMsg* param =
342          static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
343      param->observer->OnSuccess(param->description.release());
344      delete param;
345      break;
346    }
347    case MSG_CREATE_SESSIONDESCRIPTION_FAILED: {
348      CreateSessionDescriptionMsg* param =
349          static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
350      param->observer->OnFailure(param->error);
351      delete param;
352      break;
353    }
354    case MSG_USE_CONSTRUCTOR_CERTIFICATE: {
355      rtc::ScopedRefMessageData<rtc::RTCCertificate>* param =
356          static_cast<rtc::ScopedRefMessageData<rtc::RTCCertificate>*>(
357              msg->pdata);
358      LOG(LS_INFO) << "Using certificate supplied to the constructor.";
359      SetCertificate(param->data());
360      delete param;
361      break;
362    }
363    default:
364      ASSERT(false);
365      break;
366  }
367}
368
369void WebRtcSessionDescriptionFactory::InternalCreateOffer(
370    CreateSessionDescriptionRequest request) {
371  cricket::SessionDescription* desc(session_desc_factory_.CreateOffer(
372      request.options, session_->local_description()
373                           ? session_->local_description()->description()
374                           : nullptr));
375  // RFC 3264
376  // When issuing an offer that modifies the session,
377  // the "o=" line of the new SDP MUST be identical to that in the
378  // previous SDP, except that the version in the origin field MUST
379  // increment by one from the previous SDP.
380
381  // Just increase the version number by one each time when a new offer
382  // is created regardless if it's identical to the previous one or not.
383  // The |session_version_| is a uint64_t, the wrap around should not happen.
384  ASSERT(session_version_ + 1 > session_version_);
385  JsepSessionDescription* offer(new JsepSessionDescription(
386      JsepSessionDescription::kOffer));
387  if (!offer->Initialize(desc, session_id_,
388                         rtc::ToString(session_version_++))) {
389    delete offer;
390    PostCreateSessionDescriptionFailed(request.observer,
391                                       "Failed to initialize the offer.");
392    return;
393  }
394  if (session_->local_description() &&
395      !request.options.audio_transport_options.ice_restart &&
396      !request.options.video_transport_options.ice_restart &&
397      !request.options.data_transport_options.ice_restart) {
398    // Include all local ice candidates in the SessionDescription unless
399    // the an ice restart has been requested.
400    CopyCandidatesFromSessionDescription(session_->local_description(), offer);
401  }
402  PostCreateSessionDescriptionSucceeded(request.observer, offer);
403}
404
405void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
406    CreateSessionDescriptionRequest request) {
407  // According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
408  // an answer should also contain new ice ufrag and password if an offer has
409  // been received with new ufrag and password.
410  request.options.audio_transport_options.ice_restart =
411      session_->IceRestartPending();
412  request.options.video_transport_options.ice_restart =
413      session_->IceRestartPending();
414  request.options.data_transport_options.ice_restart =
415      session_->IceRestartPending();
416  // We should pass current ssl role to the transport description factory, if
417  // there is already an existing ongoing session.
418  rtc::SSLRole ssl_role;
419  if (session_->GetSslRole(session_->voice_channel(), &ssl_role)) {
420    request.options.audio_transport_options.prefer_passive_role =
421        (rtc::SSL_SERVER == ssl_role);
422  }
423  if (session_->GetSslRole(session_->video_channel(), &ssl_role)) {
424    request.options.video_transport_options.prefer_passive_role =
425        (rtc::SSL_SERVER == ssl_role);
426  }
427  if (session_->GetSslRole(session_->data_channel(), &ssl_role)) {
428    request.options.data_transport_options.prefer_passive_role =
429        (rtc::SSL_SERVER == ssl_role);
430  }
431
432  cricket::SessionDescription* desc(session_desc_factory_.CreateAnswer(
433      session_->remote_description()
434          ? session_->remote_description()->description()
435          : nullptr,
436      request.options, session_->local_description()
437                           ? session_->local_description()->description()
438                           : nullptr));
439  // RFC 3264
440  // If the answer is different from the offer in any way (different IP
441  // addresses, ports, etc.), the origin line MUST be different in the answer.
442  // In that case, the version number in the "o=" line of the answer is
443  // unrelated to the version number in the o line of the offer.
444  // Get a new version number by increasing the |session_version_answer_|.
445  // The |session_version_| is a uint64_t, the wrap around should not happen.
446  ASSERT(session_version_ + 1 > session_version_);
447  JsepSessionDescription* answer(new JsepSessionDescription(
448      JsepSessionDescription::kAnswer));
449  if (!answer->Initialize(desc, session_id_,
450                          rtc::ToString(session_version_++))) {
451    delete answer;
452    PostCreateSessionDescriptionFailed(request.observer,
453                                       "Failed to initialize the answer.");
454    return;
455  }
456  if (session_->local_description() &&
457      !request.options.audio_transport_options.ice_restart &&
458      !request.options.video_transport_options.ice_restart &&
459      !request.options.data_transport_options.ice_restart) {
460    // Include all local ice candidates in the SessionDescription unless
461    // the remote peer has requested an ice restart.
462    CopyCandidatesFromSessionDescription(session_->local_description(), answer);
463  }
464  session_->ResetIceRestartLatch();
465  PostCreateSessionDescriptionSucceeded(request.observer, answer);
466}
467
468void WebRtcSessionDescriptionFactory::FailPendingRequests(
469    const std::string& reason) {
470  ASSERT(signaling_thread_->IsCurrent());
471  while (!create_session_description_requests_.empty()) {
472    const CreateSessionDescriptionRequest& request =
473        create_session_description_requests_.front();
474    PostCreateSessionDescriptionFailed(request.observer,
475        ((request.type == CreateSessionDescriptionRequest::kOffer) ?
476            "CreateOffer" : "CreateAnswer") + reason);
477    create_session_description_requests_.pop();
478  }
479}
480
481void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed(
482    CreateSessionDescriptionObserver* observer, const std::string& error) {
483  CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
484  msg->error = error;
485  signaling_thread_->Post(this, MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
486  LOG(LS_ERROR) << "Create SDP failed: " << error;
487}
488
489void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded(
490    CreateSessionDescriptionObserver* observer,
491    SessionDescriptionInterface* description) {
492  CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(observer);
493  msg->description.reset(description);
494  signaling_thread_->Post(this, MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
495}
496
497void WebRtcSessionDescriptionFactory::OnIdentityRequestFailed(int error) {
498  ASSERT(signaling_thread_->IsCurrent());
499
500  LOG(LS_ERROR) << "Async identity request failed: error = " << error;
501  certificate_request_state_ = CERTIFICATE_FAILED;
502
503  FailPendingRequests(kFailedDueToIdentityFailed);
504}
505
506void WebRtcSessionDescriptionFactory::SetCertificate(
507    const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
508  RTC_DCHECK(certificate);
509  LOG(LS_VERBOSE) << "Setting new certificate";
510
511  certificate_request_state_ = CERTIFICATE_SUCCEEDED;
512  SignalCertificateReady(certificate);
513
514  transport_desc_factory_.set_certificate(certificate);
515  transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
516
517  while (!create_session_description_requests_.empty()) {
518    if (create_session_description_requests_.front().type ==
519        CreateSessionDescriptionRequest::kOffer) {
520      InternalCreateOffer(create_session_description_requests_.front());
521    } else {
522      InternalCreateAnswer(create_session_description_requests_.front());
523    }
524    create_session_description_requests_.pop();
525  }
526}
527}  // namespace webrtc
528