blob: 8707b5b9fa80d3564872f8741b5381d7ca99ac70 [file] [log] [blame]
Avi Drissman4a8573c2022-09-09 19:35:541// Copyright 2016 The Chromium Authors
igorcovbb898fb2016-12-01 16:07:562// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
Oleksandr Kulkov9c7cf9552022-06-02 12:25:435#include "chrome/browser/certificate_provider/pin_dialog_manager.h"
igorcovbb898fb2016-12-01 16:07:566
Jan Wilken Dörrieb5a41c32020-12-09 18:55:477#include "base/containers/contains.h"
Lei Zhanga64664b2021-06-30 16:09:388#include "base/containers/cxx20_erase.h"
Avi Drissman02e49e582023-01-07 01:23:189#include "base/functional/bind.h"
Maksim Ivanov73dfec32019-08-01 22:43:1210#include "base/logging.h"
igorcovbb898fb2016-12-01 16:07:5611
Oleksandr Kulkov1213dee82022-05-17 20:45:3412namespace chromeos {
igorcovbb898fb2016-12-01 16:07:5613
14// Define timeout for issued sign_request_id.
Peter Kastinge5a38ed2021-10-02 03:06:3515constexpr base::TimeDelta kSignRequestIdTimeout = base::Minutes(10);
igorcovbb898fb2016-12-01 16:07:5616
Maksim Ivanov86866b72019-07-25 02:15:0617PinDialogManager::PinDialogManager() = default;
igorcovbb898fb2016-12-01 16:07:5618
Maksim Ivanov73dfec32019-08-01 22:43:1219PinDialogManager::~PinDialogManager() = default;
igorcovbb898fb2016-12-01 16:07:5620
Maksim Ivanov2b8136f2019-08-09 14:03:5921void PinDialogManager::AddSignRequestId(
22 const std::string& extension_id,
23 int sign_request_id,
Arthur Sonzognife132ee2024-01-15 11:01:0424 const std::optional<AccountId>& authenticating_user_account_id) {
igorcovbb898fb2016-12-01 16:07:5625 ExtensionNameRequestIdPair key(extension_id, sign_request_id);
Maksim Ivanov2b8136f2019-08-09 14:03:5926 sign_requests_.insert(
27 std::make_pair(key, SignRequestState(/*begin_time=*/base::Time::Now(),
28 authenticating_user_account_id)));
igorcovbb898fb2016-12-01 16:07:5629}
30
Oleksandr Kulkovd47d7452021-11-30 18:16:5931void PinDialogManager::RemoveSignRequest(const std::string& extension_id,
32 int sign_request_id) {
Maksim Ivanov7652d0d2019-09-04 19:12:1933 if (active_dialog_state_ &&
34 active_dialog_state_->extension_id == extension_id &&
35 active_dialog_state_->sign_request_id == sign_request_id) {
36 CloseActiveDialog();
37 }
38
39 ExtensionNameRequestIdPair key(extension_id, sign_request_id);
40 sign_requests_.erase(key);
41}
42
Oleksandr Kulkov2ba63f012021-12-15 15:07:4343int PinDialogManager::StoredSignRequestsForTesting() const {
44 return sign_requests_.size();
45}
46
Maksim Ivanov86866b72019-07-25 02:15:0647PinDialogManager::RequestPinResult PinDialogManager::RequestPin(
igorcovbb898fb2016-12-01 16:07:5648 const std::string& extension_id,
49 const std::string& extension_name,
50 int sign_request_id,
Fabian Sommer195d1f32020-02-27 01:51:0251 security_token_pin::CodeType code_type,
52 security_token_pin::ErrorLabel error_label,
igorcovbb898fb2016-12-01 16:07:5653 int attempts_left,
Maksim Ivanov86866b72019-07-25 02:15:0654 RequestPinCallback callback) {
Maksim Ivanov73dfec32019-08-01 22:43:1255 DCHECK_GE(attempts_left, -1);
56 const bool accept_input = (attempts_left != 0);
57
Maksim Ivanov2b8136f2019-08-09 14:03:5958 // Check the validity of sign_request_id.
59 const SignRequestState* const sign_request_state =
60 FindSignRequestState(extension_id, sign_request_id);
61 if (!sign_request_state)
62 return RequestPinResult::kInvalidId;
63
Maksim Ivanov73dfec32019-08-01 22:43:1264 // Start from sanity checks, as the extension might have issued this call
65 // incorrectly.
66 if (active_dialog_state_) {
67 // The active dialog exists already, so we need to make sure it belongs to
68 // the same extension and the user submitted some input.
69 if (extension_id != active_dialog_state_->extension_id)
Maksim Ivanov86866b72019-07-25 02:15:0670 return RequestPinResult::kOtherFlowInProgress;
Maksim Ivanov73dfec32019-08-01 22:43:1271 if (active_dialog_state_->request_pin_callback ||
72 active_dialog_state_->stop_pin_request_callback) {
73 // Extension requests a PIN without having received any input from its
74 // previous request. Reject the new request.
Maksim Ivanov86866b72019-07-25 02:15:0675 return RequestPinResult::kDialogDisplayedAlready;
Maksim Ivanov73dfec32019-08-01 22:43:1276 }
77 } else {
Maksim Ivanov2b8136f2019-08-09 14:03:5978 // Check that the sign request hasn't timed out yet.
Maksim Ivanov73dfec32019-08-01 22:43:1279 const base::Time current_time = base::Time::Now();
Maksim Ivanov2b8136f2019-08-09 14:03:5980 if (current_time - sign_request_state->begin_time > kSignRequestIdTimeout)
Maksim Ivanov73dfec32019-08-01 22:43:1281 return RequestPinResult::kInvalidId;
igorcovbb898fb2016-12-01 16:07:5682
Maksim Ivanov73dfec32019-08-01 22:43:1283 // A new dialog will be opened, so initialize the related internal state.
Maksim Ivanov5fd579032019-08-02 01:15:1584 active_dialog_state_.emplace(GetHostForNewDialog(), extension_id,
Maksim Ivanov2b8136f2019-08-09 14:03:5985 extension_name, sign_request_id, code_type);
igorcovbb898fb2016-12-01 16:07:5686 }
87
Maksim Ivanov73dfec32019-08-01 22:43:1288 active_dialog_state_->request_pin_callback = std::move(callback);
89 active_dialog_state_->host->ShowSecurityTokenPinDialog(
90 extension_name, code_type, accept_input, error_label, attempts_left,
Maksim Ivanov2b8136f2019-08-09 14:03:5991 sign_request_state->authenticating_user_account_id,
Maksim Ivanov73dfec32019-08-01 22:43:1292 base::BindOnce(&PinDialogManager::OnPinEntered,
93 weak_factory_.GetWeakPtr()),
94 base::BindOnce(&PinDialogManager::OnPinDialogClosed,
95 weak_factory_.GetWeakPtr()));
igorcovbb898fb2016-12-01 16:07:5696
Maksim Ivanov86866b72019-07-25 02:15:0697 return RequestPinResult::kSuccess;
igorcovbb898fb2016-12-01 16:07:5698}
99
Maksim Ivanov86866b72019-07-25 02:15:06100PinDialogManager::StopPinRequestResult
Maksim Ivanovb2985b12019-08-08 15:23:58101PinDialogManager::StopPinRequestWithError(
102 const std::string& extension_id,
Fabian Sommer195d1f32020-02-27 01:51:02103 security_token_pin::ErrorLabel error_label,
Maksim Ivanovb2985b12019-08-08 15:23:58104 StopPinRequestCallback callback) {
Fabian Sommer195d1f32020-02-27 01:51:02105 DCHECK_NE(error_label, security_token_pin::ErrorLabel::kNone);
Maksim Ivanov73dfec32019-08-01 22:43:12106
107 // Perform sanity checks, as the extension might have issued this call
108 // incorrectly.
109 if (!active_dialog_state_ ||
110 active_dialog_state_->extension_id != extension_id) {
Maksim Ivanov86866b72019-07-25 02:15:06111 return StopPinRequestResult::kNoActiveDialog;
igorcovbb898fb2016-12-01 16:07:56112 }
Maksim Ivanov73dfec32019-08-01 22:43:12113 if (active_dialog_state_->request_pin_callback ||
114 active_dialog_state_->stop_pin_request_callback) {
Maksim Ivanov86866b72019-07-25 02:15:06115 return StopPinRequestResult::kNoUserInput;
Maksim Ivanov73dfec32019-08-01 22:43:12116 }
igorcovbb898fb2016-12-01 16:07:56117
Maksim Ivanov2b8136f2019-08-09 14:03:59118 const SignRequestState* const sign_request_state =
119 FindSignRequestState(extension_id, active_dialog_state_->sign_request_id);
120 if (!sign_request_state)
121 return StopPinRequestResult::kNoActiveDialog;
122
Maksim Ivanov73dfec32019-08-01 22:43:12123 active_dialog_state_->stop_pin_request_callback = std::move(callback);
124 active_dialog_state_->host->ShowSecurityTokenPinDialog(
125 active_dialog_state_->extension_name, active_dialog_state_->code_type,
126 /*enable_user_input=*/false, error_label,
Maksim Ivanov2b8136f2019-08-09 14:03:59127 /*attempts_left=*/-1, sign_request_state->authenticating_user_account_id,
Maksim Ivanov73dfec32019-08-01 22:43:12128 base::BindOnce(&PinDialogManager::OnPinEntered,
129 weak_factory_.GetWeakPtr()),
130 base::BindOnce(&PinDialogManager::OnPinDialogClosed,
131 weak_factory_.GetWeakPtr()));
132
Maksim Ivanov86866b72019-07-25 02:15:06133 return StopPinRequestResult::kSuccess;
igorcovbb898fb2016-12-01 16:07:56134}
135
Maksim Ivanov86866b72019-07-25 02:15:06136bool PinDialogManager::LastPinDialogClosed(
137 const std::string& extension_id) const {
138 auto iter = last_response_closed_.find(extension_id);
139 return iter != last_response_closed_.end() && iter->second;
igorcovbb898fb2016-12-01 16:07:56140}
141
142bool PinDialogManager::CloseDialog(const std::string& extension_id) {
Maksim Ivanov73dfec32019-08-01 22:43:12143 // Perform sanity checks, as the extension might have issued this call
144 // incorrectly.
145 if (!active_dialog_state_ ||
146 extension_id != active_dialog_state_->extension_id) {
igorcovbb898fb2016-12-01 16:07:56147 LOG(ERROR) << "StopPinRequest called by unexpected extension: "
148 << extension_id;
149 return false;
150 }
151
Maksim Ivanov5ccb96c2019-08-08 13:25:41152 CloseActiveDialog();
igorcovbb898fb2016-12-01 16:07:56153 return true;
154}
155
156void PinDialogManager::ExtensionUnloaded(const std::string& extension_id) {
Maksim Ivanov73dfec32019-08-01 22:43:12157 if (active_dialog_state_ &&
158 active_dialog_state_->extension_id == extension_id) {
Maksim Ivanov5ccb96c2019-08-08 13:25:41159 CloseActiveDialog();
Maksim Ivanov73dfec32019-08-01 22:43:12160 }
igorcovbb898fb2016-12-01 16:07:56161
162 last_response_closed_[extension_id] = false;
163
Maksim Ivanov2b8136f2019-08-09 14:03:59164 for (auto it = sign_requests_.cbegin(); it != sign_requests_.cend();) {
Maksim Ivanov86866b72019-07-25 02:15:06165 if (it->first.first == extension_id)
Maksim Ivanov2b8136f2019-08-09 14:03:59166 sign_requests_.erase(it++);
Maksim Ivanov86866b72019-07-25 02:15:06167 else
igorcovbb898fb2016-12-01 16:07:56168 ++it;
igorcovbb898fb2016-12-01 16:07:56169 }
170}
171
Maksim Ivanov5fd579032019-08-02 01:15:15172void PinDialogManager::AddPinDialogHost(
173 SecurityTokenPinDialogHost* pin_dialog_host) {
174 DCHECK(!base::Contains(added_dialog_hosts_, pin_dialog_host));
175 added_dialog_hosts_.push_back(pin_dialog_host);
176}
177
178void PinDialogManager::RemovePinDialogHost(
179 SecurityTokenPinDialogHost* pin_dialog_host) {
Maksim Ivanov5ccb96c2019-08-08 13:25:41180 if (active_dialog_state_ && active_dialog_state_->host == pin_dialog_host)
181 CloseActiveDialog();
Maksim Ivanov5fd579032019-08-02 01:15:15182 DCHECK(base::Contains(added_dialog_hosts_, pin_dialog_host));
183 base::Erase(added_dialog_hosts_, pin_dialog_host);
184}
185
Maksim Ivanov2b8136f2019-08-09 14:03:59186PinDialogManager::SignRequestState::SignRequestState(
187 base::Time begin_time,
Arthur Sonzognife132ee2024-01-15 11:01:04188 const std::optional<AccountId>& authenticating_user_account_id)
Maksim Ivanov2b8136f2019-08-09 14:03:59189 : begin_time(begin_time),
190 authenticating_user_account_id(authenticating_user_account_id) {}
191
192PinDialogManager::SignRequestState::SignRequestState(const SignRequestState&) =
193 default;
Oleksandr Kulkov9c7cf9552022-06-02 12:25:43194PinDialogManager::SignRequestState&
195PinDialogManager::SignRequestState::operator=(const SignRequestState&) =
196 default;
Maksim Ivanov2b8136f2019-08-09 14:03:59197
198PinDialogManager::SignRequestState::~SignRequestState() = default;
199
Maksim Ivanov73dfec32019-08-01 22:43:12200PinDialogManager::ActiveDialogState::ActiveDialogState(
201 SecurityTokenPinDialogHost* host,
202 const std::string& extension_id,
203 const std::string& extension_name,
Maksim Ivanov2b8136f2019-08-09 14:03:59204 int sign_request_id,
Fabian Sommer195d1f32020-02-27 01:51:02205 security_token_pin::CodeType code_type)
Maksim Ivanov73dfec32019-08-01 22:43:12206 : host(host),
207 extension_id(extension_id),
208 extension_name(extension_name),
Maksim Ivanov2b8136f2019-08-09 14:03:59209 sign_request_id(sign_request_id),
Maksim Ivanov73dfec32019-08-01 22:43:12210 code_type(code_type) {}
Maksim Ivanovab8e8582019-08-01 21:49:13211
Maksim Ivanov73dfec32019-08-01 22:43:12212PinDialogManager::ActiveDialogState::~ActiveDialogState() = default;
213
Maksim Ivanov2b8136f2019-08-09 14:03:59214PinDialogManager::SignRequestState* PinDialogManager::FindSignRequestState(
215 const std::string& extension_id,
216 int sign_request_id) {
217 const ExtensionNameRequestIdPair key(extension_id, sign_request_id);
218 const auto sign_request_iter = sign_requests_.find(key);
219 if (sign_request_iter == sign_requests_.end())
220 return nullptr;
221 return &sign_request_iter->second;
222}
223
Maksim Ivanov73dfec32019-08-01 22:43:12224void PinDialogManager::OnPinEntered(const std::string& user_input) {
225 DCHECK(!active_dialog_state_->stop_pin_request_callback);
226 last_response_closed_[active_dialog_state_->extension_id] = false;
227 if (active_dialog_state_->request_pin_callback)
228 std::move(active_dialog_state_->request_pin_callback).Run(user_input);
Maksim Ivanovab8e8582019-08-01 21:49:13229}
230
231void PinDialogManager::OnPinDialogClosed() {
Maksim Ivanov73dfec32019-08-01 22:43:12232 DCHECK(!active_dialog_state_->request_pin_callback ||
233 !active_dialog_state_->stop_pin_request_callback);
Maksim Ivanovab8e8582019-08-01 21:49:13234
Maksim Ivanov73dfec32019-08-01 22:43:12235 last_response_closed_[active_dialog_state_->extension_id] = true;
236 if (active_dialog_state_->request_pin_callback) {
237 std::move(active_dialog_state_->request_pin_callback)
238 .Run(/*user_input=*/std::string());
239 }
240 if (active_dialog_state_->stop_pin_request_callback)
241 std::move(active_dialog_state_->stop_pin_request_callback).Run();
242 active_dialog_state_.reset();
Maksim Ivanovab8e8582019-08-01 21:49:13243}
244
Maksim Ivanov5fd579032019-08-02 01:15:15245SecurityTokenPinDialogHost* PinDialogManager::GetHostForNewDialog() {
246 if (added_dialog_hosts_.empty())
247 return &default_dialog_host_;
248 return added_dialog_hosts_.back();
249}
250
Maksim Ivanov5ccb96c2019-08-08 13:25:41251void PinDialogManager::CloseActiveDialog() {
252 if (!active_dialog_state_)
253 return;
254
255 // Ignore any further callbacks from the host. Instead of relying on the host
256 // to call the closing callback, run OnPinDialogClosed() below explicitly.
257 weak_factory_.InvalidateWeakPtrs();
258
259 active_dialog_state_->host->CloseSecurityTokenPinDialog();
260 OnPinDialogClosed();
261 DCHECK(!active_dialog_state_);
262}
263
Oleksandr Kulkov1213dee82022-05-17 20:45:34264} // namespace chromeos