Commit 5a899167 authored by Renato Silva's avatar Renato Silva Committed by Commit Bot

PIN Auto Submit - Extend PinBackend

Extend PinBackend to support the PIN auto submit functionality.

PIN auto submit just exposes the user's PIN length in local state
when its enabled. Normal users are prompted with a dialog in Settings
for their PIN. After checking that the PIN is correct, the length is
exposed. Managed users who cannot control the feature have their PIN
lengths collected upon a successful authentication attempt.

A new method - SetPinAutoSubmitEnabled - was added to allow Chrome OS
Settings to control the functionality directly. The basic operations
in PinBackend (Set, Remove, TryAuthenticate) were extended so that
they now call PIN auto submit subroutines to do additional steps that
are necessary for the new functionality. These methods were named
PinAutosubmitOnSet, PinAutosubmitOnRemove and PinAutosubmitOnTryAuth.

In order to extract the PIN length upon a successful authentication
attempt, the call to Cryptohome is now calling the new method
'OnCryptohomeAuthenticationResponse' first, instead of the
original caller.

All of the PIN auto submit subroutines are guarded by the feature
flag, which is disabled by default.

Unit tests were added to cover all the possible user flows.

Design Document
- https://goto.google.com/chromeos-oac-pin-authentication-refinements

Bug: 1107367, 1107367
Change-Id: Ie770727c73c9abece174a10fd8c618575f849b88
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2306258
Commit-Queue: Renato Silva <rrsilva@google.com>
Reviewed-by: default avatarToni Baržić <tbarzic@chromium.org>
Cr-Commit-Position: refs/heads/master@{#791405}
parent c2470976
......@@ -6,6 +6,7 @@
#include "base/base64.h"
#include "base/bind.h"
#include "base/logging.h"
#include "base/strings/string_util.h"
#include "base/threading/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/login/quick_unlock/pin_storage_cryptohome.h"
......@@ -14,9 +15,13 @@
#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h"
#include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_utils.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/profiles/profile.h"
#include "chrome/common/pref_names.h"
#include "components/account_id/account_id.h"
#include "components/keep_alive_registry/keep_alive_types.h"
#include "components/keep_alive_registry/scoped_keep_alive.h"
#include "components/prefs/pref_service.h"
#include "components/user_manager/known_user.h"
#include "crypto/random.h"
namespace chromeos {
......@@ -25,6 +30,7 @@ namespace quick_unlock {
namespace {
constexpr int kSaltByteSize = 16;
constexpr int kPinAutosubmitMaxPinLength = 12;
QuickUnlockStorage* GetPrefsBackend(const AccountId& account_id) {
return QuickUnlockFactory::GetForAccountId(account_id);
......@@ -179,9 +185,45 @@ void PinBackend::Set(const AccountId& account_id,
storage->pin_storage_prefs()->RemovePin();
cryptohome_backend_->SetPin(*user_context, pin, base::nullopt,
std::move(did_set));
UpdatePinAutosubmitOnSet(account_id, pin.length());
} else {
storage->pin_storage_prefs()->SetPin(pin);
storage->MarkStrongAuth();
UpdatePinAutosubmitOnSet(account_id, pin.length());
PostResponse(std::move(did_set), true);
}
}
void PinBackend::SetPinAutoSubmitEnabled(const AccountId& account_id,
const std::string& pin,
const bool enabled,
BoolCallback did_set) {
// Immediate false if the PIN length isn't supported, or when the feature
// isdisabled.
if (!IsPinAutosubmitFeatureEnabled() ||
pin.length() > kPinAutosubmitMaxPinLength) {
PostResponse(std::move(did_set), false);
return;
}
// If the preference is not user controllable, the auto submit dialog
// isn't available in Settings, so we return a failure.
if (!PrefService(account_id)
->IsUserModifiablePreference(prefs::kPinUnlockAutosubmitEnabled)) {
PostResponse(std::move(did_set), false);
return;
}
if (enabled) {
// Unretained safe because the PinBackend instance is never destroyed.
TryAuthenticate(account_id, Key(pin),
base::BindOnce(&PinBackend::OnPinAutosubmitCheckComplete,
base::Unretained(this), account_id,
pin.size(), std::move(did_set)));
} else {
user_manager::known_user::SetUserPinLength(account_id, 0);
PrefService(account_id)
->SetBoolean(prefs::kPinUnlockAutosubmitEnabled, false);
PostResponse(std::move(did_set), true);
}
}
......@@ -199,6 +241,9 @@ void PinBackend::Remove(const AccountId& account_id,
QuickUnlockStorage* storage = GetPrefsBackend(account_id);
DCHECK(storage);
// Disable PIN auto submit when removing the pin.
UpdatePinAutosubmitOnRemove(account_id);
if (cryptohome_backend_) {
// If |user_context| is null, then the token timed out.
const UserContext* user_context = storage->GetUserContext(token);
......@@ -245,7 +290,12 @@ void PinBackend::TryAuthenticate(const AccountId& account_id,
}
if (ShouldUseCryptohome(account_id)) {
cryptohome_backend_->TryAuthenticate(account_id, key, std::move(result));
// Safe because the PinBackend instance is never destroyed.
cryptohome_backend_->TryAuthenticate(
account_id, key,
base::BindOnce(&PinBackend::OnCryptohomeAuthenticationResponse,
base::Unretained(this), account_id, key,
std::move(result)));
} else {
QuickUnlockStorage* storage = GetPrefsBackend(account_id);
DCHECK(storage);
......@@ -253,8 +303,13 @@ void PinBackend::TryAuthenticate(const AccountId& account_id,
if (!storage->HasStrongAuth()) {
PostResponse(std::move(result), false);
} else {
PostResponse(std::move(result),
storage->pin_storage_prefs()->TryAuthenticatePin(key));
const bool auth_success =
storage->pin_storage_prefs()->TryAuthenticatePin(key);
if (auth_success && key.GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN) {
UpdatePinAutosubmitOnSuccessfulTryAuth(account_id,
key.GetSecret().length());
}
PostResponse(std::move(result), auth_success);
}
}
}
......@@ -270,6 +325,25 @@ bool PinBackend::ShouldUseCryptohome(const AccountId& account_id) {
return !storage || !storage->pin_storage_prefs()->IsPinSet();
}
int PinBackend::GetExposedPinLength(const AccountId& account_id) {
if (!IsPinAutosubmitFeatureEnabled()) {
// Clear the exposed length if the feature was disabled.
user_manager::known_user::SetUserPinLength(account_id, 0);
return 0;
}
// Clear the pin length in local state if auto-submit got disabled, for
// example, via policy. Disabling auto submit through Settings clears it
// immediately.
if (!PrefService(account_id)
->GetBoolean(prefs::kPinUnlockAutosubmitEnabled)) {
user_manager::known_user::SetUserPinLength(account_id, 0);
return 0;
}
return user_manager::known_user::GetUserPinLength(account_id);
}
void PinBackend::OnIsCryptohomeBackendSupported(bool is_supported) {
if (is_supported)
cryptohome_backend_ = std::make_unique<PinStorageCryptohome>();
......@@ -288,5 +362,78 @@ void PinBackend::OnPinMigrationAttemptComplete(Profile* profile, bool success) {
scoped_keep_alive_.reset();
}
void PinBackend::OnCryptohomeAuthenticationResponse(const AccountId& account_id,
const Key& key,
BoolCallback result,
bool success) {
// Perform some PIN auto submit related tasks if the authentication
// was successful.
if (success && key.GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN) {
UpdatePinAutosubmitOnSuccessfulTryAuth(account_id,
key.GetSecret().length());
}
PostResponse(std::move(result), success);
}
void PinBackend::OnPinAutosubmitCheckComplete(const AccountId& account_id,
size_t pin_length,
BoolCallback result,
bool success) {
// Set the exposed PIN to zero, if not successful.
user_manager::known_user::SetUserPinLength(account_id,
success ? pin_length : 0);
PrefService(account_id)
->SetBoolean(prefs::kPinUnlockAutosubmitEnabled, success);
PostResponse(std::move(result), success);
}
PrefService* PinBackend::PrefService(const AccountId& account_id) {
return chromeos::ProfileHelper::Get()
->GetProfileByAccountId(account_id)
->GetPrefs();
}
void PinBackend::UpdatePinAutosubmitOnSet(const AccountId& account_id,
size_t pin_length) {
if (!IsPinAutosubmitFeatureEnabled())
return;
const bool autosubmit_enabled =
PrefService(account_id)->GetBoolean(prefs::kPinUnlockAutosubmitEnabled) &&
pin_length <= kPinAutosubmitMaxPinLength;
// Explicitly set the user pref to false if the PIN is longer than 12 digits
// so that the toggle on the Settings page remains unchecked. If the user
// tries to enable the toggle with a long pin an error is shown.
if (pin_length > kPinAutosubmitMaxPinLength) {
PrefService(account_id)
->SetBoolean(prefs::kPinUnlockAutosubmitEnabled, false);
}
// Expose the true PIN length if enabled
pin_length = autosubmit_enabled ? pin_length : 0;
user_manager::known_user::SetUserPinLength(account_id, pin_length);
}
void PinBackend::UpdatePinAutosubmitOnRemove(const AccountId& account_id) {
if (!IsPinAutosubmitFeatureEnabled())
return;
user_manager::known_user::SetUserPinLength(account_id, 0);
PrefService(account_id)->ClearPref(prefs::kPinUnlockAutosubmitEnabled);
}
void PinBackend::UpdatePinAutosubmitOnSuccessfulTryAuth(
const AccountId& account_id,
size_t pin_length) {
if (!IsPinAutosubmitFeatureEnabled())
return;
const bool autosubmit_enabled =
PrefService(account_id)->GetBoolean(prefs::kPinUnlockAutosubmitEnabled) &&
pin_length <= kPinAutosubmitMaxPinLength;
if (autosubmit_enabled)
user_manager::known_user::SetUserPinLength(account_id, pin_length);
}
} // namespace quick_unlock
} // namespace chromeos
......@@ -9,6 +9,7 @@
#include "base/callback.h"
#include "chromeos/login/auth/key.h"
#include "components/prefs/pref_service.h"
class AccountId;
class Profile;
......@@ -57,6 +58,13 @@ class PinBackend {
const std::string& pin,
BoolCallback did_set);
// Set the state of PIN auto submit for the given user. Called when enabling
// auto submit through the confirmation dialog in Settings.
void SetPinAutoSubmitEnabled(const AccountId& account_id,
const std::string& pin,
const bool enabled,
BoolCallback did_set);
// Remove the given user's PIN.
void Remove(const AccountId& account_id,
const std::string& auth_token,
......@@ -80,6 +88,11 @@ class PinBackend {
// Resets any cached state for testing purposes.
static void ResetForTesting();
// Interface for the lock/login screen to access the user's PIN length.
// Ensures that the UI is always consistent with the pref values without the
// need for individual observers.
int GetExposedPinLength(const AccountId& account_id);
private:
// Called when we know if the cryptohome supports PIN.
void OnIsCryptohomeBackendSupported(bool is_supported);
......@@ -88,6 +101,42 @@ class PinBackend {
// should be cleared from prefs.
void OnPinMigrationAttemptComplete(Profile* profile, bool success);
// Actions to be performed after an authentication attempt with Cryptohome.
// The only use case right now is for PIN auto submit, where we might want to
// expose the PIN length upon a successful attempt.
void OnCryptohomeAuthenticationResponse(const AccountId& account_id,
const Key& key,
BoolCallback result,
bool success);
// Called after checking the user's PIN when enabling auto submit.
// If the authentication was |success|ful, the |pin_length| will be
// exposed in local state.
void OnPinAutosubmitCheckComplete(const AccountId& account_id,
size_t pin_length,
BoolCallback result,
bool success);
// Help method for working with the PIN auto submit preference.
PrefService* PrefService(const AccountId& account_id);
// Simple operations to be performed for PIN auto submit during the common
// operations in PinBackend - Set, Remove, TryAuthenticate
// When setting/updating a PIN. After every 'Set' operation the
// exposed length can only be either the true PIN length, or zero.
void UpdatePinAutosubmitOnSet(const AccountId& account_id, size_t pin_length);
// Clears the exposed PIN length and resets the user setting.
void UpdatePinAutosubmitOnRemove(const AccountId& account_id);
// A successful authentication attempt will expose the pin length. This is
// necessary when the preference is being set by policy. When the pref is
// being controlled by the user -- through Settings --, the length is exposed
// through a confirmation dialog immediately.
void UpdatePinAutosubmitOnSuccessfulTryAuth(const AccountId& account_id,
size_t pin_length);
// True if still trying to determine which backend should be used.
bool resolving_backend_ = true;
// Determining if the device supports cryptohome-based keys requires an async
......
......@@ -85,7 +85,10 @@ void RegisterProfilePrefs(PrefRegistrySimple* registry) {
// 0 indicates no maximum length for the pin.
registry->RegisterIntegerPref(prefs::kPinUnlockMaximumLength, 0);
registry->RegisterBooleanPref(prefs::kPinUnlockWeakPinsAllowed, true);
registry->RegisterBooleanPref(prefs::kPinUnlockAutosubmitEnabled, true);
// Register as true by default only when the feature is enabled.
registry->RegisterBooleanPref(prefs::kPinUnlockAutosubmitEnabled,
IsPinAutosubmitFeatureEnabled());
}
bool IsPinDisabledByPolicy(PrefService* pref_service) {
......
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