Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(443)

Side by Side Diff: chrome/browser/extensions/api/cast_channel/cast_socket.cc

Issue 408633002: Fix error handling for message parse errors that occur during connection setup. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Misc. changes Created 6 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « no previous file | chrome/browser/extensions/api/cast_channel/cast_socket_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
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
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
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
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
OLDNEW
« no previous file with comments | « no previous file | chrome/browser/extensions/api/cast_channel/cast_socket_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698