File: remote_webauthn_message_handler.cc

package info (click to toggle)
chromium 139.0.7258.127-1
  • links: PTS, VCS
  • area: main
  • in suites:
  • size: 6,122,068 kB
  • sloc: cpp: 35,100,771; ansic: 7,163,530; javascript: 4,103,002; python: 1,436,920; asm: 946,517; xml: 746,709; pascal: 187,653; perl: 88,691; sh: 88,436; objc: 79,953; sql: 51,488; cs: 44,583; fortran: 24,137; makefile: 22,147; tcl: 15,277; php: 13,980; yacc: 8,984; ruby: 7,485; awk: 3,720; lisp: 3,096; lex: 1,327; ada: 727; jsp: 228; sed: 36
file content (372 lines) | stat: -rw-r--r-- 13,166 bytes parent folder | download | duplicates (7)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
// Copyright 2021 The Chromium Authors
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "remoting/host/webauthn/remote_webauthn_message_handler.h"

#include <stdint.h>

#include <limits>

#include "base/containers/contains.h"
#include "base/functional/bind.h"
#include "base/functional/callback_helpers.h"
#include "base/logging.h"
#include "base/notreached.h"
#include "remoting/host/mojom/webauthn_proxy.mojom.h"
#include "remoting/host/webauthn/remote_webauthn_state_change_notifier.h"
#include "remoting/proto/remote_webauthn.pb.h"
#include "remoting/protocol/message_serialization.h"

namespace remoting {

namespace {

template <typename CallbackType, typename... ResponseType>
void FindAndRunCallback(base::flat_map<uint64_t, CallbackType>& callback_map,
                        uint64_t id,
                        ResponseType&&... response) {
  auto it = callback_map.find(id);
  if (it == callback_map.end()) {
    LOG(WARNING) << "No callback found associated with ID: " << id;
    return;
  }
  std::move(it->second).Run(std::forward<ResponseType>(response)...);
  callback_map.erase(it);
}

mojom::WebAuthnExceptionDetailsPtr ProtobufErrorToMojoError(
    const protocol::RemoteWebAuthn::ExceptionDetails& pb_error) {
  auto mojo_error = mojom::WebAuthnExceptionDetails::New();
  mojo_error->name = pb_error.name();
  mojo_error->message = pb_error.message();
  return mojo_error;
}

mojom::WebAuthnExceptionDetailsPtr CreateMojoAbortError() {
  auto mojo_error = mojom::WebAuthnExceptionDetails::New();
  mojo_error->name = "AbortError";
  mojo_error->message = "Request has been canceled by the host.";
  return mojo_error;
}

}  // namespace

RemoteWebAuthnMessageHandler::RemoteWebAuthnMessageHandler(
    const std::string& name,
    std::unique_ptr<protocol::MessagePipe> pipe,
    std::unique_ptr<RemoteWebAuthnStateChangeNotifier> state_change_notifier)
    : protocol::NamedMessagePipeHandler(name, std::move(pipe)) {
  state_change_notifier_ = std::move(state_change_notifier);
  receiver_set_.set_disconnect_handler(
      base::BindRepeating(&RemoteWebAuthnMessageHandler::OnReceiverDisconnected,
                          base::Unretained(this)));
  request_cancellers_.set_disconnect_handler(base::BindRepeating(
      &RemoteWebAuthnMessageHandler::OnRequestCancellerDisconnected,
      base::Unretained(this)));
}

RemoteWebAuthnMessageHandler::~RemoteWebAuthnMessageHandler() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);
  DCHECK(!connected());
}

void RemoteWebAuthnMessageHandler::OnConnected() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  NotifyWebAuthnStateChange();
}

void RemoteWebAuthnMessageHandler::OnIncomingMessage(
    std::unique_ptr<CompoundBuffer> message) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  auto remote_webauthn =
      protocol::ParseMessage<protocol::RemoteWebAuthn>(message.get());
  if (!remote_webauthn->has_id()) {
    LOG(ERROR) << "Response doesn't have a message ID.";
    return;
  }
  switch (remote_webauthn->message_case()) {
    case protocol::RemoteWebAuthn::kIsUvpaaResponse:
      OnIsUvpaaResponse(remote_webauthn->id(),
                        remote_webauthn->is_uvpaa_response());
      break;
    case protocol::RemoteWebAuthn::kCreateResponse:
      OnCreateResponse(remote_webauthn->id(),
                       remote_webauthn->create_response());
      break;
    case protocol::RemoteWebAuthn::kGetResponse:
      OnGetResponse(remote_webauthn->id(), remote_webauthn->get_response());
      break;
    case protocol::RemoteWebAuthn::kCancelResponse:
      OnCancelResponse(remote_webauthn->id(),
                       remote_webauthn->cancel_response());
      break;
    default:
      LOG(ERROR) << "Unknown message case: " << remote_webauthn->message_case();
  }
}

void RemoteWebAuthnMessageHandler::OnDisconnecting() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  // Run mojo callbacks with error/default response then clean them up.
  for (auto& entry : is_uvpaa_callbacks_) {
    std::move(entry.second).Run(false);
  }
  is_uvpaa_callbacks_.clear();
  VLOG(1) << "Number of bound receivers on disconnecting: "
          << receiver_set_.size();
  receiver_set_.Clear();

  NotifyWebAuthnStateChange();
}

void RemoteWebAuthnMessageHandler::
    IsUserVerifyingPlatformAuthenticatorAvailable(
        IsUserVerifyingPlatformAuthenticatorAvailableCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  uint64_t id = AssignNextMessageId();
  is_uvpaa_callbacks_[id] = std::move(callback);

  protocol::RemoteWebAuthn remote_webauthn;
  remote_webauthn.set_id(id);
  // This simply creates the is_uvpaa_request.
  remote_webauthn.mutable_is_uvpaa_request();
  Send(remote_webauthn, base::DoNothing());
}

void RemoteWebAuthnMessageHandler::Create(
    const std::string& request_data,
    mojo::PendingReceiver<mojom::WebAuthnRequestCanceller> request_canceller,
    CreateCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  uint64_t id = AssignNextMessageId();
  create_callbacks_[id] = std::move(callback);
  AddRequestCanceller(id, std::move(request_canceller));

  protocol::RemoteWebAuthn remote_webauthn;
  remote_webauthn.set_id(id);
  remote_webauthn.mutable_create_request()->set_request_details_json(
      request_data);
  Send(remote_webauthn, base::DoNothing());
}

void RemoteWebAuthnMessageHandler::Get(
    const std::string& request_data,
    mojo::PendingReceiver<mojom::WebAuthnRequestCanceller> request_canceller,
    GetCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  uint64_t id = AssignNextMessageId();
  get_callbacks_[id] = std::move(callback);
  AddRequestCanceller(id, std::move(request_canceller));

  protocol::RemoteWebAuthn remote_webauthn;
  remote_webauthn.set_id(id);
  remote_webauthn.mutable_get_request()->set_request_details_json(request_data);
  Send(remote_webauthn, base::DoNothing());
}

void RemoteWebAuthnMessageHandler::Cancel(CancelCallback callback) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  uint64_t id = request_cancellers_.current_context();

  if (!base::Contains(create_callbacks_, id) &&
      !base::Contains(get_callbacks_, id)) {
    LOG(ERROR) << "No ongoing request is associated with message ID " << id;
    std::move(callback).Run(false);
    RemoveRequestCancellerByMessageId(id);
    return;
  }

  cancel_callbacks_[id] = std::move(callback);

  protocol::RemoteWebAuthn remote_webauthn;
  remote_webauthn.set_id(id);
  // This creates an empty cancel request.
  remote_webauthn.mutable_cancel_request();
  Send(remote_webauthn, base::DoNothing());
}

void RemoteWebAuthnMessageHandler::AddReceiver(
    mojo::PendingReceiver<mojom::WebAuthnProxy> receiver) {
  if (!connected()) {
    LOG(WARNING)
        << "Pending receiver rejected since message handler is not connected.";
    return;
  }
  mojo::ReceiverId id = receiver_set_.Add(this, std::move(receiver));
  VLOG(1) << "New receiver added. Receiver ID: " << id;
}

void RemoteWebAuthnMessageHandler::ClearReceivers() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  receiver_set_.Clear();
  request_cancellers_.Clear();
  message_id_to_request_canceller_.clear();
  is_uvpaa_callbacks_.clear();
  create_callbacks_.clear();
  get_callbacks_.clear();
  cancel_callbacks_.clear();
}

void RemoteWebAuthnMessageHandler::NotifyWebAuthnStateChange() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  state_change_notifier_->NotifyStateChange();
}

base::WeakPtr<RemoteWebAuthnMessageHandler>
RemoteWebAuthnMessageHandler::GetWeakPtr() {
  return weak_factory_.GetWeakPtr();
}

void RemoteWebAuthnMessageHandler::OnReceiverDisconnected() {
  VLOG(1) << "Receiver disconnected. Receiver ID: "
          << receiver_set_.current_receiver();
}

void RemoteWebAuthnMessageHandler::OnIsUvpaaResponse(
    uint64_t id,
    const protocol::RemoteWebAuthn_IsUvpaaResponse& response) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  FindAndRunCallback(is_uvpaa_callbacks_, id, response.is_available());
}

void RemoteWebAuthnMessageHandler::OnCreateResponse(
    uint64_t id,
    const protocol::RemoteWebAuthn_CreateResponse& response) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  mojom::WebAuthnCreateResponsePtr mojo_response;
  switch (response.result_case()) {
    case protocol::RemoteWebAuthn::CreateResponse::ResultCase::kError:
      mojo_response = mojom::WebAuthnCreateResponse::NewErrorDetails(
          ProtobufErrorToMojoError(response.error()));
      break;
    case protocol::RemoteWebAuthn::CreateResponse::ResultCase::kResponseJson:
      mojo_response = mojom::WebAuthnCreateResponse::NewResponseData(
          response.response_json());
      break;
    case protocol::RemoteWebAuthn::CreateResponse::ResultCase::RESULT_NOT_SET:
      // Do nothing and send a nullptr to the mojo client. This means the remote
      // create() call has yielded `null`, which is still a valid response
      // according to the spec.
      break;
    default:
      NOTREACHED() << "Unknown create result case: " << response.result_case();
  }

  RemoveRequestCancellerByMessageId(id);
  FindAndRunCallback(create_callbacks_, id, std::move(mojo_response));
}

void RemoteWebAuthnMessageHandler::OnGetResponse(
    uint64_t id,
    const protocol::RemoteWebAuthn_GetResponse& response) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  mojom::WebAuthnGetResponsePtr mojo_response;
  switch (response.result_case()) {
    case protocol::RemoteWebAuthn::GetResponse::ResultCase::kError:
      mojo_response = mojom::WebAuthnGetResponse::NewErrorDetails(
          ProtobufErrorToMojoError(response.error()));
      break;
    case protocol::RemoteWebAuthn::GetResponse::ResultCase::kResponseJson:
      mojo_response =
          mojom::WebAuthnGetResponse::NewResponseData(response.response_json());
      break;
    case protocol::RemoteWebAuthn::GetResponse::ResultCase::RESULT_NOT_SET:
      // Do nothing and send a nullptr to the mojo client. This means the remote
      // get() call has yielded `null`, which is still a valid response
      // according to the spec.
      break;
    default:
      NOTREACHED() << "Unknown get result case: " << response.result_case();
  }

  RemoveRequestCancellerByMessageId(id);
  FindAndRunCallback(get_callbacks_, id, std::move(mojo_response));
}

void RemoteWebAuthnMessageHandler::OnCancelResponse(
    uint64_t id,
    const protocol::RemoteWebAuthn_CancelResponse& response) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  if (!response.was_canceled()) {
    LOG(WARNING) << "Client failed to cancel request with ID " << id;
    FindAndRunCallback(cancel_callbacks_, id, /* was_canceled= */ false);
    // Don't remove request canceller here since cancelation might succeed after
    // retrying.
    return;
  }

  bool cancelling_create_request = base::Contains(create_callbacks_, id);
  bool cancelling_get_request = base::Contains(get_callbacks_, id);

  if (cancelling_create_request || cancelling_get_request) {
    FindAndRunCallback(cancel_callbacks_, id, /* was_canceled= */ true);
    if (cancelling_create_request) {
      FindAndRunCallback(create_callbacks_, id,
                         mojom::WebAuthnCreateResponse::NewErrorDetails(
                             CreateMojoAbortError()));
    }
    if (cancelling_get_request) {
      // The ID should belong to only one callback list.
      DCHECK(!cancelling_create_request);
      FindAndRunCallback(
          get_callbacks_, id,
          mojom::WebAuthnGetResponse::NewErrorDetails(CreateMojoAbortError()));
    }
    RemoveRequestCancellerByMessageId(id);
    return;
  }

  LOG(WARNING) << "Can't find cancelable request associated with ID " << id;
  FindAndRunCallback(cancel_callbacks_, id, /* was_canceled= */ false);
  RemoveRequestCancellerByMessageId(id);
}

uint64_t RemoteWebAuthnMessageHandler::AssignNextMessageId() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  return current_message_id_++;
}

void RemoteWebAuthnMessageHandler::AddRequestCanceller(
    uint64_t message_id,
    mojo::PendingReceiver<mojom::WebAuthnRequestCanceller> request_canceller) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  message_id_to_request_canceller_[message_id] = request_cancellers_.Add(
      this, std::move(request_canceller), /* context= */ message_id);
}

void RemoteWebAuthnMessageHandler::RemoveRequestCancellerByMessageId(
    uint64_t message_id) {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  auto it = message_id_to_request_canceller_.find(message_id);
  if (it != message_id_to_request_canceller_.end()) {
    request_cancellers_.Remove(it->second);
    message_id_to_request_canceller_.erase(it);
  } else {
    LOG(WARNING) << "Cannot find receiver for message ID " << message_id;
  }
}

void RemoteWebAuthnMessageHandler::OnRequestCancellerDisconnected() {
  DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_);

  message_id_to_request_canceller_.erase(request_cancellers_.current_context());
}

}  // namespace remoting