Home | History | Annotate | Download | only in webrtc
      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 
     35 using webrtc::DtlsIdentityRequestObserver;
     36 
     37 namespace webrtc {
     38 
     39 // Passed to SSLIdentity::Generate, "WebRTC". Used for the certificates'
     40 // subject and issuer name.
     41 const char kIdentityName[] = "WebRTC";
     42 
     43 namespace {
     44 
     45 enum {
     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.
     56 class 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 
    122 DtlsIdentityStoreImpl::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 
    136 DtlsIdentityStoreImpl::~DtlsIdentityStoreImpl() {
    137   RTC_DCHECK(signaling_thread_->IsCurrent());
    138   SignalDestroyed();
    139 }
    140 
    141 void 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 
    150 void 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 
    163 bool 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 
    169 void 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 
    208 void 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