Commit afc265a0 authored by Fabian Sommer's avatar Fabian Sommer Committed by Chromium LUCI CQ

Add policies for logout on smart card removal

This CL implements the core part of SecurityTokenSessionController.
The controller observes security tokens and triggers lock / logout when
a token that was used for login is removed, alongside UI implemented in
previous CLs.

This behavior can be controlled by the SecurityTokenSessionBehavior and
SecurityTokenSessionNotificationSeconds policies, which this CL
activates.

This CL also adds browser tests for both policies. In doing so, it also
adds a test for SecurityTokenRestrictionView.

The CL renames the should_fail_certificate_requests property in
TestCertificateProviderExtension to should_provide_certificates,
which more accurately reflects its purpose.

Fixed: 1131450
Change-Id: I8cf4e25fbbcb8ae4ab607ace254a85197f594f5a
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2632990
Commit-Queue: Fabian Sommer <fabiansommer@chromium.org>
Reviewed-by: default avatarXiyuan Xia <xiyuan@chromium.org>
Reviewed-by: default avatarMaksim Ivanov <emaxx@chromium.org>
Cr-Commit-Position: refs/heads/master@{#846276}
parent 3160ebc7
...@@ -205,7 +205,7 @@ void TestCertificateProviderExtension::TriggerSetCertificates() { ...@@ -205,7 +205,7 @@ void TestCertificateProviderExtension::TriggerSetCertificates() {
base::Value message_data(base::Value::Type::DICTIONARY); base::Value message_data(base::Value::Type::DICTIONARY);
message_data.SetStringKey("name", "setCertificates"); message_data.SetStringKey("name", "setCertificates");
base::Value cert_info_values(base::Value::Type::LIST); base::Value cert_info_values(base::Value::Type::LIST);
if (!should_fail_certificate_requests_) if (should_provide_certificates_)
cert_info_values.Append(MakeClientCertificateInfoValue(*certificate_)); cert_info_values.Append(MakeClientCertificateInfoValue(*certificate_));
message_data.SetKey("certificateInfoList", std::move(cert_info_values)); message_data.SetKey("certificateInfoList", std::move(cert_info_values));
...@@ -266,7 +266,7 @@ void TestCertificateProviderExtension::HandleCertificatesRequest( ...@@ -266,7 +266,7 @@ void TestCertificateProviderExtension::HandleCertificatesRequest(
ReplyToJsCallback callback) { ReplyToJsCallback callback) {
++certificate_request_count_; ++certificate_request_count_;
base::Value cert_info_values(base::Value::Type::LIST); base::Value cert_info_values(base::Value::Type::LIST);
if (!should_fail_certificate_requests_) if (should_provide_certificates_)
cert_info_values.Append(MakeClientCertificateInfoValue(*certificate_)); cert_info_values.Append(MakeClientCertificateInfoValue(*certificate_));
std::move(callback).Run(cert_info_values); std::move(callback).Run(cert_info_values);
} }
......
...@@ -73,11 +73,10 @@ class TestCertificateProviderExtension final ...@@ -73,11 +73,10 @@ class TestCertificateProviderExtension final
remaining_pin_attempts_ = remaining_pin_attempts; remaining_pin_attempts_ = remaining_pin_attempts;
} }
// Sets whether the extension should respond with a failure to the // Sets whether the extension should return any certificates in response to a
// onCertificatesRequested requests. // onCertificatesRequested request or a TriggerSetCertificates() call.
void set_should_fail_certificate_requests( void set_should_provide_certificates(bool should_provide_certificates) {
bool should_fail_certificate_requests) { should_provide_certificates_ = should_provide_certificates;
should_fail_certificate_requests_ = should_fail_certificate_requests;
} }
// Sets whether the extension should respond with a failure to the // Sets whether the extension should respond with a failure to the
...@@ -113,7 +112,7 @@ class TestCertificateProviderExtension final ...@@ -113,7 +112,7 @@ class TestCertificateProviderExtension final
// When equal to zero, signature requests will be failed immediately; when is // When equal to zero, signature requests will be failed immediately; when is
// negative, infinite number of attempts is allowed. // negative, infinite number of attempts is allowed.
int remaining_pin_attempts_ = -1; int remaining_pin_attempts_ = -1;
bool should_fail_certificate_requests_ = false; bool should_provide_certificates_ = true;
bool should_fail_sign_digest_requests_ = false; bool should_fail_sign_digest_requests_ = false;
content::NotificationRegistrar notification_registrar_; content::NotificationRegistrar notification_registrar_;
......
...@@ -250,7 +250,7 @@ IN_PROC_BROWSER_TEST_F(CryptohomeKeyDelegateServiceProviderTest, ...@@ -250,7 +250,7 @@ IN_PROC_BROWSER_TEST_F(CryptohomeKeyDelegateServiceProviderTest,
// by the test provider anymore. // by the test provider anymore.
IN_PROC_BROWSER_TEST_F(CryptohomeKeyDelegateServiceProviderTest, IN_PROC_BROWSER_TEST_F(CryptohomeKeyDelegateServiceProviderTest,
SignatureErrorKeyRemoved) { SignatureErrorKeyRemoved) {
certificate_provider_extension()->set_should_fail_certificate_requests(true); certificate_provider_extension()->set_should_provide_certificates(false);
RefreshCertsFromCertProviders(); RefreshCertsFromCertProviders();
std::vector<uint8_t> signature; std::vector<uint8_t> signature;
......
...@@ -5,13 +5,29 @@ ...@@ -5,13 +5,29 @@
#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SECURITY_TOKEN_SESSION_CONTROLLER_H_ #ifndef CHROME_BROWSER_CHROMEOS_LOGIN_SECURITY_TOKEN_SESSION_CONTROLLER_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_SECURITY_TOKEN_SESSION_CONTROLLER_H_ #define CHROME_BROWSER_CHROMEOS_LOGIN_SECURITY_TOKEN_SESSION_CONTROLLER_H_
#include <string>
#include <vector>
#include "base/containers/flat_map.h"
#include "base/containers/flat_set.h"
#include "base/memory/weak_ptr.h"
#include "base/timer/timer.h"
#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service.h"
#include "components/keyed_service/core/keyed_service.h" #include "components/keyed_service/core/keyed_service.h"
#include "components/prefs/pref_change_registrar.h" #include "components/prefs/pref_change_registrar.h"
#include "components/prefs/pref_registry_simple.h" #include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h" #include "components/prefs/pref_service.h"
#include "components/user_manager/user.h" #include "components/user_manager/user.h"
#include "extensions/common/extension_id.h"
namespace views {
class Widget;
}
namespace chromeos { namespace chromeos {
class CertificateProvider;
namespace login { namespace login {
// A controller that implements the combined behavior of the // A controller that implements the combined behavior of the
...@@ -21,13 +37,17 @@ namespace login { ...@@ -21,13 +37,17 @@ namespace login {
// certificate ceases to be present while the user is logged in. // certificate ceases to be present while the user is logged in.
// SecurityTokenSessionNotificationSeconds determines if and how long the user // SecurityTokenSessionNotificationSeconds determines if and how long the user
// is getting informed what is going to happen when the certificate vanishes. // is getting informed what is going to happen when the certificate vanishes.
class SecurityTokenSessionController : public KeyedService { class SecurityTokenSessionController
: public KeyedService,
public CertificateProviderService::Observer {
public: public:
enum class Behavior { kIgnore, kLogout, kLock }; enum class Behavior { kIgnore, kLogout, kLock };
SecurityTokenSessionController(PrefService* local_state, SecurityTokenSessionController(
PrefService* profile_prefs, PrefService* local_state,
const user_manager::User* user); PrefService* profile_prefs,
const user_manager::User* user,
CertificateProviderService* certificate_provider_service);
SecurityTokenSessionController(const SecurityTokenSessionController& other) = SecurityTokenSessionController(const SecurityTokenSessionController& other) =
delete; delete;
SecurityTokenSessionController& operator=( SecurityTokenSessionController& operator=(
...@@ -45,13 +65,25 @@ class SecurityTokenSessionController : public KeyedService { ...@@ -45,13 +65,25 @@ class SecurityTokenSessionController : public KeyedService {
// happens for a user on a device. // happens for a user on a device.
static void MaybeDisplayLoginScreenNotification(); static void MaybeDisplayLoginScreenNotification();
// CertificateProviderService::Observer
void OnCertificatesUpdated(
const std::string& extension_id,
const std::vector<certificate_provider::CertificateInfo>&
certificate_infos) override;
private: private:
Behavior GetBehaviorFromPref() const; Behavior GetBehaviorFromPref() const;
void UpdateBehaviorPref(); void UpdateBehaviorPref();
void UpdateNotificationPref(); void UpdateNotificationPref();
void ExtensionProvidesAllRequiredCertificates(
const extensions::ExtensionId& extension_id);
void ExtensionStopsProvidingCertificate(
const extensions::ExtensionId& extension_id);
void TriggerAction();
void AddLockNotification() const; void AddLockNotification() const;
void ScheduleLogoutNotification() const; void ScheduleLogoutNotification() const;
void Reset();
PrefService* const local_state_; PrefService* const local_state_;
PrefService* const profile_prefs_; PrefService* const profile_prefs_;
...@@ -59,6 +91,17 @@ class SecurityTokenSessionController : public KeyedService { ...@@ -59,6 +91,17 @@ class SecurityTokenSessionController : public KeyedService {
PrefChangeRegistrar pref_change_registrar_; PrefChangeRegistrar pref_change_registrar_;
Behavior behavior_ = Behavior::kIgnore; Behavior behavior_ = Behavior::kIgnore;
base::TimeDelta notification_seconds_; base::TimeDelta notification_seconds_;
base::flat_set<extensions::ExtensionId> observed_extensions_;
base::flat_map<extensions::ExtensionId, std::vector<std::string>>
extension_to_spkis_;
base::flat_set<extensions::ExtensionId>
extensions_missing_required_certificates_;
views::Widget* fullscreen_notification_ = nullptr;
base::OneShotTimer action_timer_;
CertificateProviderService* certificate_provider_service_ = nullptr;
std::unique_ptr<CertificateProvider> certificate_provider_;
base::WeakPtrFactory<SecurityTokenSessionController> weak_ptr_factory_{this};
}; };
} // namespace login } // namespace login
......
...@@ -6,11 +6,13 @@ ...@@ -6,11 +6,13 @@
#include "chrome/browser/browser_process.h" #include "chrome/browser/browser_process.h"
#include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h" #include "chrome/browser/chromeos/certificate_provider/certificate_provider_service_factory.h"
#include "chrome/browser/chromeos/login/challenge_response_auth_keys_loader.h"
#include "chrome/browser/chromeos/login/security_token_session_controller.h" #include "chrome/browser/chromeos/login/security_token_session_controller.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h" #include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/profiles/incognito_helpers.h" #include "chrome/browser/profiles/incognito_helpers.h"
#include "chrome/browser/profiles/profile.h" #include "chrome/browser/profiles/profile.h"
#include "components/keyed_service/content/browser_context_dependency_manager.h" #include "components/keyed_service/content/browser_context_dependency_manager.h"
#include "components/user_manager/user.h"
namespace chromeos { namespace chromeos {
namespace login { namespace login {
...@@ -52,10 +54,21 @@ KeyedService* SecurityTokenSessionControllerFactory::BuildServiceInstanceFor( ...@@ -52,10 +54,21 @@ KeyedService* SecurityTokenSessionControllerFactory::BuildServiceInstanceFor(
// This can happen in tests that do not have local state. // This can happen in tests that do not have local state.
return nullptr; return nullptr;
} }
// The service is only relevant for users who authenticate with a security
// token used for a challenge-response flow.
// TODO(crbug.com/1164373): This check produces false negatives for ephemeral
// users.
user_manager::User* user = ProfileHelper::Get()->GetUserByProfile( user_manager::User* user = ProfileHelper::Get()->GetUserByProfile(
Profile::FromBrowserContext(context)); Profile::FromBrowserContext(context));
if (!ChallengeResponseAuthKeysLoader::CanAuthenticateUser(
user->GetAccountId()))
return nullptr;
CertificateProviderService* certificate_provider_service =
CertificateProviderServiceFactory::GetForBrowserContext(context);
return new SecurityTokenSessionController(local_state, profile->GetPrefs(), return new SecurityTokenSessionController(local_state, profile->GetPrefs(),
user); user, certificate_provider_service);
} }
content::BrowserContext* content::BrowserContext*
......
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
#include "ui/views/controls/label.h" #include "ui/views/controls/label.h"
#include "ui/views/layout/box_layout.h" #include "ui/views/layout/box_layout.h"
#include "ui/views/layout/layout_provider.h" #include "ui/views/layout/layout_provider.h"
#include "ui/views/metadata/metadata_impl_macros.h"
#include "ui/views/widget/widget.h" #include "ui/views/widget/widget.h"
#include "ui/views/window/dialog_delegate.h" #include "ui/views/window/dialog_delegate.h"
...@@ -112,7 +113,6 @@ base::string16 GetDialogText( ...@@ -112,7 +113,6 @@ base::string16 GetDialogText(
SecurityTokenSessionRestrictionView::SecurityTokenSessionRestrictionView( SecurityTokenSessionRestrictionView::SecurityTokenSessionRestrictionView(
base::TimeDelta duration, base::TimeDelta duration,
base::OnceClosure accept_callback, base::OnceClosure accept_callback,
base::OnceClosure window_closing_callback,
chromeos::login::SecurityTokenSessionController::Behavior behavior, chromeos::login::SecurityTokenSessionController::Behavior behavior,
const std::string& domain) const std::string& domain)
: AppDialogView(GetImage()), : AppDialogView(GetImage()),
...@@ -125,7 +125,6 @@ SecurityTokenSessionRestrictionView::SecurityTokenSessionRestrictionView( ...@@ -125,7 +125,6 @@ SecurityTokenSessionRestrictionView::SecurityTokenSessionRestrictionView(
SetTitle(GetTitle(behavior)); SetTitle(GetTitle(behavior));
SetAcceptCallback(std::move(accept_callback)); SetAcceptCallback(std::move(accept_callback));
RegisterWindowClosingCallback(std::move(window_closing_callback));
InitializeView(/*heading_text=*/base::string16()); InitializeView(/*heading_text=*/base::string16());
UpdateLabel(); UpdateLabel();
...@@ -144,3 +143,6 @@ void SecurityTokenSessionRestrictionView::UpdateLabel() { ...@@ -144,3 +143,6 @@ void SecurityTokenSessionRestrictionView::UpdateLabel() {
update_timer_.Stop(); update_timer_.Stop();
} }
} }
BEGIN_METADATA(SecurityTokenSessionRestrictionView, AppDialogView);
END_METADATA
...@@ -10,11 +10,14 @@ ...@@ -10,11 +10,14 @@
#include "base/timer/timer.h" #include "base/timer/timer.h"
#include "chrome/browser/chromeos/login/security_token_session_controller.h" #include "chrome/browser/chromeos/login/security_token_session_controller.h"
#include "chrome/browser/ui/views/apps/app_dialog/app_dialog_view.h" #include "chrome/browser/ui/views/apps/app_dialog/app_dialog_view.h"
#include "ui/views/metadata/metadata_header_macros.h"
// The dialog informing the user they are about to be logged out or locked // The dialog informing the user they are about to be logged out or locked
// because they removed their security token (smart card). // because they removed their security token (smart card).
class SecurityTokenSessionRestrictionView : public AppDialogView { class SecurityTokenSessionRestrictionView : public AppDialogView {
public: public:
METADATA_HEADER(SecurityTokenSessionRestrictionView);
// Creates the dialog. // Creates the dialog.
// `duration`: Initial countdown time, will be displayed in seconds. // `duration`: Initial countdown time, will be displayed in seconds.
// `accept_callback`: Callback when the user accepts the dialog. // `accept_callback`: Callback when the user accepts the dialog.
...@@ -26,7 +29,6 @@ class SecurityTokenSessionRestrictionView : public AppDialogView { ...@@ -26,7 +29,6 @@ class SecurityTokenSessionRestrictionView : public AppDialogView {
SecurityTokenSessionRestrictionView( SecurityTokenSessionRestrictionView(
base::TimeDelta duration, base::TimeDelta duration,
base::OnceClosure accept_callback, base::OnceClosure accept_callback,
base::OnceClosure window_closing_callback,
chromeos::login::SecurityTokenSessionController::Behavior behavior, chromeos::login::SecurityTokenSessionController::Behavior behavior,
const std::string& domain); const std::string& domain);
SecurityTokenSessionRestrictionView( SecurityTokenSessionRestrictionView(
......
...@@ -7679,7 +7679,7 @@ ...@@ -7679,7 +7679,7 @@
'caption': '''Lock the current session.''', 'caption': '''Lock the current session.''',
}, },
], ],
'future_on': ['chrome_os'], 'supported_on': ['chrome_os:90-'],
'features': { 'features': {
'dynamic_refresh': True, 'dynamic_refresh': True,
'per_profile': False, 'per_profile': False,
...@@ -7695,7 +7695,7 @@ ...@@ -7695,7 +7695,7 @@
'owners': ['fabiansommer@chromium.org, emaxx@chromium.org'], 'owners': ['fabiansommer@chromium.org, emaxx@chromium.org'],
'type': 'int', 'type': 'int',
'schema': { 'type': 'integer', 'minimum': 0, 'maximum': 9999 }, 'schema': { 'type': 'integer', 'minimum': 0, 'maximum': 9999 },
'future_on': ['chrome_os'], 'supported_on': ['chrome_os:90-'],
'features': { 'features': {
'dynamic_refresh': True, 'dynamic_refresh': True,
'per_profile': False, 'per_profile': False,
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment