1 /*
2  *  Copyright 2013 The WebRTC project authors. All Rights Reserved.
3  *
4  *  Use of this source code is governed by a BSD-style license
5  *  that can be found in the LICENSE file in the root of the source
6  *  tree. An additional intellectual property rights grant can be found
7  *  in the file PATENTS.  All contributing project authors may
8  *  be found in the AUTHORS file in the root of the source tree.
9  */
10 
11 #include "pc/webrtc_session_description_factory.h"
12 
13 #include <stddef.h>
14 #include <algorithm>
15 #include <memory>
16 #include <string>
17 #include <type_traits>
18 #include <utility>
19 #include <vector>
20 
21 #include "absl/algorithm/container.h"
22 #include "absl/types/optional.h"
23 #include "api/jsep.h"
24 #include "api/jsep_session_description.h"
25 #include "api/rtc_error.h"
26 #include "pc/sdp_state_provider.h"
27 #include "pc/session_description.h"
28 #include "rtc_base/checks.h"
29 #include "rtc_base/location.h"
30 #include "rtc_base/logging.h"
31 #include "rtc_base/ref_counted_object.h"
32 #include "rtc_base/ssl_identity.h"
33 #include "rtc_base/ssl_stream_adapter.h"
34 #include "rtc_base/string_encode.h"
35 
36 using cricket::MediaSessionOptions;
37 using rtc::UniqueRandomIdGenerator;
38 
39 namespace webrtc {
40 namespace {
41 static const char kFailedDueToIdentityFailed[] =
42     " failed because DTLS identity request failed";
43 static const char kFailedDueToSessionShutdown[] =
44     " failed because the session was shut down";
45 
46 static const uint64_t kInitSessionVersion = 2;
47 
48 // Check that each sender has a unique ID.
ValidMediaSessionOptions(const cricket::MediaSessionOptions & session_options)49 static bool ValidMediaSessionOptions(
50     const cricket::MediaSessionOptions& session_options) {
51   std::vector<cricket::SenderOptions> sorted_senders;
52   for (const cricket::MediaDescriptionOptions& media_description_options :
53        session_options.media_description_options) {
54     sorted_senders.insert(sorted_senders.end(),
55                           media_description_options.sender_options.begin(),
56                           media_description_options.sender_options.end());
57   }
58   absl::c_sort(sorted_senders, [](const cricket::SenderOptions& sender1,
59                                   const cricket::SenderOptions& sender2) {
60     return sender1.track_id < sender2.track_id;
61   });
62   return absl::c_adjacent_find(sorted_senders,
63                                [](const cricket::SenderOptions& sender1,
64                                   const cricket::SenderOptions& sender2) {
65                                  return sender1.track_id == sender2.track_id;
66                                }) == sorted_senders.end();
67 }
68 
69 enum {
70   MSG_CREATE_SESSIONDESCRIPTION_SUCCESS,
71   MSG_CREATE_SESSIONDESCRIPTION_FAILED,
72   MSG_USE_CONSTRUCTOR_CERTIFICATE
73 };
74 
75 struct CreateSessionDescriptionMsg : public rtc::MessageData {
CreateSessionDescriptionMsgwebrtc::__anon392417ff0111::CreateSessionDescriptionMsg76   explicit CreateSessionDescriptionMsg(
77       webrtc::CreateSessionDescriptionObserver* observer,
78       RTCError error_in)
79       : observer(observer), error(std::move(error_in)) {}
80 
81   rtc::scoped_refptr<webrtc::CreateSessionDescriptionObserver> observer;
82   RTCError error;
83   std::unique_ptr<webrtc::SessionDescriptionInterface> description;
84 };
85 }  // namespace
86 
OnFailure()87 void WebRtcCertificateGeneratorCallback::OnFailure() {
88   SignalRequestFailed();
89 }
90 
OnSuccess(const rtc::scoped_refptr<rtc::RTCCertificate> & certificate)91 void WebRtcCertificateGeneratorCallback::OnSuccess(
92     const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
93   SignalCertificateReady(certificate);
94 }
95 
96 // static
CopyCandidatesFromSessionDescription(const SessionDescriptionInterface * source_desc,const std::string & content_name,SessionDescriptionInterface * dest_desc)97 void WebRtcSessionDescriptionFactory::CopyCandidatesFromSessionDescription(
98     const SessionDescriptionInterface* source_desc,
99     const std::string& content_name,
100     SessionDescriptionInterface* dest_desc) {
101   if (!source_desc) {
102     return;
103   }
104   const cricket::ContentInfos& contents =
105       source_desc->description()->contents();
106   const cricket::ContentInfo* cinfo =
107       source_desc->description()->GetContentByName(content_name);
108   if (!cinfo) {
109     return;
110   }
111   size_t mediasection_index = static_cast<int>(cinfo - &contents[0]);
112   const IceCandidateCollection* source_candidates =
113       source_desc->candidates(mediasection_index);
114   const IceCandidateCollection* dest_candidates =
115       dest_desc->candidates(mediasection_index);
116   if (!source_candidates || !dest_candidates) {
117     return;
118   }
119   for (size_t n = 0; n < source_candidates->count(); ++n) {
120     const IceCandidateInterface* new_candidate = source_candidates->at(n);
121     if (!dest_candidates->HasCandidate(new_candidate)) {
122       dest_desc->AddCandidate(source_candidates->at(n));
123     }
124   }
125 }
126 
WebRtcSessionDescriptionFactory(rtc::Thread * signaling_thread,cricket::ChannelManager * channel_manager,const SdpStateProvider * sdp_info,const std::string & session_id,bool dtls_enabled,std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,const rtc::scoped_refptr<rtc::RTCCertificate> & certificate,UniqueRandomIdGenerator * ssrc_generator,std::function<void (const rtc::scoped_refptr<rtc::RTCCertificate> &)> on_certificate_ready)127 WebRtcSessionDescriptionFactory::WebRtcSessionDescriptionFactory(
128     rtc::Thread* signaling_thread,
129     cricket::ChannelManager* channel_manager,
130     const SdpStateProvider* sdp_info,
131     const std::string& session_id,
132     bool dtls_enabled,
133     std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
134     const rtc::scoped_refptr<rtc::RTCCertificate>& certificate,
135     UniqueRandomIdGenerator* ssrc_generator,
136     std::function<void(const rtc::scoped_refptr<rtc::RTCCertificate>&)>
137         on_certificate_ready)
138     : signaling_thread_(signaling_thread),
139       session_desc_factory_(channel_manager,
140                             &transport_desc_factory_,
141                             ssrc_generator),
142       // RFC 4566 suggested a Network Time Protocol (NTP) format timestamp
143       // as the session id and session version. To simplify, it should be fine
144       // to just use a random number as session id and start version from
145       // |kInitSessionVersion|.
146       session_version_(kInitSessionVersion),
147       cert_generator_(dtls_enabled ? std::move(cert_generator) : nullptr),
148       sdp_info_(sdp_info),
149       session_id_(session_id),
150       certificate_request_state_(CERTIFICATE_NOT_NEEDED),
151       on_certificate_ready_(on_certificate_ready) {
152   RTC_DCHECK(signaling_thread_);
153 
154   if (!dtls_enabled) {
155     SetSdesPolicy(cricket::SEC_REQUIRED);
156     RTC_LOG(LS_VERBOSE) << "DTLS-SRTP disabled.";
157     return;
158   }
159 
160   // SRTP-SDES is disabled if DTLS is on.
161   SetSdesPolicy(cricket::SEC_DISABLED);
162   if (certificate) {
163     // Use |certificate|.
164     certificate_request_state_ = CERTIFICATE_WAITING;
165 
166     RTC_LOG(LS_VERBOSE) << "DTLS-SRTP enabled; has certificate parameter.";
167     // We already have a certificate but we wait to do |SetIdentity|; if we do
168     // it in the constructor then the caller has not had a chance to connect to
169     // |SignalCertificateReady|.
170     signaling_thread_->Post(
171         RTC_FROM_HERE, this, MSG_USE_CONSTRUCTOR_CERTIFICATE,
172         new rtc::ScopedRefMessageData<rtc::RTCCertificate>(certificate));
173   } else {
174     // Generate certificate.
175     certificate_request_state_ = CERTIFICATE_WAITING;
176 
177     rtc::scoped_refptr<WebRtcCertificateGeneratorCallback> callback(
178         new rtc::RefCountedObject<WebRtcCertificateGeneratorCallback>());
179     callback->SignalRequestFailed.connect(
180         this, &WebRtcSessionDescriptionFactory::OnCertificateRequestFailed);
181     callback->SignalCertificateReady.connect(
182         this, &WebRtcSessionDescriptionFactory::SetCertificate);
183 
184     rtc::KeyParams key_params = rtc::KeyParams();
185     RTC_LOG(LS_VERBOSE)
186         << "DTLS-SRTP enabled; sending DTLS identity request (key type: "
187         << key_params.type() << ").";
188 
189     // Request certificate. This happens asynchronously, so that the caller gets
190     // a chance to connect to |SignalCertificateReady|.
191     cert_generator_->GenerateCertificateAsync(key_params, absl::nullopt,
192                                               callback);
193   }
194 }
195 
~WebRtcSessionDescriptionFactory()196 WebRtcSessionDescriptionFactory::~WebRtcSessionDescriptionFactory() {
197   RTC_DCHECK_RUN_ON(signaling_thread_);
198 
199   // Fail any requests that were asked for before identity generation completed.
200   FailPendingRequests(kFailedDueToSessionShutdown);
201 
202   // Process all pending notifications in the message queue.  If we don't do
203   // this, requests will linger and not know they succeeded or failed.
204   rtc::MessageList list;
205   signaling_thread_->Clear(this, rtc::MQID_ANY, &list);
206   for (auto& msg : list) {
207     if (msg.message_id != MSG_USE_CONSTRUCTOR_CERTIFICATE) {
208       OnMessage(&msg);
209     } else {
210       // Skip MSG_USE_CONSTRUCTOR_CERTIFICATE because we don't want to trigger
211       // SetIdentity-related callbacks in the destructor. This can be a problem
212       // when WebRtcSession listens to the callback but it was the WebRtcSession
213       // destructor that caused WebRtcSessionDescriptionFactory's destruction.
214       // The callback is then ignored, leaking memory allocated by OnMessage for
215       // MSG_USE_CONSTRUCTOR_CERTIFICATE.
216       delete msg.pdata;
217     }
218   }
219 }
220 
CreateOffer(CreateSessionDescriptionObserver * observer,const PeerConnectionInterface::RTCOfferAnswerOptions & options,const cricket::MediaSessionOptions & session_options)221 void WebRtcSessionDescriptionFactory::CreateOffer(
222     CreateSessionDescriptionObserver* observer,
223     const PeerConnectionInterface::RTCOfferAnswerOptions& options,
224     const cricket::MediaSessionOptions& session_options) {
225   RTC_DCHECK_RUN_ON(signaling_thread_);
226   std::string error = "CreateOffer";
227   if (certificate_request_state_ == CERTIFICATE_FAILED) {
228     error += kFailedDueToIdentityFailed;
229     RTC_LOG(LS_ERROR) << error;
230     PostCreateSessionDescriptionFailed(observer, error);
231     return;
232   }
233 
234   if (!ValidMediaSessionOptions(session_options)) {
235     error += " called with invalid session options";
236     RTC_LOG(LS_ERROR) << error;
237     PostCreateSessionDescriptionFailed(observer, error);
238     return;
239   }
240 
241   CreateSessionDescriptionRequest request(
242       CreateSessionDescriptionRequest::kOffer, observer, session_options);
243   if (certificate_request_state_ == CERTIFICATE_WAITING) {
244     create_session_description_requests_.push(request);
245   } else {
246     RTC_DCHECK(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
247                certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
248     InternalCreateOffer(request);
249   }
250 }
251 
CreateAnswer(CreateSessionDescriptionObserver * observer,const cricket::MediaSessionOptions & session_options)252 void WebRtcSessionDescriptionFactory::CreateAnswer(
253     CreateSessionDescriptionObserver* observer,
254     const cricket::MediaSessionOptions& session_options) {
255   std::string error = "CreateAnswer";
256   if (certificate_request_state_ == CERTIFICATE_FAILED) {
257     error += kFailedDueToIdentityFailed;
258     RTC_LOG(LS_ERROR) << error;
259     PostCreateSessionDescriptionFailed(observer, error);
260     return;
261   }
262   if (!sdp_info_->remote_description()) {
263     error += " can't be called before SetRemoteDescription.";
264     RTC_LOG(LS_ERROR) << error;
265     PostCreateSessionDescriptionFailed(observer, error);
266     return;
267   }
268   if (sdp_info_->remote_description()->GetType() != SdpType::kOffer) {
269     error += " failed because remote_description is not an offer.";
270     RTC_LOG(LS_ERROR) << error;
271     PostCreateSessionDescriptionFailed(observer, error);
272     return;
273   }
274 
275   if (!ValidMediaSessionOptions(session_options)) {
276     error += " called with invalid session options.";
277     RTC_LOG(LS_ERROR) << error;
278     PostCreateSessionDescriptionFailed(observer, error);
279     return;
280   }
281 
282   CreateSessionDescriptionRequest request(
283       CreateSessionDescriptionRequest::kAnswer, observer, session_options);
284   if (certificate_request_state_ == CERTIFICATE_WAITING) {
285     create_session_description_requests_.push(request);
286   } else {
287     RTC_DCHECK(certificate_request_state_ == CERTIFICATE_SUCCEEDED ||
288                certificate_request_state_ == CERTIFICATE_NOT_NEEDED);
289     InternalCreateAnswer(request);
290   }
291 }
292 
SetSdesPolicy(cricket::SecurePolicy secure_policy)293 void WebRtcSessionDescriptionFactory::SetSdesPolicy(
294     cricket::SecurePolicy secure_policy) {
295   session_desc_factory_.set_secure(secure_policy);
296 }
297 
SdesPolicy() const298 cricket::SecurePolicy WebRtcSessionDescriptionFactory::SdesPolicy() const {
299   return session_desc_factory_.secure();
300 }
301 
OnMessage(rtc::Message * msg)302 void WebRtcSessionDescriptionFactory::OnMessage(rtc::Message* msg) {
303   switch (msg->message_id) {
304     case MSG_CREATE_SESSIONDESCRIPTION_SUCCESS: {
305       CreateSessionDescriptionMsg* param =
306           static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
307       param->observer->OnSuccess(param->description.release());
308       delete param;
309       break;
310     }
311     case MSG_CREATE_SESSIONDESCRIPTION_FAILED: {
312       CreateSessionDescriptionMsg* param =
313           static_cast<CreateSessionDescriptionMsg*>(msg->pdata);
314       param->observer->OnFailure(std::move(param->error));
315       delete param;
316       break;
317     }
318     case MSG_USE_CONSTRUCTOR_CERTIFICATE: {
319       rtc::ScopedRefMessageData<rtc::RTCCertificate>* param =
320           static_cast<rtc::ScopedRefMessageData<rtc::RTCCertificate>*>(
321               msg->pdata);
322       RTC_LOG(LS_INFO) << "Using certificate supplied to the constructor.";
323       SetCertificate(param->data());
324       delete param;
325       break;
326     }
327     default:
328       RTC_NOTREACHED();
329       break;
330   }
331 }
332 
InternalCreateOffer(CreateSessionDescriptionRequest request)333 void WebRtcSessionDescriptionFactory::InternalCreateOffer(
334     CreateSessionDescriptionRequest request) {
335   if (sdp_info_->local_description()) {
336     // If the needs-ice-restart flag is set as described by JSEP, we should
337     // generate an offer with a new ufrag/password to trigger an ICE restart.
338     for (cricket::MediaDescriptionOptions& options :
339          request.options.media_description_options) {
340       if (sdp_info_->NeedsIceRestart(options.mid)) {
341         options.transport_options.ice_restart = true;
342       }
343     }
344   }
345 
346   std::unique_ptr<cricket::SessionDescription> desc =
347       session_desc_factory_.CreateOffer(
348           request.options, sdp_info_->local_description()
349                                ? sdp_info_->local_description()->description()
350                                : nullptr);
351   if (!desc) {
352     PostCreateSessionDescriptionFailed(request.observer,
353                                        "Failed to initialize the offer.");
354     return;
355   }
356 
357   // RFC 3264
358   // When issuing an offer that modifies the session,
359   // the "o=" line of the new SDP MUST be identical to that in the
360   // previous SDP, except that the version in the origin field MUST
361   // increment by one from the previous SDP.
362 
363   // Just increase the version number by one each time when a new offer
364   // is created regardless if it's identical to the previous one or not.
365   // The |session_version_| is a uint64_t, the wrap around should not happen.
366   RTC_DCHECK(session_version_ + 1 > session_version_);
367   auto offer = std::make_unique<JsepSessionDescription>(
368       SdpType::kOffer, std::move(desc), session_id_,
369       rtc::ToString(session_version_++));
370   if (sdp_info_->local_description()) {
371     for (const cricket::MediaDescriptionOptions& options :
372          request.options.media_description_options) {
373       if (!options.transport_options.ice_restart) {
374         CopyCandidatesFromSessionDescription(sdp_info_->local_description(),
375                                              options.mid, offer.get());
376       }
377     }
378   }
379   PostCreateSessionDescriptionSucceeded(request.observer, std::move(offer));
380 }
381 
InternalCreateAnswer(CreateSessionDescriptionRequest request)382 void WebRtcSessionDescriptionFactory::InternalCreateAnswer(
383     CreateSessionDescriptionRequest request) {
384   if (sdp_info_->remote_description()) {
385     for (cricket::MediaDescriptionOptions& options :
386          request.options.media_description_options) {
387       // According to http://tools.ietf.org/html/rfc5245#section-9.2.1.1
388       // an answer should also contain new ICE ufrag and password if an offer
389       // has been received with new ufrag and password.
390       options.transport_options.ice_restart =
391           sdp_info_->IceRestartPending(options.mid);
392       // We should pass the current DTLS role to the transport description
393       // factory, if there is already an existing ongoing session.
394       absl::optional<rtc::SSLRole> dtls_role =
395           sdp_info_->GetDtlsRole(options.mid);
396       if (dtls_role) {
397         options.transport_options.prefer_passive_role =
398             (rtc::SSL_SERVER == *dtls_role);
399       }
400     }
401   }
402 
403   std::unique_ptr<cricket::SessionDescription> desc =
404       session_desc_factory_.CreateAnswer(
405           sdp_info_->remote_description()
406               ? sdp_info_->remote_description()->description()
407               : nullptr,
408           request.options,
409           sdp_info_->local_description()
410               ? sdp_info_->local_description()->description()
411               : nullptr);
412   if (!desc) {
413     PostCreateSessionDescriptionFailed(request.observer,
414                                        "Failed to initialize the answer.");
415     return;
416   }
417 
418   // RFC 3264
419   // If the answer is different from the offer in any way (different IP
420   // addresses, ports, etc.), the origin line MUST be different in the answer.
421   // In that case, the version number in the "o=" line of the answer is
422   // unrelated to the version number in the o line of the offer.
423   // Get a new version number by increasing the |session_version_answer_|.
424   // The |session_version_| is a uint64_t, the wrap around should not happen.
425   RTC_DCHECK(session_version_ + 1 > session_version_);
426   auto answer = std::make_unique<JsepSessionDescription>(
427       SdpType::kAnswer, std::move(desc), session_id_,
428       rtc::ToString(session_version_++));
429   if (sdp_info_->local_description()) {
430     // Include all local ICE candidates in the SessionDescription unless
431     // the remote peer has requested an ICE restart.
432     for (const cricket::MediaDescriptionOptions& options :
433          request.options.media_description_options) {
434       if (!options.transport_options.ice_restart) {
435         CopyCandidatesFromSessionDescription(sdp_info_->local_description(),
436                                              options.mid, answer.get());
437       }
438     }
439   }
440   PostCreateSessionDescriptionSucceeded(request.observer, std::move(answer));
441 }
442 
FailPendingRequests(const std::string & reason)443 void WebRtcSessionDescriptionFactory::FailPendingRequests(
444     const std::string& reason) {
445   RTC_DCHECK_RUN_ON(signaling_thread_);
446   while (!create_session_description_requests_.empty()) {
447     const CreateSessionDescriptionRequest& request =
448         create_session_description_requests_.front();
449     PostCreateSessionDescriptionFailed(
450         request.observer,
451         ((request.type == CreateSessionDescriptionRequest::kOffer)
452              ? "CreateOffer"
453              : "CreateAnswer") +
454             reason);
455     create_session_description_requests_.pop();
456   }
457 }
458 
PostCreateSessionDescriptionFailed(CreateSessionDescriptionObserver * observer,const std::string & error)459 void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionFailed(
460     CreateSessionDescriptionObserver* observer,
461     const std::string& error) {
462   CreateSessionDescriptionMsg* msg = new CreateSessionDescriptionMsg(
463       observer, RTCError(RTCErrorType::INTERNAL_ERROR, std::string(error)));
464   signaling_thread_->Post(RTC_FROM_HERE, this,
465                           MSG_CREATE_SESSIONDESCRIPTION_FAILED, msg);
466   RTC_LOG(LS_ERROR) << "Create SDP failed: " << error;
467 }
468 
PostCreateSessionDescriptionSucceeded(CreateSessionDescriptionObserver * observer,std::unique_ptr<SessionDescriptionInterface> description)469 void WebRtcSessionDescriptionFactory::PostCreateSessionDescriptionSucceeded(
470     CreateSessionDescriptionObserver* observer,
471     std::unique_ptr<SessionDescriptionInterface> description) {
472   CreateSessionDescriptionMsg* msg =
473       new CreateSessionDescriptionMsg(observer, RTCError::OK());
474   msg->description = std::move(description);
475   signaling_thread_->Post(RTC_FROM_HERE, this,
476                           MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, msg);
477 }
478 
OnCertificateRequestFailed()479 void WebRtcSessionDescriptionFactory::OnCertificateRequestFailed() {
480   RTC_DCHECK_RUN_ON(signaling_thread_);
481 
482   RTC_LOG(LS_ERROR) << "Asynchronous certificate generation request failed.";
483   certificate_request_state_ = CERTIFICATE_FAILED;
484 
485   FailPendingRequests(kFailedDueToIdentityFailed);
486 }
487 
SetCertificate(const rtc::scoped_refptr<rtc::RTCCertificate> & certificate)488 void WebRtcSessionDescriptionFactory::SetCertificate(
489     const rtc::scoped_refptr<rtc::RTCCertificate>& certificate) {
490   RTC_DCHECK(certificate);
491   RTC_LOG(LS_VERBOSE) << "Setting new certificate.";
492 
493   certificate_request_state_ = CERTIFICATE_SUCCEEDED;
494 
495   on_certificate_ready_(certificate);
496 
497   transport_desc_factory_.set_certificate(certificate);
498   transport_desc_factory_.set_secure(cricket::SEC_ENABLED);
499 
500   while (!create_session_description_requests_.empty()) {
501     if (create_session_description_requests_.front().type ==
502         CreateSessionDescriptionRequest::kOffer) {
503       InternalCreateOffer(create_session_description_requests_.front());
504     } else {
505       InternalCreateAnswer(create_session_description_requests_.front());
506     }
507     create_session_description_requests_.pop();
508   }
509 }
510 }  // namespace webrtc
511