websocket_adapter.cc 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263
  1. // Copyright 2020 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.
  4. #include "device/fido/cable/websocket_adapter.h"
  5. #include "base/callback_helpers.h"
  6. #include "base/logging.h"
  7. #include "base/metrics/histogram_functions.h"
  8. #include "base/strings/string_number_conversions.h"
  9. #include "base/strings/string_util.h"
  10. #include "components/device_event_log/device_event_log.h"
  11. #include "device/fido/fido_constants.h"
  12. #include "net/http/http_status_code.h"
  13. namespace device {
  14. namespace cablev2 {
  15. // kMaxIncomingMessageSize is the maximum number of bytes in a single message
  16. // from a WebSocket. This is set to be far larger than any plausible CTAP2
  17. // message and exists to prevent a run away server from using up all memory.
  18. static constexpr size_t kMaxIncomingMessageSize = 1 << 20;
  19. WebSocketAdapter::WebSocketAdapter(TunnelReadyCallback on_tunnel_ready,
  20. TunnelDataCallback on_tunnel_data)
  21. : on_tunnel_ready_(std::move(on_tunnel_ready)),
  22. on_tunnel_data_(std::move(on_tunnel_data)),
  23. read_pipe_watcher_(FROM_HERE, mojo::SimpleWatcher::ArmingPolicy::MANUAL) {
  24. }
  25. WebSocketAdapter::~WebSocketAdapter() = default;
  26. mojo::PendingRemote<network::mojom::WebSocketHandshakeClient>
  27. WebSocketAdapter::BindNewHandshakeClientPipe() {
  28. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  29. auto ret = handshake_receiver_.BindNewPipeAndPassRemote();
  30. handshake_receiver_.set_disconnect_handler(base::BindOnce(
  31. &WebSocketAdapter::OnMojoPipeDisconnect, base::Unretained(this)));
  32. return ret;
  33. }
  34. bool WebSocketAdapter::Write(base::span<const uint8_t> data) {
  35. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  36. if (closed_ || data.size() > std::numeric_limits<uint32_t>::max()) {
  37. return false;
  38. }
  39. socket_remote_->SendMessage(network::mojom::WebSocketMessageType::BINARY,
  40. data.size());
  41. uint32_t num_bytes = static_cast<uint32_t>(data.size());
  42. MojoResult result = write_pipe_->WriteData(data.data(), &num_bytes,
  43. MOJO_WRITE_DATA_FLAG_ALL_OR_NONE);
  44. DCHECK(result != MOJO_RESULT_OK ||
  45. data.size() == static_cast<size_t>(num_bytes));
  46. return result == MOJO_RESULT_OK;
  47. }
  48. void WebSocketAdapter::Reparent(TunnelDataCallback on_tunnel_data) {
  49. DCHECK(!on_tunnel_ready_);
  50. on_tunnel_data_ = on_tunnel_data;
  51. }
  52. void WebSocketAdapter::OnOpeningHandshakeStarted(
  53. network::mojom::WebSocketHandshakeRequestPtr request) {
  54. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  55. }
  56. void WebSocketAdapter::OnFailure(const std::string& message,
  57. int net_error,
  58. int response_code) {
  59. LOG(ERROR) << "Tunnel server connection failed: " << message << " "
  60. << net_error << " " << response_code;
  61. base::UmaHistogramSparse("WebAuthentication.CableV2.TunnelServerError",
  62. response_code > 0 ? response_code : net_error);
  63. if (response_code != net::HTTP_GONE) {
  64. // The callback will be cleaned up when the pipe disconnects.
  65. return;
  66. }
  67. // This contact ID has been marked as inactive. The pairing information for
  68. // this device should be dropped.
  69. if (on_tunnel_ready_) {
  70. std::move(on_tunnel_ready_).Run(Result::GONE, absl::nullopt);
  71. // `this` may be invalid now.
  72. }
  73. }
  74. void WebSocketAdapter::OnConnectionEstablished(
  75. mojo::PendingRemote<network::mojom::WebSocket> socket,
  76. mojo::PendingReceiver<network::mojom::WebSocketClient> client_receiver,
  77. network::mojom::WebSocketHandshakeResponsePtr response,
  78. mojo::ScopedDataPipeConsumerHandle readable,
  79. mojo::ScopedDataPipeProducerHandle writable) {
  80. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  81. if (response->selected_protocol != kCableWebSocketProtocol) {
  82. FIDO_LOG(ERROR) << "Tunnel server didn't select cable protocol";
  83. return;
  84. }
  85. absl::optional<std::array<uint8_t, kRoutingIdSize>> routing_id;
  86. for (const auto& header : response->headers) {
  87. if (base::EqualsCaseInsensitiveASCII(header->name.c_str(),
  88. kCableRoutingIdHeader)) {
  89. if (routing_id.has_value() ||
  90. !base::HexStringToSpan(header->value, routing_id.emplace())) {
  91. FIDO_LOG(ERROR) << "Invalid routing ID from tunnel server: "
  92. << header->value;
  93. return;
  94. }
  95. }
  96. }
  97. socket_remote_.Bind(std::move(socket));
  98. read_pipe_ = std::move(readable);
  99. read_pipe_watcher_.Watch(
  100. read_pipe_.get(), MOJO_HANDLE_SIGNAL_READABLE,
  101. MOJO_TRIGGER_CONDITION_SIGNALS_SATISFIED,
  102. base::BindRepeating(&WebSocketAdapter::OnDataPipeReady,
  103. base::Unretained(this)));
  104. write_pipe_ = std::move(writable);
  105. client_receiver_.Bind(std::move(client_receiver));
  106. // |handshake_receiver_| will disconnect soon. In order to catch network
  107. // process crashes, we switch to watching |client_receiver_|.
  108. handshake_receiver_.set_disconnect_handler(base::DoNothing());
  109. client_receiver_.set_disconnect_handler(base::BindOnce(
  110. &WebSocketAdapter::OnMojoPipeDisconnect, base::Unretained(this)));
  111. socket_remote_->StartReceiving();
  112. std::move(on_tunnel_ready_).Run(Result::OK, routing_id);
  113. // `this` may be invalid now.
  114. }
  115. void WebSocketAdapter::OnDataFrame(bool finish,
  116. network::mojom::WebSocketMessageType type,
  117. uint64_t data_len) {
  118. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  119. DCHECK_EQ(pending_message_i_, pending_message_.size());
  120. DCHECK(!pending_message_finished_);
  121. if (data_len == 0) {
  122. if (finish) {
  123. FlushPendingMessage();
  124. }
  125. return;
  126. }
  127. const size_t old_size = pending_message_.size();
  128. const size_t new_size = old_size + data_len;
  129. if ((type != network::mojom::WebSocketMessageType::BINARY &&
  130. type != network::mojom::WebSocketMessageType::CONTINUATION) ||
  131. data_len > std::numeric_limits<uint32_t>::max() || new_size < old_size ||
  132. new_size > kMaxIncomingMessageSize) {
  133. FIDO_LOG(ERROR) << "invalid WebSocket frame (type: "
  134. << static_cast<int>(type) << ", len: " << data_len << ")";
  135. Close();
  136. return;
  137. }
  138. // The network process sends the |OnDataFrame| message before writing to
  139. // |read_pipe_|. Therefore we cannot depend on the message bytes being
  140. // immediately available in |read_pipe_| without a race. Thus
  141. // |read_pipe_watcher_| is used to wait for the data if needed.
  142. pending_message_.resize(new_size);
  143. pending_message_finished_ = finish;
  144. // Suspend more |OnDataFrame| callbacks until frame's data has been read. The
  145. // network service has successfully read |data_len| bytes before calling this
  146. // function so there's no I/O errors to worry about while reading; we know
  147. // that the bytes are coming.
  148. client_receiver_.Pause();
  149. OnDataPipeReady(MOJO_RESULT_OK, mojo::HandleSignalsState());
  150. }
  151. void WebSocketAdapter::OnDropChannel(bool was_clean,
  152. uint16_t code,
  153. const std::string& reason) {
  154. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  155. Close();
  156. }
  157. void WebSocketAdapter::OnClosingHandshake() {
  158. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  159. }
  160. void WebSocketAdapter::OnDataPipeReady(MojoResult,
  161. const mojo::HandleSignalsState&) {
  162. const size_t todo = pending_message_.size() - pending_message_i_;
  163. DCHECK_GT(todo, 0u);
  164. // Truncation to 32-bits cannot overflow because |pending_message_.size()| is
  165. // bound by |kMaxIncomingMessageSize| when it is resized in |OnDataFrame|.
  166. uint32_t todo_32 = static_cast<uint32_t>(todo);
  167. static_assert(
  168. kMaxIncomingMessageSize <= std::numeric_limits<decltype(todo_32)>::max(),
  169. "");
  170. const MojoResult result =
  171. read_pipe_->ReadData(&pending_message_.data()[pending_message_i_],
  172. &todo_32, MOJO_READ_DATA_FLAG_NONE);
  173. if (result == MOJO_RESULT_OK) {
  174. pending_message_i_ += todo_32;
  175. DCHECK_LE(pending_message_i_, pending_message_.size());
  176. if (pending_message_i_ < pending_message_.size()) {
  177. read_pipe_watcher_.Arm();
  178. } else {
  179. client_receiver_.Resume();
  180. if (pending_message_finished_) {
  181. FlushPendingMessage();
  182. }
  183. }
  184. } else if (result == MOJO_RESULT_SHOULD_WAIT) {
  185. read_pipe_watcher_.Arm();
  186. } else {
  187. FIDO_LOG(ERROR) << "reading WebSocket frame failed: "
  188. << static_cast<int>(result);
  189. Close();
  190. }
  191. }
  192. void WebSocketAdapter::OnMojoPipeDisconnect() {
  193. DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  194. // If disconnection happens before |OnConnectionEstablished| then report a
  195. // failure to establish the tunnel.
  196. if (on_tunnel_ready_) {
  197. std::move(on_tunnel_ready_).Run(Result::FAILED, absl::nullopt);
  198. // `this` may be invalid now.
  199. return;
  200. }
  201. // Otherwise, act as if the TLS connection was closed.
  202. if (!closed_) {
  203. Close();
  204. }
  205. }
  206. void WebSocketAdapter::Close() {
  207. DCHECK(!closed_);
  208. closed_ = true;
  209. client_receiver_.reset();
  210. on_tunnel_data_.Run(absl::nullopt);
  211. // `this` may be invalid now.
  212. }
  213. void WebSocketAdapter::FlushPendingMessage() {
  214. std::vector<uint8_t> message;
  215. message.swap(pending_message_);
  216. pending_message_i_ = 0;
  217. pending_message_finished_ = false;
  218. on_tunnel_data_.Run(message);
  219. // `this` may be invalid now.
  220. }
  221. } // namespace cablev2
  222. } // namespace device