OLD | NEW |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "remoting/protocol/jingle_session.h" | 5 #include "remoting/protocol/jingle_session.h" |
6 | 6 |
| 7 #include "base/base64.h" |
7 #include "base/bind.h" | 8 #include "base/bind.h" |
8 #include "base/location.h" | 9 #include "base/location.h" |
9 #include "base/message_loop_proxy.h" | 10 #include "base/message_loop_proxy.h" |
10 #include "base/rand_util.h" | 11 #include "base/rand_util.h" |
11 #include "base/stl_util.h" | 12 #include "base/stl_util.h" |
12 #include "crypto/hmac.h" | 13 #include "crypto/hmac.h" |
13 #include "crypto/rsa_private_key.h" | |
14 #include "net/base/net_errors.h" | 14 #include "net/base/net_errors.h" |
15 #include "net/socket/stream_socket.h" | 15 #include "net/socket/stream_socket.h" |
16 #include "remoting/base/constants.h" | 16 #include "remoting/base/constants.h" |
17 #include "remoting/protocol/auth_util.h" | 17 #include "remoting/protocol/auth_util.h" |
| 18 #include "remoting/protocol/authenticator.h" |
| 19 #include "remoting/protocol/channel_authenticator.h" |
18 #include "remoting/protocol/jingle_datagram_connector.h" | 20 #include "remoting/protocol/jingle_datagram_connector.h" |
19 #include "remoting/protocol/jingle_session_manager.h" | 21 #include "remoting/protocol/jingle_session_manager.h" |
20 #include "remoting/protocol/jingle_stream_connector.h" | 22 #include "remoting/protocol/jingle_stream_connector.h" |
21 #include "remoting/protocol/v1_client_channel_authenticator.h" | |
22 #include "remoting/protocol/v1_host_channel_authenticator.h" | |
23 #include "third_party/libjingle/source/talk/base/thread.h" | 23 #include "third_party/libjingle/source/talk/base/thread.h" |
24 #include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" | 24 #include "third_party/libjingle/source/talk/p2p/base/p2ptransportchannel.h" |
25 #include "third_party/libjingle/source/talk/p2p/base/session.h" | 25 #include "third_party/libjingle/source/talk/p2p/base/session.h" |
26 #include "third_party/libjingle/source/talk/p2p/base/transport.h" | 26 #include "third_party/libjingle/source/talk/p2p/base/transport.h" |
27 | 27 |
28 using cricket::BaseSession; | 28 using cricket::BaseSession; |
29 | 29 |
30 namespace remoting { | 30 namespace remoting { |
31 namespace protocol { | 31 namespace protocol { |
32 | 32 |
33 // static | |
34 JingleSession* JingleSession::CreateClientSession( | |
35 JingleSessionManager* manager, const std::string& host_public_key) { | |
36 return new JingleSession(manager, "", NULL, host_public_key); | |
37 } | |
38 | |
39 // static | |
40 JingleSession* JingleSession::CreateServerSession( | |
41 JingleSessionManager* manager, | |
42 const std::string& certificate, | |
43 crypto::RSAPrivateKey* key) { | |
44 return new JingleSession(manager, certificate, key, ""); | |
45 } | |
46 | |
47 JingleSession::JingleSession( | 33 JingleSession::JingleSession( |
48 JingleSessionManager* jingle_session_manager, | 34 JingleSessionManager* jingle_session_manager, |
49 const std::string& local_cert, | 35 cricket::Session* cricket_session, |
50 crypto::RSAPrivateKey* local_private_key, | 36 Authenticator* authenticator) |
51 const std::string& peer_public_key) | |
52 : jingle_session_manager_(jingle_session_manager), | 37 : jingle_session_manager_(jingle_session_manager), |
53 local_cert_(local_cert), | 38 authenticator_(authenticator), |
54 state_(INITIALIZING), | 39 state_(INITIALIZING), |
55 error_(OK), | 40 error_(OK), |
56 closing_(false), | 41 closing_(false), |
57 cricket_session_(NULL), | 42 cricket_session_(cricket_session), |
58 config_set_(false), | 43 config_set_(false), |
59 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { | 44 ALLOW_THIS_IN_INITIALIZER_LIST(task_factory_(this)) { |
60 // TODO(hclam): Need a better way to clone a key. | 45 jid_ = cricket_session_->remote_name(); |
61 if (local_private_key) { | 46 cricket_session_->SignalState.connect(this, &JingleSession::OnSessionState); |
62 std::vector<uint8> key_bytes; | 47 cricket_session_->SignalError.connect(this, &JingleSession::OnSessionError); |
63 CHECK(local_private_key->ExportPrivateKey(&key_bytes)); | |
64 local_private_key_.reset( | |
65 crypto::RSAPrivateKey::CreateFromPrivateKeyInfo(key_bytes)); | |
66 CHECK(local_private_key_.get()); | |
67 } | |
68 } | 48 } |
69 | 49 |
70 JingleSession::~JingleSession() { | 50 JingleSession::~JingleSession() { |
71 // Reset the callback so that it's not called from Close(). | 51 // Reset the callback so that it's not called from Close(). |
72 state_change_callback_.Reset(); | 52 state_change_callback_.Reset(); |
73 Close(); | 53 Close(); |
74 jingle_session_manager_->SessionDestroyed(this); | 54 jingle_session_manager_->SessionDestroyed(this); |
75 DCHECK(channel_connectors_.empty()); | 55 DCHECK(channel_connectors_.empty()); |
76 } | 56 } |
77 | 57 |
78 void JingleSession::Init(cricket::Session* cricket_session) { | 58 void JingleSession::SendSessionInitiate() { |
79 DCHECK(CalledOnValidThread()); | 59 DCHECK_EQ(authenticator_->state(), Authenticator::MESSAGE_READY); |
80 | 60 cricket_session_->Initiate( |
81 cricket_session_ = cricket_session; | 61 jid_, CreateSessionDescription(candidate_config()->Clone(), |
82 jid_ = cricket_session_->remote_name(); | 62 authenticator_->GetNextMessage())); |
83 cricket_session_->SignalState.connect( | |
84 this, &JingleSession::OnSessionState); | |
85 cricket_session_->SignalError.connect( | |
86 this, &JingleSession::OnSessionError); | |
87 } | 63 } |
88 | 64 |
89 void JingleSession::CloseInternal(int result, Error error) { | 65 void JingleSession::CloseInternal(int result, Error error) { |
90 DCHECK(CalledOnValidThread()); | 66 DCHECK(CalledOnValidThread()); |
91 | 67 |
92 if (state_ != FAILED && state_ != CLOSED && !closing_) { | 68 if (state_ != FAILED && state_ != CLOSED && !closing_) { |
93 closing_ = true; | 69 closing_ = true; |
94 | 70 |
95 // Tear down the cricket session, including the cricket transport channels. | 71 // Tear down the cricket session, including the cricket transport channels. |
96 if (cricket_session_) { | 72 if (cricket_session_) { |
(...skipping 95 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
192 } | 168 } |
193 | 169 |
194 void JingleSession::set_candidate_config( | 170 void JingleSession::set_candidate_config( |
195 const CandidateSessionConfig* candidate_config) { | 171 const CandidateSessionConfig* candidate_config) { |
196 DCHECK(CalledOnValidThread()); | 172 DCHECK(CalledOnValidThread()); |
197 DCHECK(!candidate_config_.get()); | 173 DCHECK(!candidate_config_.get()); |
198 DCHECK(candidate_config); | 174 DCHECK(candidate_config); |
199 candidate_config_.reset(candidate_config); | 175 candidate_config_.reset(candidate_config); |
200 } | 176 } |
201 | 177 |
202 const std::string& JingleSession::local_certificate() const { | |
203 DCHECK(CalledOnValidThread()); | |
204 return local_cert_; | |
205 } | |
206 | |
207 const SessionConfig& JingleSession::config() { | 178 const SessionConfig& JingleSession::config() { |
208 DCHECK(CalledOnValidThread()); | 179 DCHECK(CalledOnValidThread()); |
209 DCHECK(config_set_); | 180 DCHECK(config_set_); |
210 return config_; | 181 return config_; |
211 } | 182 } |
212 | 183 |
213 void JingleSession::set_config(const SessionConfig& config) { | 184 void JingleSession::set_config(const SessionConfig& config) { |
214 DCHECK(CalledOnValidThread()); | 185 DCHECK(CalledOnValidThread()); |
215 DCHECK(!config_set_); | 186 DCHECK(!config_set_); |
216 config_ = config; | 187 config_ = config; |
217 config_set_ = true; | 188 config_set_ = true; |
218 } | 189 } |
219 | 190 |
220 const std::string& JingleSession::initiator_token() { | |
221 DCHECK(CalledOnValidThread()); | |
222 return initiator_token_; | |
223 } | |
224 | |
225 void JingleSession::set_initiator_token(const std::string& initiator_token) { | |
226 DCHECK(CalledOnValidThread()); | |
227 initiator_token_ = initiator_token; | |
228 } | |
229 | |
230 const std::string& JingleSession::receiver_token() { | |
231 DCHECK(CalledOnValidThread()); | |
232 return receiver_token_; | |
233 } | |
234 | |
235 void JingleSession::set_receiver_token(const std::string& receiver_token) { | |
236 DCHECK(CalledOnValidThread()); | |
237 receiver_token_ = receiver_token; | |
238 } | |
239 | |
240 void JingleSession::set_shared_secret(const std::string& secret) { | |
241 DCHECK(CalledOnValidThread()); | |
242 shared_secret_ = secret; | |
243 } | |
244 | |
245 const std::string& JingleSession::shared_secret() { | |
246 DCHECK(CalledOnValidThread()); | |
247 return shared_secret_; | |
248 } | |
249 | |
250 | |
251 void JingleSession::Close() { | 191 void JingleSession::Close() { |
252 DCHECK(CalledOnValidThread()); | 192 DCHECK(CalledOnValidThread()); |
253 | 193 |
254 CloseInternal(net::ERR_CONNECTION_CLOSED, OK); | 194 CloseInternal(net::ERR_CONNECTION_CLOSED, OK); |
255 } | 195 } |
256 | 196 |
257 void JingleSession::OnSessionState( | 197 void JingleSession::OnSessionState( |
258 BaseSession* session, BaseSession::State state) { | 198 BaseSession* session, BaseSession::State state) { |
259 DCHECK(CalledOnValidThread()); | 199 DCHECK(CalledOnValidThread()); |
260 DCHECK_EQ(cricket_session_, session); | 200 DCHECK_EQ(cricket_session_, session); |
(...skipping 40 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
301 if (error != cricket::Session::ERROR_NONE) { | 241 if (error != cricket::Session::ERROR_NONE) { |
302 // TODO(sergeyu): Report different errors depending on |error|. | 242 // TODO(sergeyu): Report different errors depending on |error|. |
303 CloseInternal(net::ERR_CONNECTION_ABORTED, CHANNEL_CONNECTION_ERROR); | 243 CloseInternal(net::ERR_CONNECTION_ABORTED, CHANNEL_CONNECTION_ERROR); |
304 } | 244 } |
305 } | 245 } |
306 | 246 |
307 void JingleSession::OnInitiate() { | 247 void JingleSession::OnInitiate() { |
308 DCHECK(CalledOnValidThread()); | 248 DCHECK(CalledOnValidThread()); |
309 jid_ = cricket_session_->remote_name(); | 249 jid_ = cricket_session_->remote_name(); |
310 | 250 |
311 if (!cricket_session_->initiator()) { | |
312 const protocol::ContentDescription* content_description = | |
313 static_cast<const protocol::ContentDescription*>( | |
314 GetContentInfo()->description); | |
315 CHECK(content_description); | |
316 } | |
317 | |
318 if (cricket_session_->initiator()) { | 251 if (cricket_session_->initiator()) { |
319 // Set state to CONNECTING if this is an outgoing message. We need | 252 // Set state to CONNECTING if this is an outgoing message. We need |
320 // to post this task because channel creation works only after we | 253 // to post this task because channel creation works only after we |
321 // return from this method. This is because | 254 // return from this method. This is because |
322 // JingleChannelConnector::Connect() needs to call | 255 // JingleChannelConnector::Connect() needs to call |
323 // set_incoming_only() on P2PTransportChannel, but | 256 // set_incoming_only() on P2PTransportChannel, but |
324 // P2PTransportChannel is created only after we return from this | 257 // P2PTransportChannel is created only after we return from this |
325 // method. | 258 // method. |
326 // TODO(sergeyu): Add set_incoming_only() in TransportChannelProxy. | 259 // TODO(sergeyu): Add set_incoming_only() in TransportChannelProxy. |
327 jingle_session_manager_->message_loop_->PostTask( | 260 jingle_session_manager_->message_loop_->PostTask( |
(...skipping 10 matching lines...) Expand all Loading... |
338 const cricket::SessionDescription* description) { | 271 const cricket::SessionDescription* description) { |
339 // We should only be called after ParseContent has succeeded, in which | 272 // We should only be called after ParseContent has succeeded, in which |
340 // case there will always be a Chromoting session configuration. | 273 // case there will always be a Chromoting session configuration. |
341 const cricket::ContentInfo* content = | 274 const cricket::ContentInfo* content = |
342 description->FirstContentByType(kChromotingXmlNamespace); | 275 description->FirstContentByType(kChromotingXmlNamespace); |
343 CHECK(content); | 276 CHECK(content); |
344 const protocol::ContentDescription* content_description = | 277 const protocol::ContentDescription* content_description = |
345 static_cast<const protocol::ContentDescription*>(content->description); | 278 static_cast<const protocol::ContentDescription*>(content->description); |
346 CHECK(content_description); | 279 CHECK(content_description); |
347 | 280 |
348 remote_cert_ = content_description->certificate(); | 281 // Process authenticator message. |
349 if (remote_cert_.empty()) { | 282 const buzz::XmlElement* auth_message = |
350 LOG(ERROR) << "Connection response does not specify certificate"; | 283 content_description->authenticator_message(); |
| 284 if (!auth_message) { |
| 285 DLOG(WARNING) << "Received session-accept without authentication message " |
| 286 << auth_message->Str(); |
351 return false; | 287 return false; |
352 } | 288 } |
353 | 289 |
| 290 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); |
| 291 authenticator_->ProcessMessage(auth_message); |
| 292 // Support for more than two auth message is not implemented yet. |
| 293 DCHECK(authenticator_->state() != Authenticator::WAITING_MESSAGE && |
| 294 authenticator_->state() != Authenticator::MESSAGE_READY); |
| 295 |
| 296 if (authenticator_->state() != Authenticator::ACCEPTED) { |
| 297 return false; |
| 298 } |
| 299 |
354 SessionConfig config; | 300 SessionConfig config; |
355 if (!content_description->config()->GetFinalConfig(&config)) { | 301 if (!content_description->config()->GetFinalConfig(&config)) { |
356 LOG(ERROR) << "Connection response does not specify configuration"; | 302 LOG(ERROR) << "Connection response does not specify configuration"; |
357 return false; | 303 return false; |
358 } | 304 } |
359 if (!candidate_config()->IsSupported(config)) { | 305 if (!candidate_config()->IsSupported(config)) { |
360 LOG(ERROR) << "Connection response specifies an invalid configuration"; | 306 LOG(ERROR) << "Connection response specifies an invalid configuration"; |
361 return false; | 307 return false; |
362 } | 308 } |
363 | 309 |
(...skipping 18 matching lines...) Expand all Loading... |
382 } | 328 } |
383 | 329 |
384 void JingleSession::OnTerminate() { | 330 void JingleSession::OnTerminate() { |
385 DCHECK(CalledOnValidThread()); | 331 DCHECK(CalledOnValidThread()); |
386 CloseInternal(net::ERR_CONNECTION_ABORTED, OK); | 332 CloseInternal(net::ERR_CONNECTION_ABORTED, OK); |
387 } | 333 } |
388 | 334 |
389 void JingleSession::AcceptConnection() { | 335 void JingleSession::AcceptConnection() { |
390 SetState(CONNECTING); | 336 SetState(CONNECTING); |
391 | 337 |
392 if (!jingle_session_manager_->AcceptConnection(this, cricket_session_)) { | 338 const cricket::SessionDescription* session_description = |
| 339 cricket_session_->remote_description(); |
| 340 const cricket::ContentInfo* content = |
| 341 session_description->FirstContentByType(kChromotingXmlNamespace); |
| 342 |
| 343 CHECK(content); |
| 344 const ContentDescription* content_description = |
| 345 static_cast<const ContentDescription*>(content->description); |
| 346 candidate_config_.reset(content_description->config()->Clone()); |
| 347 |
| 348 SessionManager::IncomingSessionResponse response = |
| 349 jingle_session_manager_->AcceptConnection(this); |
| 350 if (response != SessionManager::ACCEPT) { |
| 351 if (response == SessionManager::INCOMPATIBLE) { |
| 352 cricket_session_->TerminateWithReason( |
| 353 cricket::STR_TERMINATE_INCOMPATIBLE_PARAMETERS); |
| 354 } else { |
| 355 cricket_session_->TerminateWithReason(cricket::STR_TERMINATE_DECLINE); |
| 356 } |
393 Close(); | 357 Close(); |
394 // Release session so that JingleSessionManager::SessionDestroyed() | 358 // Release session so that JingleSessionManager::SessionDestroyed() |
395 // doesn't try to call cricket::SessionManager::DestroySession() for it. | 359 // doesn't try to call cricket::SessionManager::DestroySession() for it. |
396 ReleaseSession(); | 360 ReleaseSession(); |
397 delete this; | 361 delete this; |
398 return; | 362 return; |
399 } | 363 } |
400 | 364 |
401 if (!VerifySupportAuthToken(jid_, shared_secret_, initiator_token())) | 365 const buzz::XmlElement* auth_message = |
| 366 content_description->authenticator_message(); |
| 367 if (!auth_message) { |
| 368 DLOG(WARNING) << "Received session-initiate without authenticator message."; |
| 369 CloseInternal(net::ERR_CONNECTION_FAILED, INCOMPATIBLE_PROTOCOL); |
| 370 return; |
| 371 } |
| 372 |
| 373 authenticator_.reset( |
| 374 jingle_session_manager_->CreateAuthenticator(jid(), auth_message)); |
| 375 if (!authenticator_.get()) { |
| 376 CloseInternal(net::ERR_CONNECTION_FAILED, INCOMPATIBLE_PROTOCOL); |
| 377 return; |
| 378 } |
| 379 |
| 380 DCHECK(authenticator_->state() == Authenticator::WAITING_MESSAGE); |
| 381 authenticator_->ProcessMessage(auth_message); |
| 382 // Support for more than two auth message is not implemented yet. |
| 383 DCHECK(authenticator_->state() != Authenticator::WAITING_MESSAGE); |
| 384 if (authenticator_->state() == Authenticator::REJECTED) { |
402 CloseInternal(net::ERR_CONNECTION_FAILED, AUTHENTICATION_FAILED); | 385 CloseInternal(net::ERR_CONNECTION_FAILED, AUTHENTICATION_FAILED); |
| 386 return; |
| 387 } |
| 388 |
| 389 // Connection must be configured by the AcceptConnection() callback. |
| 390 CandidateSessionConfig* candidate_config = |
| 391 CandidateSessionConfig::CreateFrom(config()); |
| 392 |
| 393 buzz::XmlElement* auth_reply = NULL; |
| 394 if (authenticator_->state() == Authenticator::MESSAGE_READY) |
| 395 auth_reply = authenticator_->GetNextMessage(); |
| 396 DCHECK_EQ(authenticator_->state(), Authenticator::ACCEPTED); |
| 397 cricket_session_->Accept( |
| 398 CreateSessionDescription(candidate_config, auth_reply)); |
403 } | 399 } |
404 | 400 |
405 void JingleSession::AddChannelConnector( | 401 void JingleSession::AddChannelConnector( |
406 const std::string& name, JingleChannelConnector* connector) { | 402 const std::string& name, JingleChannelConnector* connector) { |
407 DCHECK(channel_connectors_.find(name) == channel_connectors_.end()); | 403 DCHECK(channel_connectors_.find(name) == channel_connectors_.end()); |
408 | 404 |
409 const std::string& content_name = GetContentInfo()->name; | 405 const std::string& content_name = GetContentInfo()->name; |
410 cricket::TransportChannel* raw_channel = | 406 cricket::TransportChannel* raw_channel = |
411 cricket_session_->CreateChannel(content_name, name); | 407 cricket_session_->CreateChannel(content_name, name); |
412 | 408 |
413 if (!jingle_session_manager_->allow_nat_traversal_ && | 409 if (!jingle_session_manager_->allow_nat_traversal_ && |
414 !cricket_session_->initiator()) { | 410 !cricket_session_->initiator()) { |
415 // Don't make outgoing connections from the host to client when | 411 // Don't make outgoing connections from the host to client when |
416 // NAT traversal is disabled. | 412 // NAT traversal is disabled. |
417 raw_channel->GetP2PChannel()->set_incoming_only(true); | 413 raw_channel->GetP2PChannel()->set_incoming_only(true); |
418 } | 414 } |
419 | 415 |
420 channel_connectors_[name] = connector; | 416 channel_connectors_[name] = connector; |
421 ChannelAuthenticator* authenticator; | 417 ChannelAuthenticator* authenticator = |
422 if (cricket_session_->initiator()) { | 418 authenticator_->CreateChannelAuthenticator(); |
423 authenticator = new V1ClientChannelAuthenticator( | |
424 remote_cert_, shared_secret_); | |
425 } else { | |
426 authenticator = new V1HostChannelAuthenticator( | |
427 local_cert_, local_private_key_.get(), shared_secret_); | |
428 } | |
429 connector->Connect(authenticator, raw_channel); | 419 connector->Connect(authenticator, raw_channel); |
430 | 420 |
431 // Workaround bug in libjingle - it doesn't connect channels if they | 421 // Workaround bug in libjingle - it doesn't connect channels if they |
432 // are created after the session is accepted. See crbug.com/89384. | 422 // are created after the session is accepted. See crbug.com/89384. |
433 // TODO(sergeyu): Fix the bug and remove this line. | 423 // TODO(sergeyu): Fix the bug and remove this line. |
434 cricket_session_->GetTransport(content_name)->ConnectChannels(); | 424 cricket_session_->GetTransport(content_name)->ConnectChannels(); |
435 } | 425 } |
436 | 426 |
437 void JingleSession::OnChannelConnectorFinished( | 427 void JingleSession::OnChannelConnectorFinished( |
438 const std::string& name, JingleChannelConnector* connector) { | 428 const std::string& name, JingleChannelConnector* connector) { |
(...skipping 23 matching lines...) Expand all Loading... |
462 if (new_state != state_) { | 452 if (new_state != state_) { |
463 DCHECK_NE(state_, CLOSED); | 453 DCHECK_NE(state_, CLOSED); |
464 DCHECK_NE(state_, FAILED); | 454 DCHECK_NE(state_, FAILED); |
465 | 455 |
466 state_ = new_state; | 456 state_ = new_state; |
467 if (!state_change_callback_.is_null()) | 457 if (!state_change_callback_.is_null()) |
468 state_change_callback_.Run(new_state); | 458 state_change_callback_.Run(new_state); |
469 } | 459 } |
470 } | 460 } |
471 | 461 |
| 462 // static |
| 463 cricket::SessionDescription* JingleSession::CreateSessionDescription( |
| 464 const CandidateSessionConfig* config, |
| 465 const buzz::XmlElement* authenticator_message) { |
| 466 cricket::SessionDescription* desc = new cricket::SessionDescription(); |
| 467 desc->AddContent( |
| 468 ContentDescription::kChromotingContentName, kChromotingXmlNamespace, |
| 469 new ContentDescription(config, authenticator_message)); |
| 470 return desc; |
| 471 } |
| 472 |
472 } // namespace protocol | 473 } // namespace protocol |
473 } // namespace remoting | 474 } // namespace remoting |
OLD | NEW |