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