Home | History | Annotate | Download | only in base
      1 /*
      2  * libjingle
      3  * Copyright 2012, 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 #ifndef TALK_P2P_BASE_DTLSTRANSPORT_H_
     29 #define TALK_P2P_BASE_DTLSTRANSPORT_H_
     30 
     31 #include "talk/p2p/base/dtlstransportchannel.h"
     32 #include "talk/p2p/base/transport.h"
     33 
     34 namespace rtc {
     35 class SSLIdentity;
     36 }
     37 
     38 namespace cricket {
     39 
     40 class PortAllocator;
     41 
     42 // Base should be a descendant of cricket::Transport
     43 template<class Base>
     44 class DtlsTransport : public Base {
     45  public:
     46   DtlsTransport(rtc::Thread* signaling_thread,
     47                 rtc::Thread* worker_thread,
     48                 const std::string& content_name,
     49                 PortAllocator* allocator,
     50                 rtc::SSLIdentity* identity)
     51       : Base(signaling_thread, worker_thread, content_name, allocator),
     52         identity_(identity),
     53         secure_role_(rtc::SSL_CLIENT) {
     54   }
     55 
     56   ~DtlsTransport() {
     57     Base::DestroyAllChannels();
     58   }
     59   virtual void SetIdentity_w(rtc::SSLIdentity* identity) {
     60     identity_ = identity;
     61   }
     62   virtual bool GetIdentity_w(rtc::SSLIdentity** identity) {
     63     if (!identity_)
     64       return false;
     65 
     66     *identity = identity_->GetReference();
     67     return true;
     68   }
     69 
     70   virtual bool ApplyLocalTransportDescription_w(TransportChannelImpl* channel,
     71                                                 std::string* error_desc) {
     72     rtc::SSLFingerprint* local_fp =
     73         Base::local_description()->identity_fingerprint.get();
     74 
     75     if (local_fp) {
     76       // Sanity check local fingerprint.
     77       if (identity_) {
     78         rtc::scoped_ptr<rtc::SSLFingerprint> local_fp_tmp(
     79             rtc::SSLFingerprint::Create(local_fp->algorithm,
     80                                               identity_));
     81         ASSERT(local_fp_tmp.get() != NULL);
     82         if (!(*local_fp_tmp == *local_fp)) {
     83           std::ostringstream desc;
     84           desc << "Local fingerprint does not match identity. Expected: ";
     85           desc << local_fp_tmp->ToString();
     86           desc << " Got: " << local_fp->ToString();
     87           return BadTransportDescription(desc.str(), error_desc);
     88         }
     89       } else {
     90         return BadTransportDescription(
     91             "Local fingerprint provided but no identity available.",
     92             error_desc);
     93       }
     94     } else {
     95       identity_ = NULL;
     96     }
     97 
     98     if (!channel->SetLocalIdentity(identity_)) {
     99       return BadTransportDescription("Failed to set local identity.",
    100                                      error_desc);
    101     }
    102 
    103     // Apply the description in the base class.
    104     return Base::ApplyLocalTransportDescription_w(channel, error_desc);
    105   }
    106 
    107   virtual bool NegotiateTransportDescription_w(ContentAction local_role,
    108                                                std::string* error_desc) {
    109     if (!Base::local_description() || !Base::remote_description()) {
    110       const std::string msg = "Local and Remote description must be set before "
    111                               "transport descriptions are negotiated";
    112       return BadTransportDescription(msg, error_desc);
    113     }
    114 
    115     rtc::SSLFingerprint* local_fp =
    116         Base::local_description()->identity_fingerprint.get();
    117     rtc::SSLFingerprint* remote_fp =
    118         Base::remote_description()->identity_fingerprint.get();
    119 
    120     if (remote_fp && local_fp) {
    121       remote_fingerprint_.reset(new rtc::SSLFingerprint(*remote_fp));
    122 
    123       // From RFC 4145, section-4.1, The following are the values that the
    124       // 'setup' attribute can take in an offer/answer exchange:
    125       //       Offer      Answer
    126       //      ________________
    127       //      active     passive / holdconn
    128       //      passive    active / holdconn
    129       //      actpass    active / passive / holdconn
    130       //      holdconn   holdconn
    131       //
    132       // Set the role that is most conformant with RFC 5763, Section 5, bullet 1
    133       // The endpoint MUST use the setup attribute defined in [RFC4145].
    134       // The endpoint that is the offerer MUST use the setup attribute
    135       // value of setup:actpass and be prepared to receive a client_hello
    136       // before it receives the answer.  The answerer MUST use either a
    137       // setup attribute value of setup:active or setup:passive.  Note that
    138       // if the answerer uses setup:passive, then the DTLS handshake will
    139       // not begin until the answerer is received, which adds additional
    140       // latency. setup:active allows the answer and the DTLS handshake to
    141       // occur in parallel.  Thus, setup:active is RECOMMENDED.  Whichever
    142       // party is active MUST initiate a DTLS handshake by sending a
    143       // ClientHello over each flow (host/port quartet).
    144       // IOW - actpass and passive modes should be treated as server and
    145       // active as client.
    146       ConnectionRole local_connection_role =
    147           Base::local_description()->connection_role;
    148       ConnectionRole remote_connection_role =
    149           Base::remote_description()->connection_role;
    150 
    151       bool is_remote_server = false;
    152       if (local_role == CA_OFFER) {
    153         if (local_connection_role != CONNECTIONROLE_ACTPASS) {
    154           return BadTransportDescription(
    155               "Offerer must use actpass value for setup attribute.",
    156               error_desc);
    157         }
    158 
    159         if (remote_connection_role == CONNECTIONROLE_ACTIVE ||
    160             remote_connection_role == CONNECTIONROLE_PASSIVE ||
    161             remote_connection_role == CONNECTIONROLE_NONE) {
    162           is_remote_server = (remote_connection_role == CONNECTIONROLE_PASSIVE);
    163         } else {
    164           const std::string msg =
    165               "Answerer must use either active or passive value "
    166               "for setup attribute.";
    167           return BadTransportDescription(msg, error_desc);
    168         }
    169         // If remote is NONE or ACTIVE it will act as client.
    170       } else {
    171         if (remote_connection_role != CONNECTIONROLE_ACTPASS &&
    172             remote_connection_role != CONNECTIONROLE_NONE) {
    173           return BadTransportDescription(
    174               "Offerer must use actpass value for setup attribute.",
    175               error_desc);
    176         }
    177 
    178         if (local_connection_role == CONNECTIONROLE_ACTIVE ||
    179             local_connection_role == CONNECTIONROLE_PASSIVE) {
    180           is_remote_server = (local_connection_role == CONNECTIONROLE_ACTIVE);
    181         } else {
    182           const std::string msg =
    183               "Answerer must use either active or passive value "
    184               "for setup attribute.";
    185           return BadTransportDescription(msg, error_desc);
    186         }
    187 
    188         // If local is passive, local will act as server.
    189       }
    190 
    191       secure_role_ = is_remote_server ? rtc::SSL_CLIENT :
    192                                         rtc::SSL_SERVER;
    193 
    194     } else if (local_fp && (local_role == CA_ANSWER)) {
    195       return BadTransportDescription(
    196           "Local fingerprint supplied when caller didn't offer DTLS.",
    197           error_desc);
    198     } else {
    199       // We are not doing DTLS
    200       remote_fingerprint_.reset(new rtc::SSLFingerprint(
    201           "", NULL, 0));
    202     }
    203 
    204     // Now run the negotiation for the base class.
    205     return Base::NegotiateTransportDescription_w(local_role, error_desc);
    206   }
    207 
    208   virtual DtlsTransportChannelWrapper* CreateTransportChannel(int component) {
    209     return new DtlsTransportChannelWrapper(
    210         this, Base::CreateTransportChannel(component));
    211   }
    212 
    213   virtual void DestroyTransportChannel(TransportChannelImpl* channel) {
    214     // Kind of ugly, but this lets us do the exact inverse of the create.
    215     DtlsTransportChannelWrapper* dtls_channel =
    216         static_cast<DtlsTransportChannelWrapper*>(channel);
    217     TransportChannelImpl* base_channel = dtls_channel->channel();
    218     delete dtls_channel;
    219     Base::DestroyTransportChannel(base_channel);
    220   }
    221 
    222   virtual bool GetSslRole_w(rtc::SSLRole* ssl_role) const {
    223     ASSERT(ssl_role != NULL);
    224     *ssl_role = secure_role_;
    225     return true;
    226   }
    227 
    228  private:
    229   virtual bool ApplyNegotiatedTransportDescription_w(
    230       TransportChannelImpl* channel,
    231       std::string* error_desc) {
    232     // Set ssl role. Role must be set before fingerprint is applied, which
    233     // initiates DTLS setup.
    234     if (!channel->SetSslRole(secure_role_)) {
    235       return BadTransportDescription("Failed to set ssl role for the channel.",
    236                                      error_desc);
    237     }
    238     // Apply remote fingerprint.
    239     if (!channel->SetRemoteFingerprint(
    240         remote_fingerprint_->algorithm,
    241         reinterpret_cast<const uint8 *>(remote_fingerprint_->
    242                                     digest.data()),
    243         remote_fingerprint_->digest.length())) {
    244       return BadTransportDescription("Failed to apply remote fingerprint.",
    245                                      error_desc);
    246     }
    247     return Base::ApplyNegotiatedTransportDescription_w(channel, error_desc);
    248   }
    249 
    250   rtc::SSLIdentity* identity_;
    251   rtc::SSLRole secure_role_;
    252   rtc::scoped_ptr<rtc::SSLFingerprint> remote_fingerprint_;
    253 };
    254 
    255 }  // namespace cricket
    256 
    257 #endif  // TALK_P2P_BASE_DTLSTRANSPORT_H_
    258