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