| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service.h" |
| |
| #include <memory> |
| #include <utility> |
| |
| #include "ash/components/login/auth/user_context.h" |
| #include "ash/components/proximity_auth/proximity_auth_local_state_pref_manager.h" |
| #include "ash/components/proximity_auth/proximity_auth_profile_pref_manager.h" |
| #include "ash/components/proximity_auth/proximity_auth_system.h" |
| #include "ash/components/proximity_auth/screenlock_bridge.h" |
| #include "ash/constants/ash_features.h" |
| #include "ash/public/cpp/smartlock_state.h" |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| #include "base/guid.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/system/sys_info.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "base/time/time.h" |
| #include "base/values.h" |
| #include "base/version.h" |
| #include "build/build_config.h" |
| #include "chrome/browser/ash/login/easy_unlock/chrome_proximity_auth_client.h" |
| #include "chrome/browser/ash/login/easy_unlock/easy_unlock_key_manager.h" |
| #include "chrome/browser/ash/login/easy_unlock/easy_unlock_service_factory.h" |
| #include "chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager.h" |
| #include "chrome/browser/ash/login/easy_unlock/easy_unlock_tpm_key_manager_factory.h" |
| #include "chrome/browser/ash/login/session/user_session_manager.h" |
| #include "chrome/browser/ash/profiles/profile_helper.h" |
| #include "chrome/browser/browser_process.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/common/extensions/extension_constants.h" |
| #include "chrome/common/pref_names.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "chromeos/components/multidevice/logging/logging.h" |
| #include "chromeos/dbus/dbus_thread_manager.h" |
| #include "chromeos/dbus/power/power_manager_client.h" |
| #include "components/account_id/account_id.h" |
| #include "components/pref_registry/pref_registry_syncable.h" |
| #include "components/prefs/pref_registry_simple.h" |
| #include "components/prefs/pref_service.h" |
| #include "components/prefs/scoped_user_pref_update.h" |
| #include "components/session_manager/core/session_manager.h" |
| #include "components/user_manager/user.h" |
| #include "components/version_info/version_info.h" |
| #include "third_party/abseil-cpp/absl/types/optional.h" |
| #include "ui/base/l10n/l10n_util.h" |
| |
| namespace ash { |
| namespace { |
| |
| PrefService* GetLocalState() { |
| return g_browser_process ? g_browser_process->local_state() : NULL; |
| } |
| |
| void RecordAuthResultFailure( |
| EasyUnlockAuthAttempt::Type auth_attempt_type, |
| SmartLockMetricsRecorder::SmartLockAuthResultFailureReason failure_reason) { |
| if (auth_attempt_type == EasyUnlockAuthAttempt::TYPE_UNLOCK) |
| SmartLockMetricsRecorder::RecordAuthResultUnlockFailure(failure_reason); |
| else if (auth_attempt_type == EasyUnlockAuthAttempt::TYPE_SIGNIN) |
| SmartLockMetricsRecorder::RecordAuthResultSignInFailure(failure_reason); |
| } |
| |
| } // namespace |
| |
| // static |
| EasyUnlockService* EasyUnlockService::Get(Profile* profile) { |
| return EasyUnlockServiceFactory::GetForBrowserContext(profile); |
| } |
| |
| // static |
| EasyUnlockService* EasyUnlockService::GetForUser( |
| const user_manager::User& user) { |
| Profile* profile = ProfileHelper::Get()->GetProfileByUser(&user); |
| if (!profile) |
| return nullptr; |
| return EasyUnlockService::Get(profile); |
| } |
| |
| class EasyUnlockService::PowerMonitor : public PowerManagerClient::Observer { |
| public: |
| explicit PowerMonitor(EasyUnlockService* service) : service_(service) { |
| PowerManagerClient::Get()->AddObserver(this); |
| } |
| |
| PowerMonitor(const PowerMonitor&) = delete; |
| PowerMonitor& operator=(const PowerMonitor&) = delete; |
| |
| ~PowerMonitor() override { PowerManagerClient::Get()->RemoveObserver(this); } |
| |
| private: |
| // PowerManagerClient::Observer: |
| void SuspendImminent(power_manager::SuspendImminent::Reason reason) override { |
| service_->PrepareForSuspend(); |
| } |
| |
| void SuspendDone(base::TimeDelta sleep_duration) override { |
| base::ThreadTaskRunnerHandle::Get()->PostDelayedTask( |
| FROM_HERE, |
| base::BindOnce(&PowerMonitor::ResetWakingUp, |
| weak_ptr_factory_.GetWeakPtr()), |
| base::Seconds(5)); |
| service_->OnSuspendDone(); |
| service_->UpdateAppState(); |
| // Note that `this` may get deleted after `UpdateAppState` is called. |
| } |
| |
| void ResetWakingUp() { service_->UpdateAppState(); } |
| |
| EasyUnlockService* service_; |
| base::WeakPtrFactory<PowerMonitor> weak_ptr_factory_{this}; |
| }; |
| |
| EasyUnlockService::EasyUnlockService( |
| Profile* profile, |
| secure_channel::SecureChannelClient* secure_channel_client) |
| : profile_(profile), |
| secure_channel_client_(secure_channel_client), |
| proximity_auth_client_(profile), |
| shut_down_(false), |
| tpm_key_checked_(false) {} |
| |
| EasyUnlockService::~EasyUnlockService() = default; |
| |
| // static |
| void EasyUnlockService::RegisterProfilePrefs( |
| user_prefs::PrefRegistrySyncable* registry) { |
| registry->RegisterDictionaryPref(prefs::kEasyUnlockPairing); |
| proximity_auth::ProximityAuthProfilePrefManager::RegisterPrefs(registry); |
| } |
| |
| // static |
| void EasyUnlockService::RegisterPrefs(PrefRegistrySimple* registry) { |
| registry->RegisterDictionaryPref(prefs::kEasyUnlockHardlockState); |
| EasyUnlockTpmKeyManager::RegisterLocalStatePrefs(registry); |
| proximity_auth::ProximityAuthLocalStatePrefManager::RegisterPrefs(registry); |
| } |
| |
| // static |
| void EasyUnlockService::ResetLocalStateForUser(const AccountId& account_id) { |
| DCHECK(account_id.is_valid()); |
| |
| PrefService* local_state = GetLocalState(); |
| if (!local_state) |
| return; |
| |
| DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockHardlockState); |
| update->RemoveKey(account_id.GetUserEmail()); |
| |
| EasyUnlockTpmKeyManager::ResetLocalStateForUser(account_id); |
| } |
| |
| void EasyUnlockService::Initialize() { |
| InitializeInternal(); |
| } |
| |
| proximity_auth::ProximityAuthPrefManager* |
| EasyUnlockService::GetProximityAuthPrefManager() { |
| NOTREACHED(); |
| return nullptr; |
| } |
| |
| bool EasyUnlockService::IsAllowed() const { |
| if (shut_down_) |
| return false; |
| |
| if (!IsAllowedInternal()) |
| return false; |
| |
| return true; |
| } |
| |
| bool EasyUnlockService::IsEnabled() const { |
| return false; |
| } |
| |
| bool EasyUnlockService::IsChromeOSLoginEnabled() const { |
| return false; |
| } |
| |
| void EasyUnlockService::SetHardlockState( |
| SmartLockStateHandler::HardlockState state) { |
| if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) |
| return; |
| |
| const AccountId& account_id = GetAccountId(); |
| if (!account_id.is_valid()) |
| return; |
| |
| if (state == GetHardlockState()) |
| return; |
| |
| SetHardlockStateForUser(account_id, state); |
| } |
| |
| SmartLockStateHandler::HardlockState EasyUnlockService::GetHardlockState() |
| const { |
| if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) |
| return SmartLockStateHandler::NO_HARDLOCK; |
| |
| SmartLockStateHandler::HardlockState state; |
| if (GetPersistedHardlockState(&state)) |
| return state; |
| |
| return SmartLockStateHandler::NO_HARDLOCK; |
| } |
| |
| bool EasyUnlockService::GetPersistedHardlockState( |
| SmartLockStateHandler::HardlockState* state) const { |
| if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) |
| return false; |
| |
| const AccountId& account_id = GetAccountId(); |
| if (!account_id.is_valid()) |
| return false; |
| |
| PrefService* local_state = GetLocalState(); |
| if (!local_state) |
| return false; |
| |
| const base::Value* dict = |
| local_state->GetDictionary(prefs::kEasyUnlockHardlockState); |
| if (!dict) |
| return false; |
| |
| absl::optional<int> state_int = dict->FindIntKey(account_id.GetUserEmail()); |
| if (!state_int.has_value()) |
| return false; |
| |
| *state = static_cast<SmartLockStateHandler::HardlockState>(state_int.value()); |
| return true; |
| } |
| |
| SmartLockStateHandler* EasyUnlockService::GetSmartLockStateHandler() { |
| if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) |
| return nullptr; |
| |
| if (!IsAllowed()) |
| return nullptr; |
| if (!smartlock_state_handler_) { |
| smartlock_state_handler_ = std::make_unique<SmartLockStateHandler>( |
| GetAccountId(), GetHardlockState(), |
| proximity_auth::ScreenlockBridge::Get(), GetProximityAuthPrefManager()); |
| } |
| return smartlock_state_handler_.get(); |
| } |
| |
| void EasyUnlockService::UpdateSmartLockState(SmartLockState state) { |
| if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) { |
| if (smart_lock_state_ && state == smart_lock_state_.value()) |
| return; |
| |
| if (!proximity_auth::ScreenlockBridge::Get()->IsLocked()) |
| return; |
| |
| proximity_auth::ScreenlockBridge::Get()->lock_handler()->SetSmartLockState( |
| GetAccountId(), state); |
| smart_lock_state_ = state; |
| |
| // TODO(https://crbug.com/1233614): Eventually we would like to remove |
| // auth_type.mojom where AuthType lives, but this will require further |
| // investigation. This logic was copied from |
| // SmartLockStateHandler::UpdateScreenlockAuthType. |
| // Do not override online signin. |
| const proximity_auth::mojom::AuthType existing_auth_type = |
| proximity_auth::ScreenlockBridge::Get()->lock_handler()->GetAuthType( |
| GetAccountId()); |
| if (existing_auth_type == proximity_auth::mojom::AuthType::ONLINE_SIGN_IN) |
| return; |
| |
| if (smart_lock_state_ == SmartLockState::kPhoneAuthenticated) { |
| if (existing_auth_type != proximity_auth::mojom::AuthType::USER_CLICK) { |
| proximity_auth::ScreenlockBridge::Get()->lock_handler()->SetAuthType( |
| GetAccountId(), proximity_auth::mojom::AuthType::USER_CLICK, |
| l10n_util::GetStringUTF16( |
| IDS_EASY_UNLOCK_SCREENLOCK_USER_POD_AUTH_VALUE)); |
| } |
| } else if (existing_auth_type != |
| proximity_auth::mojom::AuthType::OFFLINE_PASSWORD) { |
| proximity_auth::ScreenlockBridge::Get()->lock_handler()->SetAuthType( |
| GetAccountId(), proximity_auth::mojom::AuthType::OFFLINE_PASSWORD, |
| std::u16string()); |
| } |
| |
| if (state != SmartLockState::kPhoneAuthenticated && auth_attempt_) { |
| // Clean up existing auth attempt if we can no longer authenticate the |
| // remote device. |
| auth_attempt_.reset(); |
| |
| if (!IsSmartLockStateValidOnRemoteAuthFailure()) |
| HandleAuthFailure(GetAccountId()); |
| } |
| |
| return; |
| } |
| |
| SmartLockStateHandler* handler = GetSmartLockStateHandler(); |
| if (!handler) |
| return; |
| |
| handler->ChangeState(state); |
| |
| if (state != SmartLockState::kPhoneAuthenticated && auth_attempt_) { |
| auth_attempt_.reset(); |
| |
| if (!handler->InStateValidOnRemoteAuthFailure()) |
| HandleAuthFailure(GetAccountId()); |
| } |
| } |
| |
| void EasyUnlockService::OnUserEnteredPassword() { |
| if (proximity_auth_system_) |
| proximity_auth_system_->CancelConnectionAttempt(); |
| } |
| |
| bool EasyUnlockService::AttemptAuth(const AccountId& account_id) { |
| const EasyUnlockAuthAttempt::Type auth_attempt_type = |
| GetType() == TYPE_REGULAR ? EasyUnlockAuthAttempt::TYPE_UNLOCK |
| : EasyUnlockAuthAttempt::TYPE_SIGNIN; |
| PA_LOG(VERBOSE) << "User began auth attempt (unlock or sign in attempt)."; |
| |
| if (auth_attempt_) { |
| PA_LOG(VERBOSE) << "Already attempting auth, skipping this request."; |
| return false; |
| } |
| |
| if (!GetAccountId().is_valid()) { |
| PA_LOG(ERROR) << "Empty user account. Auth attempt failed."; |
| RecordAuthResultFailure( |
| auth_attempt_type, |
| SmartLockMetricsRecorder::SmartLockAuthResultFailureReason:: |
| kEmptyUserAccount); |
| return false; |
| } |
| |
| if (GetAccountId() != account_id) { |
| RecordAuthResultFailure( |
| auth_attempt_type, |
| SmartLockMetricsRecorder::SmartLockAuthResultFailureReason:: |
| kInvalidAccoundId); |
| |
| PA_LOG(ERROR) << "Check failed: " << GetAccountId().Serialize() << " vs " |
| << account_id.Serialize(); |
| return false; |
| } |
| |
| auth_attempt_ = |
| std::make_unique<EasyUnlockAuthAttempt>(account_id, auth_attempt_type); |
| if (!auth_attempt_->Start()) { |
| RecordAuthResultFailure( |
| auth_attempt_type, |
| SmartLockMetricsRecorder::SmartLockAuthResultFailureReason:: |
| kAuthAttemptCannotStart); |
| auth_attempt_.reset(); |
| return false; |
| } |
| |
| // TODO(tengs): We notify ProximityAuthSystem whenever unlock attempts are |
| // attempted. However, we ideally should refactor the auth attempt logic to |
| // the proximity_auth component. |
| if (!proximity_auth_system_) { |
| PA_LOG(ERROR) << "No ProximityAuthSystem present."; |
| return false; |
| } |
| |
| proximity_auth_system_->OnAuthAttempted(); |
| return true; |
| } |
| |
| void EasyUnlockService::FinalizeUnlock(bool success) { |
| if (!auth_attempt_) |
| return; |
| |
| set_will_authenticate_using_easy_unlock(true); |
| auth_attempt_->FinalizeUnlock(GetAccountId(), success); |
| |
| // If successful, allow |auth_attempt_| to continue until |
| // UpdateSmartLockState() is called (indicating screen unlock). |
| |
| // Make sure that the lock screen is updated on failure. |
| if (!success) { |
| auth_attempt_.reset(); |
| RecordEasyUnlockScreenUnlockEvent(EASY_UNLOCK_FAILURE); |
| if (!base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) { |
| HandleAuthFailure(GetAccountId()); |
| } |
| } |
| |
| if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) { |
| NotifySmartLockAuthResult(success); |
| } |
| } |
| |
| void EasyUnlockService::FinalizeSignin(const std::string& key) { |
| if (!auth_attempt_) |
| return; |
| |
| std::string wrapped_secret = GetWrappedSecret(); |
| if (!wrapped_secret.empty()) |
| auth_attempt_->FinalizeSignin(GetAccountId(), wrapped_secret, key); |
| |
| // If successful, allow |auth_attempt_| to continue until |
| // UpdateSmartLockState() is called (indicating sign in). |
| |
| // Processing empty key is equivalent to auth cancellation. In this case the |
| // signin request will not actually be processed by login stack, so the lock |
| // screen state should be set from here. |
| bool success = !key.empty(); |
| |
| if (success) { |
| set_will_authenticate_using_easy_unlock(true); |
| } else { |
| auth_attempt_.reset(); |
| if (!base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) { |
| HandleAuthFailure(GetAccountId()); |
| } |
| } |
| |
| if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) { |
| NotifySmartLockAuthResult(success); |
| } |
| } |
| |
| void EasyUnlockService::HandleAuthFailure(const AccountId& account_id) { |
| if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) { |
| NotifySmartLockAuthResult(/*success=*/false); |
| return; |
| } |
| |
| if (account_id != GetAccountId()) |
| return; |
| |
| if (!smartlock_state_handler_.get()) |
| return; |
| |
| smartlock_state_handler_->SetHardlockState( |
| SmartLockStateHandler::LOGIN_FAILED); |
| } |
| |
| void EasyUnlockService::CheckCryptohomeKeysAndMaybeHardlock() { |
| const AccountId& account_id = GetAccountId(); |
| if (!account_id.is_valid() || !IsChromeOSLoginEnabled()) |
| return; |
| |
| const base::ListValue* device_list = GetRemoteDevices(); |
| std::set<std::string> paired_devices; |
| if (device_list) { |
| EasyUnlockDeviceKeyDataList parsed_paired; |
| EasyUnlockKeyManager::RemoteDeviceRefListToDeviceDataList(*device_list, |
| &parsed_paired); |
| for (const auto& device_key_data : parsed_paired) |
| paired_devices.insert(device_key_data.psk); |
| } |
| if (paired_devices.empty()) { |
| SetHardlockState(SmartLockStateHandler::NO_PAIRING); |
| return; |
| } |
| |
| // No need to compare if a change is already recorded. |
| if (GetHardlockState() == SmartLockStateHandler::PAIRING_CHANGED || |
| GetHardlockState() == SmartLockStateHandler::PAIRING_ADDED) { |
| return; |
| } |
| |
| EasyUnlockKeyManager* key_manager = |
| UserSessionManager::GetInstance()->GetEasyUnlockKeyManager(); |
| DCHECK(key_manager); |
| |
| const user_manager::User* const user = |
| user_manager::UserManager::Get()->FindUser(account_id); |
| DCHECK(user); |
| key_manager->GetDeviceDataList( |
| UserContext(*user), |
| base::BindOnce(&EasyUnlockService::OnCryptohomeKeysFetchedForChecking, |
| weak_ptr_factory_.GetWeakPtr(), account_id, |
| paired_devices)); |
| } |
| |
| void EasyUnlockService::Shutdown() { |
| if (shut_down_) |
| return; |
| shut_down_ = true; |
| |
| ShutdownInternal(); |
| |
| ResetSmartLockState(); |
| proximity_auth_system_.reset(); |
| power_monitor_.reset(); |
| |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| } |
| |
| void EasyUnlockService::UpdateAppState() { |
| if (IsAllowed()) { |
| EnsureTpmKeyPresentIfNeeded(); |
| |
| if (proximity_auth_system_) |
| proximity_auth_system_->Start(); |
| |
| if (!power_monitor_) |
| power_monitor_ = std::make_unique<PowerMonitor>(this); |
| } |
| } |
| |
| void EasyUnlockService::ResetSmartLockState() { |
| smartlock_state_handler_.reset(); |
| auth_attempt_.reset(); |
| } |
| |
| void EasyUnlockService::SetSmartLockHardlockedState( |
| SmartLockStateHandler::HardlockState state) { |
| if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) |
| return; |
| |
| if (GetSmartLockStateHandler()) { |
| smartlock_state_handler_->SetHardlockState(state); |
| smartlock_state_handler_->MaybeShowHardlockUI(); |
| } |
| if (state != SmartLockStateHandler::NO_HARDLOCK) |
| auth_attempt_.reset(); |
| } |
| |
| void EasyUnlockService::SetHardlockStateForUser( |
| const AccountId& account_id, |
| SmartLockStateHandler::HardlockState state) { |
| if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) |
| return; |
| |
| DCHECK(account_id.is_valid()); |
| |
| PrefService* local_state = GetLocalState(); |
| if (!local_state) |
| return; |
| |
| // Disallow setting the hardlock state if the password is currently being |
| // forced. |
| if (GetSmartLockStateHandler() && |
| GetSmartLockStateHandler()->state() == |
| SmartLockState::kPasswordReentryRequired) { |
| return; |
| } |
| |
| DictionaryPrefUpdate update(local_state, prefs::kEasyUnlockHardlockState); |
| update->SetIntKey(account_id.GetUserEmail(), static_cast<int>(state)); |
| |
| if (GetAccountId() == account_id) |
| SetSmartLockHardlockedState(state); |
| } |
| |
| SmartLockMetricsRecorder::SmartLockAuthEventPasswordState |
| EasyUnlockService::GetSmartUnlockPasswordAuthEvent() const { |
| DCHECK(IsEnabled()); |
| |
| if (GetHardlockState() != SmartLockStateHandler::NO_HARDLOCK) { |
| switch (GetHardlockState()) { |
| case SmartLockStateHandler::NO_PAIRING: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kNoPairing; |
| case SmartLockStateHandler::USER_HARDLOCK: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kUserHardlock; |
| case SmartLockStateHandler::PAIRING_CHANGED: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kPairingChanged; |
| case SmartLockStateHandler::LOGIN_FAILED: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kLoginFailed; |
| case SmartLockStateHandler::PAIRING_ADDED: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kPairingAdded; |
| case SmartLockStateHandler::LOGIN_DISABLED: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kLoginWithSmartLockDisabled; |
| default: |
| NOTREACHED(); |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kUnknownState; |
| } |
| } else if (!base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp) && |
| !smartlock_state_handler()) { |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kUnknownState; |
| } else if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp) && |
| !smart_lock_state_) { |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kUnknownState; |
| } else { |
| SmartLockState state = |
| (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) |
| ? smart_lock_state_.value() |
| : smartlock_state_handler()->state(); |
| switch (state) { |
| case SmartLockState::kInactive: |
| case SmartLockState::kDisabled: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kServiceNotActive; |
| case SmartLockState::kBluetoothDisabled: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kNoBluetooth; |
| case SmartLockState::kConnectingToPhone: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kBluetoothConnecting; |
| case SmartLockState::kPhoneNotFound: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kCouldNotConnectToPhone; |
| case SmartLockState::kPhoneNotAuthenticated: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kNotAuthenticated; |
| case SmartLockState::kPhoneFoundLockedAndProximate: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kPhoneLocked; |
| case SmartLockState::kPhoneFoundUnlockedAndDistant: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kRssiTooLow; |
| case SmartLockState::kPhoneFoundLockedAndDistant: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kPhoneLockedAndRssiTooLow; |
| case SmartLockState::kPhoneAuthenticated: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kAuthenticatedPhone; |
| case SmartLockState::kPasswordReentryRequired: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kForcedReauth; |
| case SmartLockState::kPhoneNotLockable: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kPhoneNotLockable; |
| case SmartLockState::kPrimaryUserAbsent: |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kPrimaryUserAbsent; |
| default: |
| NOTREACHED(); |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kUnknownState; |
| } |
| } |
| |
| NOTREACHED(); |
| return SmartLockMetricsRecorder::SmartLockAuthEventPasswordState:: |
| kUnknownState; |
| } |
| |
| EasyUnlockAuthEvent EasyUnlockService::GetPasswordAuthEvent() const { |
| DCHECK(IsEnabled()); |
| |
| if (GetHardlockState() != SmartLockStateHandler::NO_HARDLOCK) { |
| switch (GetHardlockState()) { |
| case SmartLockStateHandler::NO_HARDLOCK: |
| NOTREACHED(); |
| return EASY_UNLOCK_AUTH_EVENT_COUNT; |
| case SmartLockStateHandler::NO_PAIRING: |
| return PASSWORD_ENTRY_NO_PAIRING; |
| case SmartLockStateHandler::USER_HARDLOCK: |
| return PASSWORD_ENTRY_USER_HARDLOCK; |
| case SmartLockStateHandler::PAIRING_CHANGED: |
| return PASSWORD_ENTRY_PAIRING_CHANGED; |
| case SmartLockStateHandler::LOGIN_FAILED: |
| return PASSWORD_ENTRY_LOGIN_FAILED; |
| case SmartLockStateHandler::PAIRING_ADDED: |
| return PASSWORD_ENTRY_PAIRING_ADDED; |
| case SmartLockStateHandler::LOGIN_DISABLED: |
| return PASSWORD_ENTRY_LOGIN_DISABLED; |
| } |
| } else if (!base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp) && |
| !smartlock_state_handler()) { |
| return PASSWORD_ENTRY_NO_SMARTLOCK_STATE_HANDLER; |
| } else if (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp) && |
| !smart_lock_state_) { |
| return PASSWORD_ENTRY_NO_SMARTLOCK_STATE_HANDLER; |
| } else { |
| SmartLockState state = |
| (base::FeatureList::IsEnabled(ash::features::kSmartLockUIRevamp)) |
| ? smart_lock_state_.value() |
| : smartlock_state_handler()->state(); |
| |
| switch (state) { |
| case SmartLockState::kInactive: |
| case SmartLockState::kDisabled: |
| return PASSWORD_ENTRY_SERVICE_NOT_ACTIVE; |
| case SmartLockState::kBluetoothDisabled: |
| return PASSWORD_ENTRY_NO_BLUETOOTH; |
| case SmartLockState::kConnectingToPhone: |
| return PASSWORD_ENTRY_BLUETOOTH_CONNECTING; |
| case SmartLockState::kPhoneNotFound: |
| return PASSWORD_ENTRY_NO_PHONE; |
| case SmartLockState::kPhoneNotAuthenticated: |
| return PASSWORD_ENTRY_PHONE_NOT_AUTHENTICATED; |
| case SmartLockState::kPhoneFoundLockedAndProximate: |
| return PASSWORD_ENTRY_PHONE_LOCKED; |
| case SmartLockState::kPhoneNotLockable: |
| return PASSWORD_ENTRY_PHONE_NOT_LOCKABLE; |
| case SmartLockState::kPhoneFoundUnlockedAndDistant: |
| return PASSWORD_ENTRY_RSSI_TOO_LOW; |
| case SmartLockState::kPhoneFoundLockedAndDistant: |
| return PASSWORD_ENTRY_PHONE_LOCKED_AND_RSSI_TOO_LOW; |
| case SmartLockState::kPhoneAuthenticated: |
| return PASSWORD_ENTRY_WITH_AUTHENTICATED_PHONE; |
| case SmartLockState::kPasswordReentryRequired: |
| return PASSWORD_ENTRY_FORCED_REAUTH; |
| case SmartLockState::kPrimaryUserAbsent: |
| return PASSWORD_ENTRY_PRIMARY_USER_ABSENT; |
| } |
| } |
| |
| NOTREACHED(); |
| return EASY_UNLOCK_AUTH_EVENT_COUNT; |
| } |
| |
| void EasyUnlockService::SetProximityAuthDevices( |
| const AccountId& account_id, |
| const multidevice::RemoteDeviceRefList& remote_devices, |
| absl::optional<multidevice::RemoteDeviceRef> local_device) { |
| UMA_HISTOGRAM_COUNTS_100("SmartLock.EnabledDevicesCount", |
| remote_devices.size()); |
| |
| if (remote_devices.size() == 0) { |
| proximity_auth_system_.reset(); |
| return; |
| } |
| |
| if (!proximity_auth_system_) { |
| PA_LOG(VERBOSE) << "Creating ProximityAuthSystem."; |
| proximity_auth_system_ = |
| std::make_unique<proximity_auth::ProximityAuthSystem>( |
| GetType() == TYPE_SIGNIN |
| ? proximity_auth::ProximityAuthSystem::SIGN_IN |
| : proximity_auth::ProximityAuthSystem::SESSION_LOCK, |
| proximity_auth_client(), secure_channel_client_); |
| } |
| |
| proximity_auth_system_->SetRemoteDevicesForUser(account_id, remote_devices, |
| local_device); |
| proximity_auth_system_->Start(); |
| } |
| |
| void EasyUnlockService::StartFeatureUsageMetrics() { |
| feature_usage_metrics_ = std::make_unique<SmartLockFeatureUsageMetrics>( |
| base::BindRepeating(&EasyUnlockService::IsEligible, |
| base::Unretained(this)), |
| base::BindRepeating(&EasyUnlockService::IsEnabled, |
| base::Unretained(this))); |
| |
| SmartLockMetricsRecorder::SetUsageRecorderInstance( |
| feature_usage_metrics_.get()); |
| } |
| |
| void EasyUnlockService::StopFeatureUsageMetrics() { |
| feature_usage_metrics_.reset(); |
| SmartLockMetricsRecorder::SetUsageRecorderInstance(nullptr); |
| } |
| |
| void EasyUnlockService::OnCryptohomeKeysFetchedForChecking( |
| const AccountId& account_id, |
| const std::set<std::string> paired_devices, |
| bool success, |
| const EasyUnlockDeviceKeyDataList& key_data_list) { |
| DCHECK(account_id.is_valid() && !paired_devices.empty()); |
| |
| if (!success) { |
| SetHardlockStateForUser(account_id, SmartLockStateHandler::NO_PAIRING); |
| return; |
| } |
| |
| std::set<std::string> devices_in_cryptohome; |
| for (const auto& device_key_data : key_data_list) |
| devices_in_cryptohome.insert(device_key_data.psk); |
| |
| if (paired_devices != devices_in_cryptohome || |
| GetHardlockState() == SmartLockStateHandler::NO_PAIRING) { |
| SetHardlockStateForUser(account_id, |
| devices_in_cryptohome.empty() |
| ? SmartLockStateHandler::PAIRING_ADDED |
| : SmartLockStateHandler::PAIRING_CHANGED); |
| } |
| } |
| |
| void EasyUnlockService::PrepareForSuspend() { |
| if (smartlock_state_handler_ && smartlock_state_handler_->IsActive()) |
| UpdateSmartLockState(SmartLockState::kConnectingToPhone); |
| if (proximity_auth_system_) |
| proximity_auth_system_->OnSuspend(); |
| } |
| |
| void EasyUnlockService::OnSuspendDone() { |
| if (proximity_auth_system_) |
| proximity_auth_system_->OnSuspendDone(); |
| } |
| |
| void EasyUnlockService::EnsureTpmKeyPresentIfNeeded() { |
| if (tpm_key_checked_ || GetType() != TYPE_REGULAR || GetAccountId().empty() || |
| GetHardlockState() == SmartLockStateHandler::NO_PAIRING) { |
| return; |
| } |
| |
| // If this is called before the session is started, the chances are Chrome |
| // is restarting in order to apply user flags. Don't check TPM keys in this |
| // case. |
| if (!session_manager::SessionManager::Get() || |
| !session_manager::SessionManager::Get()->IsSessionStarted()) |
| return; |
| |
| // TODO(tbarzic): Set check_private_key only if previous sign-in attempt |
| // failed. |
| EasyUnlockTpmKeyManagerFactory::GetInstance()->Get(profile_)->PrepareTpmKey( |
| /*check_private_key=*/true, base::OnceClosure()); |
| |
| tpm_key_checked_ = true; |
| } |
| |
| bool EasyUnlockService::IsSmartLockStateValidOnRemoteAuthFailure() const { |
| // Note that NO_PHONE is not valid in this case because the phone may close |
| // the connection if the auth challenge sent to it is invalid. This case |
| // should be handled as authentication failure. |
| return smart_lock_state_ == SmartLockState::kInactive || |
| smart_lock_state_ == SmartLockState::kDisabled || |
| smart_lock_state_ == SmartLockState::kBluetoothDisabled || |
| smart_lock_state_ == SmartLockState::kPhoneFoundLockedAndProximate; |
| } |
| |
| void EasyUnlockService::NotifySmartLockAuthResult(bool success) { |
| if (!proximity_auth::ScreenlockBridge::Get()->IsLocked()) |
| return; |
| |
| proximity_auth::ScreenlockBridge::Get() |
| ->lock_handler() |
| ->NotifySmartLockAuthResult(GetAccountId(), success); |
| } |
| |
| std::string EasyUnlockService::GetLastRemoteStatusUnlockForLogging() { |
| if (proximity_auth_system_) { |
| return proximity_auth_system_->GetLastRemoteStatusUnlockForLogging(); |
| } |
| return std::string(); |
| } |
| |
| } // namespace ash |