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 39 using cricket::MediaSessionOptions; 40 41 namespace webrtc { 42 namespace { 43 static const char kFailedDueToIdentityFailed[] = 44 " failed because DTLS identity request failed"; 45 static const char kFailedDueToSessionShutdown[] = 46 " failed because the session was shut down"; 47 48 static const uint64_t kInitSessionVersion = 2; 49 50 static bool CompareStream(const MediaSessionOptions::Stream& stream1, 51 const MediaSessionOptions::Stream& stream2) { 52 return stream1.id < stream2.id; 53 } 54 55 static 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. 61 static 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 70 enum { 71 MSG_CREATE_SESSIONDESCRIPTION_SUCCESS, 72 MSG_CREATE_SESSIONDESCRIPTION_FAILED, 73 MSG_USE_CONSTRUCTOR_CERTIFICATE 74 }; 75 76 struct 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 88 void WebRtcIdentityRequestObserver::OnFailure(int error) { 89 SignalRequestFailed(error); 90 } 91 92 void 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 107 void WebRtcIdentityRequestObserver::OnSuccess( 108 rtc::scoped_ptr<rtc::SSLIdentity> identity) { 109 SignalCertificateReady(rtc::RTCCertificate::Create(std::move(identity))); 110 } 111 112 // static 113 void 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. 132 WebRtcSessionDescriptionFactory::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 158 WebRtcSessionDescriptionFactory::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 173 WebRtcSessionDescriptionFactory::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 205 WebRtcSessionDescriptionFactory::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 231 WebRtcSessionDescriptionFactory::~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 256 void 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 286 void 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 329 void WebRtcSessionDescriptionFactory::SetSdesPolicy( 330 cricket::SecurePolicy secure_policy) { 331 session_desc_factory_.set_secure(secure_policy); 332 } 333 334 cricket::SecurePolicy WebRtcSessionDescriptionFactory::SdesPolicy() const { 335 return session_desc_factory_.secure(); 336 } 337 338 void 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 369 void 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 405 void 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 468 void 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 481 void 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 489 void 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 497 void 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 506 void 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