1 // Copyright 2014 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "extensions/browser/api/cast_channel/cast_socket.h"
10 #include "base/bind.h"
11 #include "base/callback_helpers.h"
12 #include "base/format_macros.h"
13 #include "base/lazy_instance.h"
14 #include "base/numerics/safe_conversions.h"
15 #include "base/strings/string_number_conversions.h"
16 #include "base/strings/stringprintf.h"
17 #include "base/sys_byteorder.h"
18 #include "base/time/time.h"
19 #include "extensions/browser/api/cast_channel/cast_auth_util.h"
20 #include "extensions/browser/api/cast_channel/cast_framer.h"
21 #include "extensions/browser/api/cast_channel/cast_message_util.h"
22 #include "extensions/browser/api/cast_channel/cast_transport.h"
23 #include "extensions/browser/api/cast_channel/logger.h"
24 #include "extensions/browser/api/cast_channel/logger_util.h"
25 #include "extensions/common/api/cast_channel/cast_channel.pb.h"
26 #include "net/base/address_list.h"
27 #include "net/base/host_port_pair.h"
28 #include "net/base/net_errors.h"
29 #include "net/base/net_util.h"
30 #include "net/cert/cert_verifier.h"
31 #include "net/cert/x509_certificate.h"
32 #include "net/http/transport_security_state.h"
33 #include "net/socket/client_socket_factory.h"
34 #include "net/socket/client_socket_handle.h"
35 #include "net/socket/ssl_client_socket.h"
36 #include "net/socket/stream_socket.h"
37 #include "net/socket/tcp_client_socket.h"
38 #include "net/ssl/ssl_config_service.h"
39 #include "net/ssl/ssl_info.h"
41 // Assumes |ip_endpoint_| of type net::IPEndPoint and |channel_auth_| of enum
42 // type ChannelAuthType are available in the current scope.
43 #define VLOG_WITH_CONNECTION(level) VLOG(level) << "[" << \
44 ip_endpoint_.ToString() << ", auth=" << channel_auth_ << "] "
48 const int kMaxSelfSignedCertLifetimeInDays
= 2;
50 std::string
FormatTimeForLogging(base::Time time
) {
51 base::Time::Exploded exploded_time
;
52 time
.UTCExplode(&exploded_time
);
53 return base::StringPrintf(
54 "%04d-%02d-%02d %02d:%02d:%02d.%03d UTC", exploded_time
.year
,
55 exploded_time
.month
, exploded_time
.day_of_month
, exploded_time
.hour
,
56 exploded_time
.minute
, exploded_time
.second
, exploded_time
.millisecond
);
61 namespace extensions
{
62 static base::LazyInstance
<BrowserContextKeyedAPIFactory
<
63 ApiResourceManager
<core_api::cast_channel::CastSocket
> > > g_factory
=
64 LAZY_INSTANCE_INITIALIZER
;
68 BrowserContextKeyedAPIFactory
<
69 ApiResourceManager
<core_api::cast_channel::CastSocket
> >*
70 ApiResourceManager
<core_api::cast_channel::CastSocket
>::GetFactoryInstance() {
71 return g_factory
.Pointer();
75 namespace cast_channel
{
77 CastSocket::CastSocket(const std::string
& owner_extension_id
)
78 : ApiResource(owner_extension_id
) {
81 CastSocketImpl::CastSocketImpl(const std::string
& owner_extension_id
,
82 const net::IPEndPoint
& ip_endpoint
,
83 ChannelAuthType channel_auth
,
85 const base::TimeDelta
& timeout
,
87 const scoped_refptr
<Logger
>& logger
,
88 long device_capabilities
)
89 : CastSocket(owner_extension_id
),
91 owner_extension_id_(owner_extension_id
),
93 ip_endpoint_(ip_endpoint
),
94 channel_auth_(channel_auth
),
96 keep_alive_(keep_alive
),
98 connect_timeout_(timeout
),
99 connect_timeout_timer_(new base::OneShotTimer
<CastSocketImpl
>),
101 device_capabilities_(device_capabilities
),
102 connect_state_(proto::CONN_STATE_NONE
),
103 error_state_(CHANNEL_ERROR_NONE
),
104 ready_state_(READY_STATE_NONE
) {
106 DCHECK(channel_auth_
== CHANNEL_AUTH_TYPE_SSL
||
107 channel_auth_
== CHANNEL_AUTH_TYPE_SSL_VERIFIED
);
108 net_log_source_
.type
= net::NetLog::SOURCE_SOCKET
;
109 net_log_source_
.id
= net_log_
->NextID();
112 CastSocketImpl::~CastSocketImpl() {
113 // Ensure that resources are freed but do not run pending callbacks to avoid
118 ReadyState
CastSocketImpl::ready_state() const {
122 ChannelError
CastSocketImpl::error_state() const {
126 const net::IPEndPoint
& CastSocketImpl::ip_endpoint() const {
130 int CastSocketImpl::id() const {
134 void CastSocketImpl::set_id(int id
) {
138 ChannelAuthType
CastSocketImpl::channel_auth() const {
139 return channel_auth_
;
142 bool CastSocketImpl::keep_alive() const {
146 scoped_ptr
<net::TCPClientSocket
> CastSocketImpl::CreateTcpSocket() {
147 net::AddressList
addresses(ip_endpoint_
);
148 return scoped_ptr
<net::TCPClientSocket
>(
149 new net::TCPClientSocket(addresses
, net_log_
, net_log_source_
));
150 // Options cannot be set on the TCPClientSocket yet, because the
151 // underlying platform socket will not be created until Bind()
152 // or Connect() is called.
155 scoped_ptr
<net::SSLClientSocket
> CastSocketImpl::CreateSslSocket(
156 scoped_ptr
<net::StreamSocket
> socket
) {
157 net::SSLConfig ssl_config
;
158 // If a peer cert was extracted in a previous attempt to connect, then
159 // whitelist that cert.
160 if (!peer_cert_
.empty()) {
161 net::SSLConfig::CertAndStatus cert_and_status
;
162 cert_and_status
.cert_status
= net::CERT_STATUS_AUTHORITY_INVALID
;
163 cert_and_status
.der_cert
= peer_cert_
;
164 ssl_config
.allowed_bad_certs
.push_back(cert_and_status
);
165 logger_
->LogSocketEvent(channel_id_
, proto::SSL_CERT_WHITELISTED
);
168 cert_verifier_
.reset(net::CertVerifier::CreateDefault());
169 transport_security_state_
.reset(new net::TransportSecurityState
);
170 net::SSLClientSocketContext context
;
171 // CertVerifier and TransportSecurityState are owned by us, not the
173 context
.cert_verifier
= cert_verifier_
.get();
174 context
.transport_security_state
= transport_security_state_
.get();
176 scoped_ptr
<net::ClientSocketHandle
> connection(new net::ClientSocketHandle
);
177 connection
->SetSocket(socket
.Pass());
178 net::HostPortPair host_and_port
= net::HostPortPair::FromIPEndPoint(
181 return net::ClientSocketFactory::GetDefaultFactory()->CreateSSLClientSocket(
182 connection
.Pass(), host_and_port
, ssl_config
, context
);
185 bool CastSocketImpl::ExtractPeerCert(std::string
* cert
) {
187 DCHECK(peer_cert_
.empty());
188 net::SSLInfo ssl_info
;
189 if (!socket_
->GetSSLInfo(&ssl_info
) || !ssl_info
.cert
.get()) {
193 logger_
->LogSocketEvent(channel_id_
, proto::SSL_INFO_OBTAINED
);
195 // Ensure that the peer cert (which is self-signed) doesn't have an excessive
196 // life-time (i.e. no more than 2 days).
197 base::Time expiry
= ssl_info
.cert
->valid_expiry();
198 base::Time lifetimeLimit
=
200 base::TimeDelta::FromDays(kMaxSelfSignedCertLifetimeInDays
);
201 if (expiry
.is_null() || expiry
> lifetimeLimit
) {
202 std::string details
= FormatTimeForLogging(expiry
);
203 details
+= " " + ip_endpoint().ToString();
204 LOG(ERROR
) << "Peer cert has excessive lifetime. details=" << details
;
205 logger_
->LogSocketEventWithDetails(
206 channel_id_
, proto::SSL_CERT_EXCESSIVE_LIFETIME
, details
);
210 bool result
= net::X509Certificate::GetDEREncoded(
211 ssl_info
.cert
->os_cert_handle(), cert
);
213 VLOG_WITH_CONNECTION(1) << "Successfully extracted peer certificate";
216 logger_
->LogSocketEventWithRv(
217 channel_id_
, proto::DER_ENCODED_CERT_OBTAIN
, result
? 1 : 0);
221 bool CastSocketImpl::VerifyChannelPolicy(const AuthResult
& result
) {
222 if ((device_capabilities_
& CastDeviceCapability::VIDEO_OUT
) != 0 &&
223 (result
.channel_policies
& AuthResult::POLICY_AUDIO_ONLY
) != 0) {
224 LOG(ERROR
) << "Audio only policy enforced";
225 logger_
->LogSocketEventWithDetails(
226 channel_id_
, proto::CHANNEL_POLICY_ENFORCED
, std::string());
232 bool CastSocketImpl::VerifyChallengeReply() {
233 AuthResult result
= AuthenticateChallengeReply(*challenge_reply_
, peer_cert_
);
234 if (result
.success()) {
235 VLOG(1) << result
.error_message
;
236 if (!VerifyChannelPolicy(result
)) {
240 logger_
->LogSocketChallengeReplyEvent(channel_id_
, result
);
241 return result
.success();
244 void CastSocketImpl::SetTransportForTesting(
245 scoped_ptr
<CastTransport
> transport
) {
246 transport_
= transport
.Pass();
249 void CastSocketImpl::Connect(scoped_ptr
<CastTransport::Delegate
> delegate
,
250 base::Callback
<void(ChannelError
)> callback
) {
251 DCHECK(CalledOnValidThread());
252 VLOG_WITH_CONNECTION(1) << "Connect readyState = " << ready_state_
;
254 read_delegate_
= delegate
.Pass();
256 if (ready_state_
!= READY_STATE_NONE
) {
257 logger_
->LogSocketEventWithDetails(
258 channel_id_
, proto::CONNECT_FAILED
, "ReadyState not NONE");
259 callback
.Run(CHANNEL_ERROR_CONNECT_ERROR
);
263 connect_callback_
= callback
;
264 SetReadyState(READY_STATE_CONNECTING
);
265 SetConnectState(proto::CONN_STATE_TCP_CONNECT
);
267 // Set up connection timeout.
268 if (connect_timeout_
.InMicroseconds() > 0) {
269 DCHECK(connect_timeout_callback_
.IsCancelled());
270 connect_timeout_callback_
.Reset(
271 base::Bind(&CastSocketImpl::OnConnectTimeout
, base::Unretained(this)));
272 GetTimer()->Start(FROM_HERE
,
274 connect_timeout_callback_
.callback());
277 DoConnectLoop(net::OK
);
280 CastTransport
* CastSocketImpl::transport() const {
281 DCHECK_EQ(ready_state_
, READY_STATE_OPEN
);
282 return transport_
.get();
285 void CastSocketImpl::OnConnectTimeout() {
286 DCHECK(CalledOnValidThread());
287 // Stop all pending connection setup tasks and report back to the client.
289 logger_
->LogSocketEvent(channel_id_
, proto::CONNECT_TIMED_OUT
);
290 VLOG_WITH_CONNECTION(1) << "Timeout while establishing a connection.";
291 SetErrorState(CHANNEL_ERROR_CONNECT_TIMEOUT
);
295 void CastSocketImpl::PostTaskToStartConnectLoop(int result
) {
296 DCHECK(CalledOnValidThread());
297 DCHECK(connect_loop_callback_
.IsCancelled());
298 connect_loop_callback_
.Reset(base::Bind(&CastSocketImpl::DoConnectLoop
,
299 base::Unretained(this), result
));
300 base::MessageLoop::current()->PostTask(FROM_HERE
,
301 connect_loop_callback_
.callback());
304 // This method performs the state machine transitions for connection flow.
305 // There are two entry points to this method:
306 // 1. Connect method: this starts the flow
307 // 2. Callback from network operations that finish asynchronously.
308 void CastSocketImpl::DoConnectLoop(int result
) {
309 connect_loop_callback_
.Cancel();
311 LOG(ERROR
) << "CANCELLED - Aborting DoConnectLoop.";
315 // Network operations can either finish synchronously or asynchronously.
316 // This method executes the state machine transitions in a loop so that
317 // correct state transitions happen even when network operations finish
321 proto::ConnectionState state
= connect_state_
;
322 // Default to CONN_STATE_NONE, which breaks the processing loop if any
323 // handler fails to transition to another state to continue processing.
324 connect_state_
= proto::CONN_STATE_NONE
;
326 case proto::CONN_STATE_TCP_CONNECT
:
329 case proto::CONN_STATE_TCP_CONNECT_COMPLETE
:
330 rv
= DoTcpConnectComplete(rv
);
332 case proto::CONN_STATE_SSL_CONNECT
:
333 DCHECK_EQ(net::OK
, rv
);
336 case proto::CONN_STATE_SSL_CONNECT_COMPLETE
:
337 rv
= DoSslConnectComplete(rv
);
339 case proto::CONN_STATE_AUTH_CHALLENGE_SEND
:
340 rv
= DoAuthChallengeSend();
342 case proto::CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE
:
343 rv
= DoAuthChallengeSendComplete(rv
);
345 case proto::CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE
:
346 rv
= DoAuthChallengeReplyComplete(rv
);
349 NOTREACHED() << "BUG in connect flow. Unknown state: " << state
;
352 } while (rv
!= net::ERR_IO_PENDING
&&
353 connect_state_
!= proto::CONN_STATE_NONE
);
354 // Get out of the loop either when: // a. A network operation is pending, OR
355 // b. The Do* method called did not change state
357 // No state change occurred above. This means state has transitioned to NONE.
358 if (connect_state_
== proto::CONN_STATE_NONE
) {
359 logger_
->LogSocketConnectState(channel_id_
, connect_state_
);
362 if (rv
!= net::ERR_IO_PENDING
) {
368 int CastSocketImpl::DoTcpConnect() {
369 DCHECK(connect_loop_callback_
.IsCancelled());
370 VLOG_WITH_CONNECTION(1) << "DoTcpConnect";
371 SetConnectState(proto::CONN_STATE_TCP_CONNECT_COMPLETE
);
372 tcp_socket_
= CreateTcpSocket();
374 int rv
= tcp_socket_
->Connect(
375 base::Bind(&CastSocketImpl::DoConnectLoop
, base::Unretained(this)));
376 logger_
->LogSocketEventWithRv(channel_id_
, proto::TCP_SOCKET_CONNECT
, rv
);
380 int CastSocketImpl::DoTcpConnectComplete(int connect_result
) {
381 VLOG_WITH_CONNECTION(1) << "DoTcpConnectComplete: " << connect_result
;
382 logger_
->LogSocketEventWithRv(channel_id_
, proto::TCP_SOCKET_CONNECT_COMPLETE
,
384 if (connect_result
== net::OK
) {
385 SetConnectState(proto::CONN_STATE_SSL_CONNECT
);
387 SetErrorState(CHANNEL_ERROR_CONNECT_ERROR
);
389 return connect_result
;
392 int CastSocketImpl::DoSslConnect() {
393 DCHECK(connect_loop_callback_
.IsCancelled());
394 VLOG_WITH_CONNECTION(1) << "DoSslConnect";
395 SetConnectState(proto::CONN_STATE_SSL_CONNECT_COMPLETE
);
396 socket_
= CreateSslSocket(tcp_socket_
.Pass());
398 int rv
= socket_
->Connect(
399 base::Bind(&CastSocketImpl::DoConnectLoop
, base::Unretained(this)));
400 logger_
->LogSocketEventWithRv(channel_id_
, proto::SSL_SOCKET_CONNECT
, rv
);
404 int CastSocketImpl::DoSslConnectComplete(int result
) {
405 logger_
->LogSocketEventWithRv(channel_id_
, proto::SSL_SOCKET_CONNECT_COMPLETE
,
407 VLOG_WITH_CONNECTION(1) << "DoSslConnectComplete: " << result
;
408 if (result
== net::ERR_CERT_AUTHORITY_INVALID
&&
409 peer_cert_
.empty() && ExtractPeerCert(&peer_cert_
)) {
410 // Peer certificate fallback - try the connection again, from the top.
411 SetConnectState(proto::CONN_STATE_TCP_CONNECT
);
412 } else if (result
== net::OK
) {
413 // SSL connection succeeded.
414 if (!transport_
.get()) {
415 // Create a channel transport if one wasn't already set (e.g. by test
417 transport_
.reset(new CastTransportImpl(this->socket_
.get(), channel_id_
,
418 ip_endpoint_
, channel_auth_
,
421 transport_
->SetReadDelegate(
422 make_scoped_ptr(new AuthTransportDelegate(this)));
423 if (channel_auth_
== CHANNEL_AUTH_TYPE_SSL_VERIFIED
) {
424 // Additionally verify the connection with a handshake.
425 SetConnectState(proto::CONN_STATE_AUTH_CHALLENGE_SEND
);
430 SetErrorState(CHANNEL_ERROR_AUTHENTICATION_ERROR
);
435 int CastSocketImpl::DoAuthChallengeSend() {
436 VLOG_WITH_CONNECTION(1) << "DoAuthChallengeSend";
437 SetConnectState(proto::CONN_STATE_AUTH_CHALLENGE_SEND_COMPLETE
);
439 CastMessage challenge_message
;
440 CreateAuthChallengeMessage(&challenge_message
);
441 VLOG_WITH_CONNECTION(1) << "Sending challenge: "
442 << CastMessageToString(challenge_message
);
444 transport_
->SendMessage(
446 base::Bind(&CastSocketImpl::DoConnectLoop
, base::Unretained(this)));
448 // Always return IO_PENDING since the result is always asynchronous.
449 return net::ERR_IO_PENDING
;
452 int CastSocketImpl::DoAuthChallengeSendComplete(int result
) {
453 VLOG_WITH_CONNECTION(1) << "DoAuthChallengeSendComplete: " << result
;
455 SetErrorState(CHANNEL_ERROR_SOCKET_ERROR
);
459 SetConnectState(proto::CONN_STATE_AUTH_CHALLENGE_REPLY_COMPLETE
);
460 return net::ERR_IO_PENDING
;
463 CastSocketImpl::AuthTransportDelegate::AuthTransportDelegate(
464 CastSocketImpl
* socket
)
469 void CastSocketImpl::AuthTransportDelegate::OnError(
470 ChannelError error_state
,
471 const LastErrors
& last_errors
) {
472 socket_
->SetErrorState(error_state
);
473 socket_
->PostTaskToStartConnectLoop(net::ERR_CONNECTION_FAILED
);
476 void CastSocketImpl::AuthTransportDelegate::OnMessage(
477 const CastMessage
& message
) {
478 if (!IsAuthMessage(message
)) {
479 socket_
->SetErrorState(CHANNEL_ERROR_TRANSPORT_ERROR
);
480 socket_
->PostTaskToStartConnectLoop(net::ERR_INVALID_RESPONSE
);
482 socket_
->challenge_reply_
.reset(new CastMessage(message
));
483 socket_
->logger_
->LogSocketEvent(socket_
->channel_id_
,
484 proto::RECEIVED_CHALLENGE_REPLY
);
485 socket_
->PostTaskToStartConnectLoop(net::OK
);
489 void CastSocketImpl::AuthTransportDelegate::Start() {
492 int CastSocketImpl::DoAuthChallengeReplyComplete(int result
) {
493 VLOG_WITH_CONNECTION(1) << "DoAuthChallengeReplyComplete: " << result
;
497 if (!VerifyChallengeReply()) {
498 SetErrorState(CHANNEL_ERROR_AUTHENTICATION_ERROR
);
499 return net::ERR_CONNECTION_FAILED
;
501 VLOG_WITH_CONNECTION(1) << "Auth challenge verification succeeded";
505 void CastSocketImpl::DoConnectCallback() {
506 VLOG(1) << "DoConnectCallback (error_state = " << error_state_
<< ")";
507 if (error_state_
== CHANNEL_ERROR_NONE
) {
508 SetReadyState(READY_STATE_OPEN
);
509 transport_
->SetReadDelegate(read_delegate_
.Pass());
511 SetReadyState(READY_STATE_CLOSED
);
514 base::ResetAndReturn(&connect_callback_
).Run(error_state_
);
517 void CastSocketImpl::Close(const net::CompletionCallback
& callback
) {
518 DCHECK(CalledOnValidThread());
520 // Run this callback last. It may delete the socket.
521 callback
.Run(net::OK
);
524 void CastSocketImpl::CloseInternal() {
525 // TODO(mfoltz): Enforce this when CastChannelAPITest is rewritten to create
526 // and free sockets on the same thread. crbug.com/398242
527 DCHECK(CalledOnValidThread());
528 if (ready_state_
== READY_STATE_CLOSED
) {
532 VLOG_WITH_CONNECTION(1) << "Close ReadyState = " << ready_state_
;
536 transport_security_state_
.reset();
541 // Cancel callbacks that we queued ourselves to re-enter the connect or read
543 connect_loop_callback_
.Cancel();
544 send_auth_challenge_callback_
.Cancel();
545 connect_timeout_callback_
.Cancel();
546 SetReadyState(READY_STATE_CLOSED
);
547 logger_
->LogSocketEvent(channel_id_
, proto::SOCKET_CLOSED
);
550 std::string
CastSocketImpl::cast_url() const {
551 return ((channel_auth_
== CHANNEL_AUTH_TYPE_SSL_VERIFIED
) ? "casts://"
553 ip_endpoint_
.ToString();
556 bool CastSocketImpl::CalledOnValidThread() const {
557 return thread_checker_
.CalledOnValidThread();
560 base::Timer
* CastSocketImpl::GetTimer() {
561 return connect_timeout_timer_
.get();
564 void CastSocketImpl::SetConnectState(proto::ConnectionState connect_state
) {
565 if (connect_state_
!= connect_state
) {
566 connect_state_
= connect_state
;
567 logger_
->LogSocketConnectState(channel_id_
, connect_state_
);
571 void CastSocketImpl::SetReadyState(ReadyState ready_state
) {
572 if (ready_state_
!= ready_state
) {
573 ready_state_
= ready_state
;
574 logger_
->LogSocketReadyState(channel_id_
, ReadyStateToProto(ready_state_
));
578 void CastSocketImpl::SetErrorState(ChannelError error_state
) {
579 VLOG_WITH_CONNECTION(1) << "SetErrorState " << error_state
;
580 DCHECK_EQ(CHANNEL_ERROR_NONE
, error_state_
);
581 error_state_
= error_state
;
582 logger_
->LogSocketErrorState(channel_id_
, ErrorStateToProto(error_state_
));
584 } // namespace cast_channel
585 } // namespace core_api
586 } // namespace extensions
587 #undef VLOG_WITH_CONNECTION