1/* 2 * libjingle 3 * Copyright 2015 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/dtlsidentitystore.h" 29 30#include <utility> 31 32#include "talk/app/webrtc/webrtcsessiondescriptionfactory.h" 33#include "webrtc/base/logging.h" 34 35using webrtc::DtlsIdentityRequestObserver; 36 37namespace webrtc { 38 39// Passed to SSLIdentity::Generate, "WebRTC". Used for the certificates' 40// subject and issuer name. 41const char kIdentityName[] = "WebRTC"; 42 43namespace { 44 45enum { 46 MSG_DESTROY, 47 MSG_GENERATE_IDENTITY, 48 MSG_GENERATE_IDENTITY_RESULT 49}; 50 51} // namespace 52 53// This class runs on the worker thread to generate the identity. It's necessary 54// to separate this class from DtlsIdentityStore so that it can live on the 55// worker thread after DtlsIdentityStore is destroyed. 56class DtlsIdentityStoreImpl::WorkerTask : public sigslot::has_slots<>, 57 public rtc::MessageHandler { 58 public: 59 WorkerTask(DtlsIdentityStoreImpl* store, rtc::KeyType key_type) 60 : signaling_thread_(rtc::Thread::Current()), 61 store_(store), 62 key_type_(key_type) { 63 store_->SignalDestroyed.connect(this, &WorkerTask::OnStoreDestroyed); 64 } 65 66 virtual ~WorkerTask() { RTC_DCHECK(signaling_thread_->IsCurrent()); } 67 68 private: 69 void GenerateIdentity_w() { 70 LOG(LS_INFO) << "Generating identity, using keytype " << key_type_; 71 rtc::scoped_ptr<rtc::SSLIdentity> identity( 72 rtc::SSLIdentity::Generate(kIdentityName, key_type_)); 73 74 // Posting to |this| avoids touching |store_| on threads other than 75 // |signaling_thread_| and thus avoids having to use locks. 76 IdentityResultMessageData* msg = new IdentityResultMessageData( 77 new IdentityResult(key_type_, std::move(identity))); 78 signaling_thread_->Post(this, MSG_GENERATE_IDENTITY_RESULT, msg); 79 } 80 81 void OnMessage(rtc::Message* msg) override { 82 switch (msg->message_id) { 83 case MSG_GENERATE_IDENTITY: 84 // This message always runs on the worker thread. 85 GenerateIdentity_w(); 86 87 // Must delete |this|, owned by msg->pdata, on the signaling thread to 88 // avoid races on disconnecting the signal. 89 signaling_thread_->Post(this, MSG_DESTROY, msg->pdata); 90 break; 91 case MSG_GENERATE_IDENTITY_RESULT: 92 RTC_DCHECK(signaling_thread_->IsCurrent()); 93 { 94 rtc::scoped_ptr<IdentityResultMessageData> pdata( 95 static_cast<IdentityResultMessageData*>(msg->pdata)); 96 if (store_) { 97 store_->OnIdentityGenerated(pdata->data()->key_type_, 98 std::move(pdata->data()->identity_)); 99 } 100 } 101 break; 102 case MSG_DESTROY: 103 RTC_DCHECK(signaling_thread_->IsCurrent()); 104 delete msg->pdata; 105 // |this| has now been deleted. Don't touch member variables. 106 break; 107 default: 108 RTC_CHECK(false) << "Unexpected message type"; 109 } 110 } 111 112 void OnStoreDestroyed() { 113 RTC_DCHECK(signaling_thread_->IsCurrent()); 114 store_ = nullptr; 115 } 116 117 rtc::Thread* const signaling_thread_; 118 DtlsIdentityStoreImpl* store_; // Only touched on |signaling_thread_|. 119 const rtc::KeyType key_type_; 120}; 121 122DtlsIdentityStoreImpl::DtlsIdentityStoreImpl(rtc::Thread* signaling_thread, 123 rtc::Thread* worker_thread) 124 : signaling_thread_(signaling_thread), 125 worker_thread_(worker_thread), 126 request_info_() { 127 RTC_DCHECK(signaling_thread_->IsCurrent()); 128 // Preemptively generate identities unless the worker thread and signaling 129 // thread are the same (only do preemptive work in the background). 130 if (worker_thread_ != signaling_thread_) { 131 // Only necessary for RSA. 132 GenerateIdentity(rtc::KT_RSA, nullptr); 133 } 134} 135 136DtlsIdentityStoreImpl::~DtlsIdentityStoreImpl() { 137 RTC_DCHECK(signaling_thread_->IsCurrent()); 138 SignalDestroyed(); 139} 140 141void DtlsIdentityStoreImpl::RequestIdentity( 142 rtc::KeyType key_type, 143 const rtc::scoped_refptr<webrtc::DtlsIdentityRequestObserver>& observer) { 144 RTC_DCHECK(signaling_thread_->IsCurrent()); 145 RTC_DCHECK(observer); 146 147 GenerateIdentity(key_type, observer); 148} 149 150void DtlsIdentityStoreImpl::OnMessage(rtc::Message* msg) { 151 RTC_DCHECK(signaling_thread_->IsCurrent()); 152 switch (msg->message_id) { 153 case MSG_GENERATE_IDENTITY_RESULT: { 154 rtc::scoped_ptr<IdentityResultMessageData> pdata( 155 static_cast<IdentityResultMessageData*>(msg->pdata)); 156 OnIdentityGenerated(pdata->data()->key_type_, 157 std::move(pdata->data()->identity_)); 158 break; 159 } 160 } 161} 162 163bool DtlsIdentityStoreImpl::HasFreeIdentityForTesting( 164 rtc::KeyType key_type) const { 165 RTC_DCHECK(signaling_thread_->IsCurrent()); 166 return request_info_[key_type].free_identity_.get() != nullptr; 167} 168 169void DtlsIdentityStoreImpl::GenerateIdentity( 170 rtc::KeyType key_type, 171 const rtc::scoped_refptr<webrtc::DtlsIdentityRequestObserver>& observer) { 172 RTC_DCHECK(signaling_thread_->IsCurrent()); 173 174 // Enqueue observer to be informed when generation of |key_type| is completed. 175 if (observer.get()) { 176 request_info_[key_type].request_observers_.push(observer); 177 178 // Already have a free identity generated? 179 if (request_info_[key_type].free_identity_.get()) { 180 // Return identity async - post even though we are on |signaling_thread_|. 181 LOG(LS_VERBOSE) << "Using a free DTLS identity."; 182 ++request_info_[key_type].gen_in_progress_counts_; 183 IdentityResultMessageData* msg = 184 new IdentityResultMessageData(new IdentityResult( 185 key_type, std::move(request_info_[key_type].free_identity_))); 186 signaling_thread_->Post(this, MSG_GENERATE_IDENTITY_RESULT, msg); 187 return; 188 } 189 190 // Free identity in the process of being generated? 191 if (request_info_[key_type].gen_in_progress_counts_ == 192 request_info_[key_type].request_observers_.size()) { 193 // No need to do anything, the free identity will be returned to the 194 // observer in a MSG_GENERATE_IDENTITY_RESULT. 195 return; 196 } 197 } 198 199 // Enqueue/Post a worker task to do the generation. 200 ++request_info_[key_type].gen_in_progress_counts_; 201 WorkerTask* task = new WorkerTask(this, key_type); // Post 1 task/request. 202 // The WorkerTask is owned by the message data to make sure it will not be 203 // leaked even if the task does not get run. 204 WorkerTaskMessageData* msg = new WorkerTaskMessageData(task); 205 worker_thread_->Post(task, MSG_GENERATE_IDENTITY, msg); 206} 207 208void DtlsIdentityStoreImpl::OnIdentityGenerated( 209 rtc::KeyType key_type, rtc::scoped_ptr<rtc::SSLIdentity> identity) { 210 RTC_DCHECK(signaling_thread_->IsCurrent()); 211 212 RTC_DCHECK(request_info_[key_type].gen_in_progress_counts_); 213 --request_info_[key_type].gen_in_progress_counts_; 214 215 rtc::scoped_refptr<webrtc::DtlsIdentityRequestObserver> observer; 216 if (!request_info_[key_type].request_observers_.empty()) { 217 observer = request_info_[key_type].request_observers_.front(); 218 request_info_[key_type].request_observers_.pop(); 219 } 220 221 if (observer.get() == nullptr) { 222 // No observer - store result in |free_identities_|. 223 RTC_DCHECK(!request_info_[key_type].free_identity_.get()); 224 request_info_[key_type].free_identity_.swap(identity); 225 if (request_info_[key_type].free_identity_.get()) 226 LOG(LS_VERBOSE) << "A free DTLS identity was saved."; 227 else 228 LOG(LS_WARNING) << "Failed to generate DTLS identity (preemptively)."; 229 } else { 230 // Return the result to the observer. 231 if (identity.get()) { 232 LOG(LS_VERBOSE) << "A DTLS identity is returned to an observer."; 233 observer->OnSuccess(std::move(identity)); 234 } else { 235 LOG(LS_WARNING) << "Failed to generate DTLS identity."; 236 observer->OnFailure(0); 237 } 238 239 // Preemptively generate another identity of the same type? 240 if (worker_thread_ != signaling_thread_ && // Only do in background thread. 241 key_type == rtc::KT_RSA && // Only necessary for RSA. 242 !request_info_[key_type].free_identity_.get() && 243 request_info_[key_type].request_observers_.size() <= 244 request_info_[key_type].gen_in_progress_counts_) { 245 GenerateIdentity(key_type, nullptr); 246 } 247 } 248} 249 250} // namespace webrtc 251