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