Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2013 The Chromium Authors. All rights reserved. | 1 // Copyright 2013 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 "chrome/browser/extensions/api/cast_channel/cast_socket.h" | 5 #include "chrome/browser/extensions/api/cast_channel/cast_socket.h" |
| 6 | 6 |
| 7 #include <stdlib.h> | 7 #include <stdlib.h> |
| 8 #include <string.h> | 8 #include <string.h> |
| 9 | 9 |
| 10 #include "base/bind.h" | 10 #include "base/bind.h" |
| (...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 151 return false; | 151 return false; |
| 152 bool result = net::X509Certificate::GetDEREncoded( | 152 bool result = net::X509Certificate::GetDEREncoded( |
| 153 ssl_info.cert->os_cert_handle(), cert); | 153 ssl_info.cert->os_cert_handle(), cert); |
| 154 if (result) | 154 if (result) |
| 155 VLOG_WITH_CONNECTION(1) << "Successfully extracted peer certificate: " | 155 VLOG_WITH_CONNECTION(1) << "Successfully extracted peer certificate: " |
| 156 << *cert; | 156 << *cert; |
| 157 return result; | 157 return result; |
| 158 } | 158 } |
| 159 | 159 |
| 160 bool CastSocket::VerifyChallengeReply() { | 160 bool CastSocket::VerifyChallengeReply() { |
| 161 return AuthenticateChallengeReply(*challenge_reply_.get(), peer_cert_); | 161 return AuthenticateChallengeReply(*challenge_reply_, peer_cert_); |
| 162 } | 162 } |
| 163 | 163 |
| 164 void CastSocket::Connect(const net::CompletionCallback& callback) { | 164 void CastSocket::Connect(const net::CompletionCallback& callback) { |
| 165 DCHECK(CalledOnValidThread()); | 165 DCHECK(CalledOnValidThread()); |
| 166 VLOG_WITH_CONNECTION(1) << "Connect readyState = " << ready_state_; | 166 VLOG_WITH_CONNECTION(1) << "Connect readyState = " << ready_state_; |
| 167 if (ready_state_ != READY_STATE_NONE) { | 167 if (ready_state_ != READY_STATE_NONE) { |
| 168 callback.Run(net::ERR_CONNECTION_FAILED); | 168 callback.Run(net::ERR_CONNECTION_FAILED); |
| 169 return; | 169 return; |
| 170 } | 170 } |
| 171 ready_state_ = READY_STATE_CONNECTING; | 171 ready_state_ = READY_STATE_CONNECTING; |
| (...skipping 352 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 524 case READ_STATE_READ: | 524 case READ_STATE_READ: |
| 525 rv = DoRead(); | 525 rv = DoRead(); |
| 526 break; | 526 break; |
| 527 case READ_STATE_READ_COMPLETE: | 527 case READ_STATE_READ_COMPLETE: |
| 528 rv = DoReadComplete(rv); | 528 rv = DoReadComplete(rv); |
| 529 break; | 529 break; |
| 530 case READ_STATE_DO_CALLBACK: | 530 case READ_STATE_DO_CALLBACK: |
| 531 rv = DoReadCallback(); | 531 rv = DoReadCallback(); |
| 532 break; | 532 break; |
| 533 case READ_STATE_ERROR: | 533 case READ_STATE_ERROR: |
| 534 // DoReadError leaves the read_state_ as READ_STATE_NONE, | |
| 535 // which is an exit criteria for the finite state machine. | |
|
Wez
2014/07/22 22:33:10
This comment is really stipulating an invariant of
Kevin M
2014/07/23 18:57:33
:) Good idea. Done.
| |
| 534 rv = DoReadError(rv); | 536 rv = DoReadError(rv); |
| 535 break; | 537 break; |
| 536 default: | 538 default: |
| 537 NOTREACHED() << "BUG in read flow. Unknown state: " << state; | 539 NOTREACHED() << "BUG in read flow. Unknown state: " << state; |
| 538 break; | 540 break; |
| 539 } | 541 } |
| 540 } while (rv != net::ERR_IO_PENDING && read_state_ != READ_STATE_NONE); | 542 } while (rv != net::ERR_IO_PENDING && read_state_ != READ_STATE_NONE); |
| 541 | 543 |
| 542 // Read loop is done - If the result is ERR_FAILED then close with error. | 544 if (rv == net::ERR_FAILED) { |
| 543 if (rv == net::ERR_FAILED) | 545 // Handle errors that might have occurred. |
|
Wez
2014/07/22 22:33:09
nit: This comment is superfluous.
Kevin M
2014/07/23 18:57:33
Done.
| |
| 544 CloseWithError(error_state_); | 546 if (ready_state_ == READY_STATE_CONNECTING) { |
| 547 // Connection not yet established. | |
| 548 // Clean up without triggering the message event delegate (error | |
| 549 // will be sent on the connect cb instead.) | |
|
Wez
2014/07/22 22:33:09
nit: Suggest:
"Read errors during the connection h
Kevin M
2014/07/23 18:57:33
Done.
| |
| 550 PostTaskToStartConnectLoop(net::ERR_FAILED); | |
| 551 } else { | |
| 552 // Connection is already established. | |
| 553 // Close and send error status via the message event delegate. | |
| 554 CloseWithError(error_state_); | |
| 555 } | |
| 556 } | |
| 545 } | 557 } |
| 546 | 558 |
| 547 int CastSocket::DoRead() { | 559 int CastSocket::DoRead() { |
| 548 read_state_ = READ_STATE_READ_COMPLETE; | 560 read_state_ = READ_STATE_READ_COMPLETE; |
| 549 // Figure out whether to read header or body, and the remaining bytes. | 561 // Figure out whether to read header or body, and the remaining bytes. |
| 550 uint32 num_bytes_to_read = 0; | 562 uint32 num_bytes_to_read = 0; |
| 551 if (header_read_buffer_->RemainingCapacity() > 0) { | 563 if (header_read_buffer_->RemainingCapacity() > 0) { |
| 552 current_read_buffer_ = header_read_buffer_; | 564 current_read_buffer_ = header_read_buffer_; |
| 553 num_bytes_to_read = header_read_buffer_->RemainingCapacity(); | 565 num_bytes_to_read = header_read_buffer_->RemainingCapacity(); |
| 554 DCHECK_LE(num_bytes_to_read, MessageHeader::header_size()); | 566 CHECK_LE(num_bytes_to_read, MessageHeader::header_size()); |
| 555 } else { | 567 } else { |
| 556 DCHECK_GT(current_message_size_, 0U); | 568 DCHECK_GT(current_message_size_, 0U); |
| 557 num_bytes_to_read = current_message_size_ - body_read_buffer_->offset(); | 569 num_bytes_to_read = current_message_size_ - body_read_buffer_->offset(); |
| 558 current_read_buffer_ = body_read_buffer_; | 570 current_read_buffer_ = body_read_buffer_; |
| 559 DCHECK_LE(num_bytes_to_read, MessageHeader::max_message_size()); | 571 CHECK_LE(num_bytes_to_read, MessageHeader::max_message_size()); |
|
Ryan Sleevi
2014/07/22 22:52:43
BUG? It seems to me that the attacker has full con
Wez
2014/07/23 05:22:00
Exactly; see my suggestion re the NOTREACHED() + f
Kevin M
2014/07/23 18:57:33
Done.
Kevin M
2014/07/23 18:57:34
Acknowledged.
| |
| 560 } | 572 } |
| 561 DCHECK_GT(num_bytes_to_read, 0U); | 573 CHECK_GT(num_bytes_to_read, 0U); |
| 562 | 574 |
| 563 // Read up to num_bytes_to_read into |current_read_buffer_|. | 575 // Read up to num_bytes_to_read into |current_read_buffer_|. |
| 564 return socket_->Read( | 576 return socket_->Read( |
| 565 current_read_buffer_.get(), | 577 current_read_buffer_.get(), |
| 566 num_bytes_to_read, | 578 num_bytes_to_read, |
| 567 base::Bind(&CastSocket::DoReadLoop, AsWeakPtr())); | 579 base::Bind(&CastSocket::DoReadLoop, AsWeakPtr())); |
| 568 } | 580 } |
| 569 | 581 |
| 570 int CastSocket::DoReadComplete(int result) { | 582 int CastSocket::DoReadComplete(int result) { |
| 571 VLOG_WITH_CONNECTION(2) << "DoReadComplete result = " << result | 583 VLOG_WITH_CONNECTION(2) << "DoReadComplete result = " << result |
| 572 << " header offset = " | 584 << " header offset = " |
| 573 << header_read_buffer_->offset() | 585 << header_read_buffer_->offset() |
| 574 << " body offset = " << body_read_buffer_->offset(); | 586 << " body offset = " << body_read_buffer_->offset(); |
| 575 if (result <= 0) { // 0 means EOF: the peer closed the socket | 587 if (result <= 0) { // 0 means EOF: the peer closed the socket |
| 576 VLOG_WITH_CONNECTION(1) << "Read error, peer closed the socket"; | 588 VLOG_WITH_CONNECTION(1) << "Read error, peer closed the socket"; |
| 577 error_state_ = CHANNEL_ERROR_SOCKET_ERROR; | 589 error_state_ = CHANNEL_ERROR_SOCKET_ERROR; |
| 578 read_state_ = READ_STATE_ERROR; | 590 read_state_ = READ_STATE_ERROR; |
| 579 return result == 0 ? net::ERR_FAILED : result; | 591 return result == 0 ? net::ERR_FAILED : result; |
| 580 } | 592 } |
| 581 | 593 |
| 582 // Some data was read. Move the offset in the current buffer forward. | 594 // Some data was read. Move the offset in the current buffer forward. |
| 583 DCHECK_LE(current_read_buffer_->offset() + result, | 595 CHECK_LE(current_read_buffer_->offset() + result, |
| 584 current_read_buffer_->capacity()); | 596 current_read_buffer_->capacity()); |
| 585 current_read_buffer_->set_offset(current_read_buffer_->offset() + result); | 597 current_read_buffer_->set_offset(current_read_buffer_->offset() + result); |
| 586 read_state_ = READ_STATE_READ; | 598 read_state_ = READ_STATE_READ; |
| 587 | 599 |
| 588 if (current_read_buffer_.get() == header_read_buffer_.get() && | 600 if (current_read_buffer_.get() == header_read_buffer_.get() && |
| 589 current_read_buffer_->RemainingCapacity() == 0) { | 601 current_read_buffer_->RemainingCapacity() == 0) { |
| 590 // A full header is read, process the contents. | 602 // A full header is read, process the contents. |
| 591 if (!ProcessHeader()) { | 603 if (!ProcessHeader()) { |
| 592 error_state_ = cast_channel::CHANNEL_ERROR_INVALID_MESSAGE; | 604 error_state_ = cast_channel::CHANNEL_ERROR_INVALID_MESSAGE; |
| 593 read_state_ = READ_STATE_ERROR; | 605 read_state_ = READ_STATE_ERROR; |
| 594 } | 606 } |
| 595 } else if (current_read_buffer_.get() == body_read_buffer_.get() && | 607 } else if (current_read_buffer_.get() == body_read_buffer_.get() && |
| 596 static_cast<uint32>(current_read_buffer_->offset()) == | 608 static_cast<uint32>(current_read_buffer_->offset()) == |
| 597 current_message_size_) { | 609 current_message_size_) { |
| 598 // Full body is read, process the contents. | 610 // Full body is read, process the contents. |
| 599 if (ProcessBody()) { | 611 if (ProcessBody()) { |
| 600 read_state_ = READ_STATE_DO_CALLBACK; | 612 read_state_ = READ_STATE_DO_CALLBACK; |
| 601 } else { | 613 } else { |
| 602 error_state_ = cast_channel::CHANNEL_ERROR_INVALID_MESSAGE; | 614 error_state_ = cast_channel::CHANNEL_ERROR_INVALID_MESSAGE; |
| 603 read_state_ = READ_STATE_ERROR; | 615 read_state_ = READ_STATE_ERROR; |
| 604 } | 616 } |
| 605 } | 617 } |
| 606 | 618 |
| 607 return net::OK; | 619 return net::OK; |
| 608 } | 620 } |
| 609 | 621 |
| 610 int CastSocket::DoReadCallback() { | 622 int CastSocket::DoReadCallback() { |
| 611 read_state_ = READ_STATE_READ; | 623 read_state_ = READ_STATE_READ; |
| 612 const CastMessage& message = *(current_message_.get()); | 624 const CastMessage& message = *current_message_; |
| 613 if (IsAuthMessage(message)) { | 625 if (ready_state_ == READY_STATE_CONNECTING) { |
| 614 // An auth message is received, check that connect flow is running. | 626 if (IsAuthMessage(message)) { |
| 615 if (ready_state_ == READY_STATE_CONNECTING) { | |
| 616 challenge_reply_.reset(new CastMessage(message)); | 627 challenge_reply_.reset(new CastMessage(message)); |
| 617 PostTaskToStartConnectLoop(net::OK); | 628 PostTaskToStartConnectLoop(net::OK); |
| 629 return net::OK; | |
| 618 } else { | 630 } else { |
| 631 // Expected an auth message, got something else instead. Handle as error. | |
| 619 read_state_ = READ_STATE_ERROR; | 632 read_state_ = READ_STATE_ERROR; |
| 633 return net::ERR_INVALID_RESPONSE; | |
| 620 } | 634 } |
| 621 } else if (delegate_) { | |
| 622 MessageInfo message_info; | |
| 623 if (CastMessageToMessageInfo(message, &message_info)) | |
| 624 delegate_->OnMessage(this, message_info); | |
| 625 else | |
| 626 read_state_ = READ_STATE_ERROR; | |
| 627 } | 635 } |
| 636 | |
| 637 MessageInfo message_info; | |
| 638 if (!CastMessageToMessageInfo(message, &message_info)) { | |
| 639 read_state_ = READ_STATE_ERROR; | |
| 640 return net::ERR_INVALID_RESPONSE; | |
|
Wez
2014/07/22 22:33:09
Is it a problem that we're no longer clearing |cur
Kevin M
2014/07/23 18:57:33
Re-added that behavior.
| |
| 641 } | |
| 642 delegate_->OnMessage(this, message_info); | |
|
Ryan Sleevi
2014/07/22 22:52:43
Is the delegate_ allowed to delete this?
If so, l
Kevin M
2014/07/23 18:57:33
No, the delegate is not allowed to delete this dur
| |
| 628 current_message_->Clear(); | 643 current_message_->Clear(); |
| 629 return net::OK; | 644 return net::OK; |
| 630 } | 645 } |
| 631 | 646 |
| 632 int CastSocket::DoReadError(int result) { | 647 int CastSocket::DoReadError(int result) { |
| 648 VLOG_WITH_CONNECTION(1) << "DoReadError: " << result; | |
|
Ryan Sleevi
2014/07/22 22:52:43
Spammy. VLOG(2) if you have to. But really, you sh
Kevin M
2014/07/23 18:57:33
Done.
| |
| 633 DCHECK_LE(result, 0); | 649 DCHECK_LE(result, 0); |
| 634 // If inside connection flow, then get back to connect loop. | |
| 635 if (ready_state_ == READY_STATE_CONNECTING) { | |
| 636 PostTaskToStartConnectLoop(result); | |
| 637 // does not try to report error also. | |
| 638 return net::OK; | |
| 639 } | |
| 640 return net::ERR_FAILED; | 650 return net::ERR_FAILED; |
| 641 } | 651 } |
| 642 | 652 |
| 643 bool CastSocket::ProcessHeader() { | 653 bool CastSocket::ProcessHeader() { |
| 644 DCHECK_EQ(static_cast<uint32>(header_read_buffer_->offset()), | 654 CHECK_EQ(static_cast<uint32>(header_read_buffer_->offset()), |
| 645 MessageHeader::header_size()); | 655 MessageHeader::header_size()); |
| 646 MessageHeader header; | 656 MessageHeader header; |
| 647 MessageHeader::ReadFromIOBuffer(header_read_buffer_.get(), &header); | 657 MessageHeader::ReadFromIOBuffer(header_read_buffer_.get(), &header); |
| 648 if (header.message_size > MessageHeader::max_message_size()) | 658 if (header.message_size > MessageHeader::max_message_size()) |
| 649 return false; | 659 return false; |
| 650 | 660 |
| 651 VLOG_WITH_CONNECTION(2) << "Parsed header { message_size: " | 661 VLOG_WITH_CONNECTION(2) << "Parsed header { message_size: " |
| 652 << header.message_size << " }"; | 662 << header.message_size << " }"; |
| 653 current_message_size_ = header.message_size; | 663 current_message_size_ = header.message_size; |
| 654 return true; | 664 return true; |
| 655 } | 665 } |
| 656 | 666 |
| 657 bool CastSocket::ProcessBody() { | 667 bool CastSocket::ProcessBody() { |
| 658 DCHECK_EQ(static_cast<uint32>(body_read_buffer_->offset()), | 668 CHECK_EQ(static_cast<uint32>(body_read_buffer_->offset()), |
| 659 current_message_size_); | 669 current_message_size_); |
|
Ryan Sleevi
2014/07/22 22:52:43
SECURITY BUG: Crash on hostile input.
Kevin M
2014/07/23 18:57:33
Done.
| |
| 660 if (!current_message_->ParseFromArray( | 670 if (!current_message_->ParseFromArray( |
| 661 body_read_buffer_->StartOfBuffer(), current_message_size_)) { | 671 body_read_buffer_->StartOfBuffer(), current_message_size_)) { |
| 662 return false; | 672 return false; |
| 663 } | 673 } |
| 664 current_message_size_ = 0; | 674 current_message_size_ = 0; |
| 665 header_read_buffer_->set_offset(0); | 675 header_read_buffer_->set_offset(0); |
| 666 body_read_buffer_->set_offset(0); | 676 body_read_buffer_->set_offset(0); |
| 667 current_read_buffer_ = header_read_buffer_; | 677 current_read_buffer_ = header_read_buffer_; |
| 668 return true; | 678 return true; |
| 669 } | 679 } |
| 670 | 680 |
| 671 // static | 681 // static |
| 672 bool CastSocket::Serialize(const CastMessage& message_proto, | 682 bool CastSocket::Serialize(const CastMessage& message_proto, |
| 673 std::string* message_data) { | 683 std::string* message_data) { |
| 674 DCHECK(message_data); | 684 DCHECK(message_data); |
| 675 message_proto.SerializeToString(message_data); | 685 message_proto.SerializeToString(message_data); |
| 676 size_t message_size = message_data->size(); | 686 size_t message_size = message_data->size(); |
| 677 if (message_size > MessageHeader::max_message_size()) { | 687 if (message_size > MessageHeader::max_message_size()) { |
| 678 message_data->clear(); | 688 message_data->clear(); |
| 679 return false; | 689 return false; |
| 680 } | 690 } |
| 681 CastSocket::MessageHeader header; | 691 CastSocket::MessageHeader header; |
| 682 header.SetMessageSize(message_size); | 692 header.SetMessageSize(message_size); |
| 683 header.PrependToString(message_data); | 693 header.PrependToString(message_data); |
| 684 return true; | 694 return true; |
| 685 }; | 695 } |
| 686 | 696 |
| 687 void CastSocket::CloseWithError(ChannelError error) { | 697 void CastSocket::CloseWithError(ChannelError error) { |
| 688 DCHECK(CalledOnValidThread()); | 698 DCHECK(CalledOnValidThread()); |
| 689 socket_.reset(NULL); | 699 socket_.reset(NULL); |
| 690 ready_state_ = READY_STATE_CLOSED; | 700 ready_state_ = READY_STATE_CLOSED; |
| 691 error_state_ = error; | 701 error_state_ = error; |
| 692 if (delegate_) | 702 if (delegate_) |
| 693 delegate_->OnError(this, error); | 703 delegate_->OnError(this, error); |
| 694 } | 704 } |
| 695 | 705 |
| 696 std::string CastSocket::CastUrl() const { | 706 std::string CastSocket::CastUrl() const { |
| 697 return ((channel_auth_ == CHANNEL_AUTH_TYPE_SSL_VERIFIED) ? | 707 return ((channel_auth_ == CHANNEL_AUTH_TYPE_SSL_VERIFIED) ? |
| 698 "casts://" : "cast://") + ip_endpoint_.ToString(); | 708 "casts://" : "cast://") + ip_endpoint_.ToString(); |
| 699 } | 709 } |
| 700 | 710 |
| 701 bool CastSocket::CalledOnValidThread() const { | 711 bool CastSocket::CalledOnValidThread() const { |
| 702 return thread_checker_.CalledOnValidThread(); | 712 return thread_checker_.CalledOnValidThread(); |
| 703 } | 713 } |
| 704 | 714 |
| 705 CastSocket::MessageHeader::MessageHeader() : message_size(0) { } | 715 CastSocket::MessageHeader::MessageHeader() : message_size(0) { } |
| 706 | 716 |
| 707 void CastSocket::MessageHeader::SetMessageSize(size_t size) { | 717 void CastSocket::MessageHeader::SetMessageSize(size_t size) { |
| 708 DCHECK(size < static_cast<size_t>(kuint32max)); | 718 CHECK(size < static_cast<size_t>(kuint32max)); |
| 709 DCHECK(size > 0); | 719 CHECK(size > 0); |
|
Ryan Sleevi
2014/07/22 22:52:43
CHECK_LT
CHECK_GT
SECURITY_BUG? Crash on hostile
Kevin M
2014/07/23 18:57:33
Now returning a bool, which eventually yields an e
| |
| 710 message_size = static_cast<size_t>(size); | 720 message_size = static_cast<size_t>(size); |
| 711 } | 721 } |
| 712 | 722 |
| 713 // TODO(mfoltz): Investigate replacing header serialization with base::Pickle, | 723 // TODO(mfoltz): Investigate replacing header serialization with base::Pickle, |
| 714 // if bit-for-bit compatible. | 724 // if bit-for-bit compatible. |
| 715 void CastSocket::MessageHeader::PrependToString(std::string* str) { | 725 void CastSocket::MessageHeader::PrependToString(std::string* str) { |
| 716 MessageHeader output = *this; | 726 MessageHeader output = *this; |
| 717 output.message_size = base::HostToNet32(message_size); | 727 output.message_size = base::HostToNet32(message_size); |
| 718 size_t header_size = base::checked_cast<size_t,uint32>( | 728 size_t header_size = base::checked_cast<size_t,uint32>( |
| 719 MessageHeader::header_size()); | 729 MessageHeader::header_size()); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 751 return true; | 761 return true; |
| 752 } | 762 } |
| 753 | 763 |
| 754 CastSocket::WriteRequest::~WriteRequest() { } | 764 CastSocket::WriteRequest::~WriteRequest() { } |
| 755 | 765 |
| 756 } // namespace cast_channel | 766 } // namespace cast_channel |
| 757 } // namespace api | 767 } // namespace api |
| 758 } // namespace extensions | 768 } // namespace extensions |
| 759 | 769 |
| 760 #undef VLOG_WITH_CONNECTION | 770 #undef VLOG_WITH_CONNECTION |
| OLD | NEW |