| // 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/chromeos/login/lock/screen_locker.h" |
| |
| #include <string> |
| #include <vector> |
| |
| #include "ash/public/cpp/ash_switches.h" |
| #include "ash/public/cpp/login_screen.h" |
| #include "ash/public/cpp/login_screen_model.h" |
| #include "ash/public/cpp/login_types.h" |
| #include "ash/public/interfaces/constants.mojom.h" |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/lazy_instance.h" |
| #include "base/location.h" |
| #include "base/macros.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/message_loop/message_loop.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "base/metrics/user_metrics.h" |
| #include "base/single_thread_task_runner.h" |
| #include "base/stl_util.h" |
| #include "base/strings/string_number_conversions.h" |
| #include "base/strings/string_util.h" |
| #include "base/task/post_task.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/chromeos/accessibility/accessibility_manager.h" |
| #include "chrome/browser/chromeos/authpolicy/authpolicy_helper.h" |
| #include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_service.h" |
| #include "chrome/browser/chromeos/login/helper.h" |
| #include "chrome/browser/chromeos/login/lock/views_screen_locker.h" |
| #include "chrome/browser/chromeos/login/login_auth_recorder.h" |
| #include "chrome/browser/chromeos/login/quick_unlock/fingerprint_storage.h" |
| #include "chrome/browser/chromeos/login/quick_unlock/pin_backend.h" |
| #include "chrome/browser/chromeos/login/quick_unlock/pin_storage_prefs.h" |
| #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_factory.h" |
| #include "chrome/browser/chromeos/login/quick_unlock/quick_unlock_storage.h" |
| #include "chrome/browser/chromeos/login/session/user_session_manager.h" |
| #include "chrome/browser/chromeos/login/supervised/supervised_user_authentication.h" |
| #include "chrome/browser/chromeos/login/ui/user_adding_screen.h" |
| #include "chrome/browser/chromeos/login/users/chrome_user_manager.h" |
| #include "chrome/browser/chromeos/login/users/supervised_user_manager.h" |
| #include "chrome/browser/chromeos/profiles/profile_helper.h" |
| #include "chrome/browser/lifetime/application_lifetime.h" |
| #include "chrome/browser/ui/ash/login_screen_client.h" |
| #include "chrome/browser/ui/ash/session_controller_client_impl.h" |
| #include "chrome/common/chrome_switches.h" |
| #include "chrome/grit/browser_resources.h" |
| #include "chrome/grit/generated_resources.h" |
| #include "chromeos/audio/chromeos_sounds.h" |
| #include "chromeos/dbus/biod/constants.pb.h" |
| #include "chromeos/dbus/session_manager/session_manager_client.h" |
| #include "chromeos/login/auth/authenticator.h" |
| #include "chromeos/login/auth/extended_authenticator.h" |
| #include "chromeos/login/session/session_termination_manager.h" |
| #include "components/password_manager/core/browser/hash_password_manager.h" |
| #include "components/session_manager/core/session_manager.h" |
| #include "components/session_manager/core/session_manager_observer.h" |
| #include "components/user_manager/user_manager.h" |
| #include "components/user_manager/user_type.h" |
| #include "content/public/browser/browser_task_traits.h" |
| #include "content/public/browser/browser_thread.h" |
| #include "content/public/browser/notification_service.h" |
| #include "content/public/browser/url_data_source.h" |
| #include "content/public/browser/web_contents.h" |
| #include "content/public/browser/web_ui.h" |
| #include "content/public/common/service_manager_connection.h" |
| #include "services/audio/public/cpp/sounds/sounds_manager.h" |
| #include "services/device/public/mojom/constants.mojom.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "ui/base/resource/resource_bundle.h" |
| #include "ui/display/display.h" |
| #include "ui/display/screen.h" |
| #include "ui/gfx/image/image.h" |
| #include "url/gurl.h" |
| |
| using base::UserMetricsAction; |
| using content::BrowserThread; |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| // Returns true if fingerprint authentication is available for |user|. |
| bool IsFingerprintAvailableForUser(const user_manager::User* user) { |
| quick_unlock::QuickUnlockStorage* quick_unlock_storage = |
| quick_unlock::QuickUnlockFactory::GetForUser(user); |
| return quick_unlock_storage && |
| quick_unlock_storage->IsFingerprintAuthenticationAvailable(); |
| } |
| |
| // Observer to start ScreenLocker when locking the screen is requested. |
| class ScreenLockObserver : public SessionManagerClient::StubDelegate, |
| public UserAddingScreen::Observer, |
| public session_manager::SessionManagerObserver { |
| public: |
| ScreenLockObserver() : session_started_(false) { |
| session_manager::SessionManager::Get()->AddObserver(this); |
| SessionManagerClient::Get()->SetStubDelegate(this); |
| } |
| |
| ~ScreenLockObserver() override { |
| session_manager::SessionManager::Get()->RemoveObserver(this); |
| if (SessionManagerClient::Get()) |
| SessionManagerClient::Get()->SetStubDelegate(nullptr); |
| } |
| |
| bool session_started() const { return session_started_; } |
| |
| // SessionManagerClient::StubDelegate overrides: |
| void LockScreenForStub() override { |
| ScreenLocker::HandleShowLockScreenRequest(); |
| } |
| |
| // session_manager::SessionManagerObserver: |
| void OnSessionStateChanged() override { |
| // Only set MarkStrongAuth for the first time session becomes active, which |
| // is when user first sign-in. |
| // For unlocking case which state changes from active->lock->active, it |
| // should be handled in OnPasswordAuthSuccess. |
| if (session_started_ || |
| session_manager::SessionManager::Get()->session_state() != |
| session_manager::SessionState::ACTIVE) { |
| return; |
| } |
| |
| session_started_ = true; |
| |
| // The user session has just started, so the user has logged in. Mark a |
| // strong authentication to allow them to use PIN to unlock the device. |
| user_manager::User* user = |
| user_manager::UserManager::Get()->GetActiveUser(); |
| quick_unlock::QuickUnlockStorage* quick_unlock_storage = |
| quick_unlock::QuickUnlockFactory::GetForUser(user); |
| if (quick_unlock_storage) |
| quick_unlock_storage->MarkStrongAuth(); |
| } |
| |
| // UserAddingScreen::Observer overrides: |
| void OnUserAddingFinished() override { |
| UserAddingScreen::Get()->RemoveObserver(this); |
| ScreenLocker::HandleShowLockScreenRequest(); |
| } |
| |
| private: |
| bool session_started_; |
| |
| DISALLOW_COPY_AND_ASSIGN(ScreenLockObserver); |
| }; |
| |
| ScreenLockObserver* g_screen_lock_observer = nullptr; |
| |
| } // namespace |
| |
| // static |
| ScreenLocker* ScreenLocker::screen_locker_ = nullptr; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // ScreenLocker::Delegate, public: |
| |
| ScreenLocker::Delegate::Delegate() = default; |
| |
| ScreenLocker::Delegate::~Delegate() = default; |
| |
| ////////////////////////////////////////////////////////////////////////////// |
| // ScreenLocker, public: |
| |
| ScreenLocker::ScreenLocker(const user_manager::UserList& users) |
| : users_(users), fingerprint_observer_binding_(this), weak_factory_(this) { |
| DCHECK(!screen_locker_); |
| screen_locker_ = this; |
| |
| ui::ResourceBundle& bundle = ui::ResourceBundle::GetSharedInstance(); |
| audio::SoundsManager* manager = audio::SoundsManager::Get(); |
| manager->Initialize(SOUND_LOCK, |
| bundle.GetRawDataResource(IDR_SOUND_LOCK_WAV)); |
| manager->Initialize(SOUND_UNLOCK, |
| bundle.GetRawDataResource(IDR_SOUND_UNLOCK_WAV)); |
| service_manager::Connector* connector = |
| content::ServiceManagerConnection::GetForProcess()->GetConnector(); |
| connector->BindInterface(device::mojom::kServiceName, &fp_service_); |
| |
| device::mojom::FingerprintObserverPtr observer; |
| fingerprint_observer_binding_.Bind(mojo::MakeRequest(&observer)); |
| fp_service_->AddFingerprintObserver(std::move(observer)); |
| } |
| |
| void ScreenLocker::Init() { |
| input_method::InputMethodManager* imm = |
| input_method::InputMethodManager::Get(); |
| saved_ime_state_ = imm->GetActiveIMEState(); |
| imm->SetState(saved_ime_state_->Clone()); |
| |
| authenticator_ = UserSessionManager::GetInstance()->CreateAuthenticator(this); |
| extended_authenticator_ = ExtendedAuthenticator::Create(this); |
| |
| // Create delegate that calls into the views-based lock screen via mojo. |
| views_screen_locker_ = std::make_unique<ViewsScreenLocker>(this); |
| delegate_ = views_screen_locker_.get(); |
| |
| // Create and display lock screen. |
| CHECK(LoginScreenClient::HasInstance()); |
| LoginScreenClient::Get()->login_screen()->ShowLockScreen(base::BindOnce( |
| [](ViewsScreenLocker* screen_locker, bool did_show) { |
| CHECK(did_show); |
| screen_locker->OnLockScreenReady(); |
| |
| content::NotificationService::current()->Notify( |
| chrome::NOTIFICATION_LOGIN_OR_LOCK_WEBUI_VISIBLE, |
| content::NotificationService::AllSources(), |
| content::NotificationService::NoDetails()); |
| }, |
| views_screen_locker_.get())); |
| |
| views_screen_locker_->Init(); |
| |
| // Start locking on ash side. |
| SessionControllerClientImpl::Get()->StartLock(base::BindOnce( |
| &ScreenLocker::OnStartLockCallback, weak_factory_.GetWeakPtr())); |
| } |
| |
| void ScreenLocker::OnAuthFailure(const AuthFailure& error) { |
| base::RecordAction(UserMetricsAction("ScreenLocker_OnLoginFailure")); |
| if (authentication_start_time_.is_null()) { |
| LOG(ERROR) << "Start time is not set at authentication failure"; |
| } else { |
| base::TimeDelta delta = base::Time::Now() - authentication_start_time_; |
| VLOG(1) << "Authentication failure: " << delta.InSecondsF() << " second(s)"; |
| UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationFailureTime", delta); |
| } |
| |
| UMA_HISTOGRAM_ENUMERATION("ScreenLocker.AuthenticationFailure", |
| unlock_attempt_type_, UnlockType::AUTH_COUNT); |
| |
| EnableInput(); |
| // Don't enable signout button here as we're showing |
| // MessageBubble. |
| |
| delegate_->ShowErrorMessage(incorrect_passwords_count_++ |
| ? IDS_LOGIN_ERROR_AUTHENTICATING_2ND_TIME |
| : IDS_LOGIN_ERROR_AUTHENTICATING, |
| HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT); |
| |
| if (auth_status_consumer_) |
| auth_status_consumer_->OnAuthFailure(error); |
| |
| if (on_auth_complete_) |
| std::move(on_auth_complete_).Run(false); |
| } |
| |
| void ScreenLocker::OnAuthSuccess(const UserContext& user_context) { |
| CHECK(!base::ContainsKey(users_with_disabled_auth_, |
| user_context.GetAccountId())) |
| << "Authentication is disabled for this user."; |
| |
| incorrect_passwords_count_ = 0; |
| if (authentication_start_time_.is_null()) { |
| if (user_context.GetAccountId().is_valid()) |
| LOG(ERROR) << "Start time is not set at authentication success"; |
| } else { |
| base::TimeDelta delta = base::Time::Now() - authentication_start_time_; |
| VLOG(1) << "Authentication success: " << delta.InSecondsF() << " second(s)"; |
| UMA_HISTOGRAM_TIMES("ScreenLocker.AuthenticationSuccessTime", delta); |
| } |
| |
| UMA_HISTOGRAM_ENUMERATION("ScreenLocker.AuthenticationSuccess", |
| unlock_attempt_type_, UnlockType::AUTH_COUNT); |
| |
| const user_manager::User* user = |
| user_manager::UserManager::Get()->FindUser(user_context.GetAccountId()); |
| if (user) { |
| if (!user->is_active()) { |
| saved_ime_state_ = nullptr; |
| user_manager::UserManager::Get()->SwitchActiveUser( |
| user_context.GetAccountId()); |
| } |
| |
| // Reset the number of PIN attempts available to the user. We always do this |
| // because: |
| // 1. If the user signed in with a PIN, that means they should be able to |
| // continue signing in with a PIN. |
| // 2. If the user signed in with cryptohome keys, then the PIN timeout is |
| // going to be reset as well, so it is safe to reset the unlock attempt |
| // count. |
| quick_unlock::QuickUnlockStorage* quick_unlock_storage = |
| quick_unlock::QuickUnlockFactory::GetForUser(user); |
| if (quick_unlock_storage) { |
| quick_unlock_storage->pin_storage_prefs()->ResetUnlockAttemptCount(); |
| quick_unlock_storage->fingerprint_storage()->ResetUnlockAttemptCount(); |
| } |
| |
| UserSessionManager::GetInstance()->UpdateEasyUnlockKeys(user_context); |
| } else { |
| NOTREACHED() << "Logged in user not found."; |
| } |
| |
| if (on_auth_complete_) |
| std::move(on_auth_complete_).Run(true); |
| |
| if (auth_status_consumer_) |
| auth_status_consumer_->OnAuthSuccess(user_context); |
| weak_factory_.InvalidateWeakPtrs(); |
| |
| VLOG(1) << "Hiding the lock screen."; |
| chromeos::ScreenLocker::Hide(); |
| } |
| |
| void ScreenLocker::OnPasswordAuthSuccess(const UserContext& user_context) { |
| // The user has signed in using their password, so reset the PIN timeout. |
| quick_unlock::QuickUnlockStorage* quick_unlock_storage = |
| quick_unlock::QuickUnlockFactory::GetForAccountId( |
| user_context.GetAccountId()); |
| if (quick_unlock_storage) |
| quick_unlock_storage->MarkStrongAuth(); |
| SaveSyncPasswordHash(user_context); |
| } |
| |
| void ScreenLocker::EnableAuthForUser(const AccountId& account_id) { |
| const user_manager::User* user = FindUnlockUser(account_id); |
| CHECK(user) << "Invalid user - cannot enable authentication."; |
| |
| users_with_disabled_auth_.erase(account_id); |
| ash::LoginScreen::Get()->GetModel()->EnableAuthForUser(account_id); |
| } |
| |
| void ScreenLocker::DisableAuthForUser( |
| const AccountId& account_id, |
| const ash::AuthDisabledData& auth_disabled_data) { |
| const user_manager::User* user = FindUnlockUser(account_id); |
| CHECK(user) << "Invalid user - cannot disable authentication."; |
| |
| users_with_disabled_auth_.insert(account_id); |
| ash::LoginScreen::Get()->GetModel()->DisableAuthForUser(account_id, |
| auth_disabled_data); |
| } |
| |
| void ScreenLocker::Authenticate(const UserContext& user_context, |
| AuthenticateCallback callback) { |
| LOG_ASSERT(IsUserLoggedIn(user_context.GetAccountId())) |
| << "Invalid user trying to unlock."; |
| |
| // Do not attempt authentication if it is disabled for the user. |
| if (base::ContainsKey(users_with_disabled_auth_, |
| user_context.GetAccountId())) { |
| VLOG(1) << "Authentication disabled for user."; |
| if (auth_status_consumer_) |
| auth_status_consumer_->OnAuthFailure( |
| AuthFailure(AuthFailure::AUTH_DISABLED)); |
| if (callback) |
| std::move(callback).Run(false); |
| return; |
| } |
| |
| DCHECK(!on_auth_complete_); |
| on_auth_complete_ = std::move(callback); |
| unlock_attempt_type_ = AUTH_PASSWORD; |
| |
| authentication_start_time_ = base::Time::Now(); |
| if (user_context.IsUsingPin()) |
| unlock_attempt_type_ = AUTH_PIN; |
| |
| const user_manager::User* user = FindUnlockUser(user_context.GetAccountId()); |
| if (user) { |
| // Check to see if the user submitted a PIN and it is valid. |
| if (unlock_attempt_type_ == AUTH_PIN) { |
| quick_unlock::PinBackend::GetInstance()->TryAuthenticate( |
| user_context.GetAccountId(), *user_context.GetKey(), |
| base::BindOnce(&ScreenLocker::OnPinAttemptDone, |
| weak_factory_.GetWeakPtr(), user_context)); |
| // OnPinAttemptDone will call ContinueAuthenticate. |
| return; |
| } |
| } |
| |
| ContinueAuthenticate(user_context); |
| } |
| |
| void ScreenLocker::OnPinAttemptDone(const UserContext& user_context, |
| bool success) { |
| if (success) { |
| // Mark strong auth if this is cryptohome based pin. |
| if (quick_unlock::PinBackend::GetInstance()->ShouldUseCryptohome( |
| user_context.GetAccountId())) { |
| quick_unlock::QuickUnlockStorage* quick_unlock_storage = |
| quick_unlock::QuickUnlockFactory::GetForAccountId( |
| user_context.GetAccountId()); |
| if (quick_unlock_storage) |
| quick_unlock_storage->MarkStrongAuth(); |
| } |
| OnAuthSuccess(user_context); |
| } else { |
| // PIN authentication has failed; try submitting as a normal password. |
| ContinueAuthenticate(user_context); |
| } |
| } |
| |
| void ScreenLocker::ContinueAuthenticate( |
| const chromeos::UserContext& user_context) { |
| const user_manager::User* user = FindUnlockUser(user_context.GetAccountId()); |
| if (user) { |
| // Special case: supervised users. Use special authenticator. |
| if (user->GetType() == user_manager::USER_TYPE_SUPERVISED) { |
| UserContext updated_context = ChromeUserManager::Get() |
| ->GetSupervisedUserManager() |
| ->GetAuthentication() |
| ->TransformKey(user_context); |
| base::PostTaskWithTraits( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce( |
| &ExtendedAuthenticator::AuthenticateToCheck, |
| extended_authenticator_.get(), updated_context, |
| base::Bind(&ScreenLocker::OnPasswordAuthSuccess, |
| weak_factory_.GetWeakPtr(), updated_context))); |
| return; |
| } |
| } |
| |
| if (user_context.GetAccountId().GetAccountType() == |
| AccountType::ACTIVE_DIRECTORY && |
| user_context.GetKey()->GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN) { |
| // Try to get kerberos TGT while we have user's password typed on the lock |
| // screen. Failure to get TGT here is OK - that could mean e.g. Active |
| // Directory server is not reachable. AuthPolicyCredentialsManager regularly |
| // checks TGT status inside the user session. |
| AuthPolicyHelper::TryAuthenticateUser( |
| user_context.GetAccountId().GetUserEmail(), |
| user_context.GetAccountId().GetObjGuid(), |
| user_context.GetKey()->GetSecret()); |
| } |
| |
| base::PostTaskWithTraits( |
| FROM_HERE, {BrowserThread::UI}, |
| base::BindOnce(&ExtendedAuthenticator::AuthenticateToCheck, |
| extended_authenticator_.get(), user_context, |
| base::Bind(&ScreenLocker::OnPasswordAuthSuccess, |
| weak_factory_.GetWeakPtr(), user_context))); |
| } |
| |
| const user_manager::User* ScreenLocker::FindUnlockUser( |
| const AccountId& account_id) { |
| for (const user_manager::User* user : users_) { |
| if (user->GetAccountId() == account_id) |
| return user; |
| } |
| return nullptr; |
| } |
| |
| void ScreenLocker::OnStartLockCallback(bool locked) { |
| // Happens in tests that exit with a pending lock. In real lock failure, |
| // ash::LockStateController would cause the current user session to be |
| // terminated. |
| if (!locked) |
| return; |
| |
| delegate_->OnAshLockAnimationFinished(); |
| |
| AccessibilityManager::Get()->PlayEarcon( |
| SOUND_LOCK, PlaySoundOption::ONLY_IF_SPOKEN_FEEDBACK_ENABLED); |
| } |
| |
| void ScreenLocker::ClearErrors() { |
| delegate_->ClearErrors(); |
| } |
| |
| void ScreenLocker::Signout() { |
| delegate_->ClearErrors(); |
| base::RecordAction(UserMetricsAction("ScreenLocker_Signout")); |
| // We expect that this call will not wait for any user input. |
| // If it changes at some point, we will need to force exit. |
| chrome::AttemptUserExit(); |
| |
| // Don't hide yet the locker because the chrome screen may become visible |
| // briefly. |
| } |
| |
| void ScreenLocker::EnableInput() { |
| // TODO(crbug.com/927498): Remove this. |
| } |
| |
| void ScreenLocker::ShowErrorMessage(int error_msg_id, |
| HelpAppLauncher::HelpTopic help_topic_id, |
| bool sign_out_only) { |
| delegate_->ShowErrorMessage(error_msg_id, help_topic_id); |
| } |
| |
| void ScreenLocker::SetLoginStatusConsumer( |
| chromeos::AuthStatusConsumer* consumer) { |
| auth_status_consumer_ = consumer; |
| } |
| |
| // static |
| void ScreenLocker::InitClass() { |
| DCHECK(!g_screen_lock_observer); |
| g_screen_lock_observer = new ScreenLockObserver; |
| } |
| |
| // static |
| void ScreenLocker::ShutDownClass() { |
| DCHECK(g_screen_lock_observer); |
| delete g_screen_lock_observer; |
| g_screen_lock_observer = nullptr; |
| |
| // Delete |screen_locker_| if it is being shown. |
| ScheduleDeletion(); |
| } |
| |
| // static |
| void ScreenLocker::HandleShowLockScreenRequest() { |
| VLOG(1) << "Received ShowLockScreen request from session manager"; |
| DCHECK(g_screen_lock_observer); |
| if (UserAddingScreen::Get()->IsRunning()) { |
| VLOG(1) << "Waiting for user adding screen to stop"; |
| UserAddingScreen::Get()->AddObserver(g_screen_lock_observer); |
| UserAddingScreen::Get()->Cancel(); |
| return; |
| } |
| if (g_screen_lock_observer->session_started() && |
| user_manager::UserManager::Get()->CanCurrentUserLock()) { |
| ScreenLocker::Show(); |
| } else { |
| // If the current user's session cannot be locked or the user has not |
| // completed all sign-in steps yet, log out instead. The latter is done to |
| // avoid complications with displaying the lock screen over the login |
| // screen while remaining secure in the case the user walks away during |
| // the sign-in steps. See crbug.com/112225 and crbug.com/110933. |
| VLOG(1) << "The user session cannot be locked, logging out"; |
| SessionTerminationManager::Get()->StopSession(); |
| } |
| } |
| |
| // static |
| void ScreenLocker::Show() { |
| base::RecordAction(UserMetricsAction("ScreenLocker_Show")); |
| DCHECK(base::MessageLoopCurrentForUI::IsSet()); |
| |
| // Check whether the currently logged in user is a guest account and if so, |
| // refuse to lock the screen (crosbug.com/23764). |
| if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) { |
| VLOG(1) << "Refusing to lock screen for guest account"; |
| return; |
| } |
| |
| if (!screen_locker_) { |
| SessionControllerClientImpl::Get()->PrepareForLock(base::BindOnce([]() { |
| ScreenLocker* locker = |
| new ScreenLocker(user_manager::UserManager::Get()->GetUnlockUsers()); |
| VLOG(1) << "Created ScreenLocker " << locker; |
| locker->Init(); |
| })); |
| } else { |
| VLOG(1) << "ScreenLocker " << screen_locker_ << " already exists; " |
| << " calling session manager's HandleLockScreenShown D-Bus method"; |
| SessionManagerClient::Get()->NotifyLockScreenShown(); |
| } |
| } |
| |
| // static |
| void ScreenLocker::Hide() { |
| DCHECK(base::MessageLoopCurrentForUI::IsSet()); |
| // For a guest user, screen_locker_ would have never been initialized. |
| if (user_manager::UserManager::Get()->IsLoggedInAsGuest()) { |
| VLOG(1) << "Refusing to hide lock screen for guest account"; |
| return; |
| } |
| |
| DCHECK(screen_locker_); |
| SessionControllerClientImpl::Get()->RunUnlockAnimation(base::BindOnce([]() { |
| session_manager::SessionManager::Get()->SetSessionState( |
| session_manager::SessionState::ACTIVE); |
| ScreenLocker::ScheduleDeletion(); |
| })); |
| } |
| |
| void ScreenLocker::RefreshPinAndFingerprintTimeout() { |
| MaybeDisablePinAndFingerprintFromTimeout( |
| "RefreshPinAndFingerprintTimeout", |
| user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId()); |
| } |
| |
| // static |
| void ScreenLocker::ScheduleDeletion() { |
| // Avoid possible multiple calls. |
| if (screen_locker_ == nullptr) |
| return; |
| VLOG(1) << "Deleting ScreenLocker " << screen_locker_; |
| |
| AccessibilityManager::Get()->PlayEarcon( |
| SOUND_UNLOCK, PlaySoundOption::ONLY_IF_SPOKEN_FEEDBACK_ENABLED); |
| |
| delete screen_locker_; |
| screen_locker_ = nullptr; |
| } |
| |
| void ScreenLocker::SaveSyncPasswordHash(const UserContext& user_context) { |
| if (!user_context.GetSyncPasswordData().has_value()) |
| return; |
| |
| const user_manager::User* user = |
| user_manager::UserManager::Get()->FindUser(user_context.GetAccountId()); |
| if (!user || !user->is_active()) |
| return; |
| Profile* profile = chromeos::ProfileHelper::Get()->GetProfileByUser(user); |
| if (profile) |
| login::SaveSyncPasswordDataToProfile(user_context, profile); |
| } |
| |
| bool ScreenLocker::IsAuthEnabledForUser(const AccountId& account_id) { |
| return !base::ContainsKey(users_with_disabled_auth_, account_id); |
| } |
| |
| void ScreenLocker::SetAuthenticatorsForTesting( |
| scoped_refptr<Authenticator> authenticator, |
| scoped_refptr<ExtendedAuthenticator> extended_authenticator) { |
| authenticator_ = std::move(authenticator); |
| extended_authenticator_ = std::move(extended_authenticator); |
| } |
| |
| //////////////////////////////////////////////////////////////////////////////// |
| // ScreenLocker, private: |
| |
| ScreenLocker::~ScreenLocker() { |
| VLOG(1) << "Destroying ScreenLocker " << this; |
| DCHECK(base::MessageLoopCurrentForUI::IsSet()); |
| |
| if (authenticator_) |
| authenticator_->SetConsumer(nullptr); |
| if (extended_authenticator_) |
| extended_authenticator_->SetConsumer(nullptr); |
| |
| ClearErrors(); |
| |
| screen_locker_ = nullptr; |
| bool state = false; |
| VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state; |
| content::NotificationService::current()->Notify( |
| chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, |
| content::Source<ScreenLocker>(this), content::Details<bool>(&state)); |
| |
| VLOG(1) << "Calling session manager's HandleLockScreenDismissed D-Bus method"; |
| SessionManagerClient::Get()->NotifyLockScreenDismissed(); |
| |
| if (saved_ime_state_.get()) { |
| input_method::InputMethodManager::Get()->SetState(saved_ime_state_); |
| } |
| } |
| |
| void ScreenLocker::ScreenLockReady() { |
| locked_ = true; |
| base::TimeDelta delta = base::Time::Now() - start_time_; |
| VLOG(1) << "ScreenLocker " << this << " is ready after " << delta.InSecondsF() |
| << " second(s)"; |
| UMA_HISTOGRAM_TIMES("ScreenLocker.ScreenLockTime", delta); |
| |
| bool state = true; |
| VLOG(1) << "Emitting SCREEN_LOCK_STATE_CHANGED with state=" << state; |
| content::NotificationService::current()->Notify( |
| chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED, |
| content::Source<ScreenLocker>(this), content::Details<bool>(&state)); |
| VLOG(1) << "Calling session manager's HandleLockScreenShown D-Bus method"; |
| SessionManagerClient::Get()->NotifyLockScreenShown(); |
| |
| session_manager::SessionManager::Get()->SetSessionState( |
| session_manager::SessionState::LOCKED); |
| |
| input_method::InputMethodManager::Get() |
| ->GetActiveIMEState() |
| ->EnableLockScreenLayouts(); |
| |
| // Start a fingerprint authentication session if fingerprint is available for |
| // the primary user. Only the primary user can use fingerprint. |
| if (IsFingerprintAvailableForUser( |
| user_manager::UserManager::Get()->GetPrimaryUser())) { |
| VLOG(1) << "Fingerprint is available on lock screen, start fingerprint " |
| << "auth session now."; |
| fp_service_->StartAuthSession(); |
| } else { |
| VLOG(1) << "Fingerprint is not available on lock screen"; |
| } |
| |
| MaybeDisablePinAndFingerprintFromTimeout( |
| "ScreenLockReady", |
| user_manager::UserManager::Get()->GetPrimaryUser()->GetAccountId()); |
| } |
| |
| bool ScreenLocker::IsUserLoggedIn(const AccountId& account_id) const { |
| for (user_manager::User* user : users_) { |
| if (user->GetAccountId() == account_id) |
| return true; |
| } |
| return false; |
| } |
| |
| void ScreenLocker::OnRestarted() {} |
| |
| void ScreenLocker::OnEnrollScanDone(device::mojom::ScanResult scan_result, |
| bool enroll_session_complete, |
| int percent_complete) {} |
| |
| void ScreenLocker::OnAuthScanDone( |
| device::mojom::ScanResult scan_result, |
| const base::flat_map<std::string, std::vector<std::string>>& matches) { |
| RefreshPinAndFingerprintTimeout(); |
| |
| VLOG(1) << "Receive fingerprint auth scan result. scan_result=" |
| << scan_result; |
| unlock_attempt_type_ = AUTH_FINGERPRINT; |
| user_manager::User* active_user = |
| user_manager::UserManager::Get()->GetActiveUser(); |
| quick_unlock::QuickUnlockStorage* quick_unlock_storage = |
| quick_unlock::QuickUnlockFactory::GetForUser(active_user); |
| if (!quick_unlock_storage || |
| !quick_unlock_storage->IsFingerprintAuthenticationAvailable() || |
| base::ContainsKey(users_with_disabled_auth_, |
| active_user->GetAccountId())) { |
| return; |
| } |
| |
| LoginScreenClient::Get()->auth_recorder()->RecordAuthMethod( |
| LoginAuthRecorder::AuthMethod::kFingerprint); |
| |
| if (scan_result != device::mojom::ScanResult::SUCCESS) { |
| LOG(ERROR) << "Fingerprint unlock failed because scan_result=" |
| << scan_result; |
| OnFingerprintAuthFailure(*active_user); |
| return; |
| } |
| |
| UserContext user_context(*active_user); |
| if (!base::ContainsKey(matches, active_user->username_hash())) { |
| LOG(ERROR) << "Fingerprint unlock failed because it does not match active" |
| << " user's record"; |
| OnFingerprintAuthFailure(*active_user); |
| return; |
| } |
| ash::LoginScreen::Get()->GetModel()->NotifyFingerprintAuthResult( |
| active_user->GetAccountId(), true /*success*/); |
| VLOG(1) << "Fingerprint unlock is successful."; |
| LoginScreenClient::Get()->auth_recorder()->RecordFingerprintAuthSuccess( |
| true /*success*/, |
| quick_unlock_storage->fingerprint_storage()->unlock_attempt_count()); |
| OnAuthSuccess(user_context); |
| } |
| |
| void ScreenLocker::OnSessionFailed() { |
| LOG(ERROR) << "Fingerprint session failed."; |
| } |
| |
| void ScreenLocker::OnFingerprintAuthFailure(const user_manager::User& user) { |
| UMA_HISTOGRAM_ENUMERATION("ScreenLocker.AuthenticationFailure", |
| unlock_attempt_type_, UnlockType::AUTH_COUNT); |
| LoginScreenClient::Get()->auth_recorder()->RecordFingerprintAuthSuccess( |
| false /*success*/, base::nullopt /*num_attempts*/); |
| ash::LoginScreen::Get()->GetModel()->NotifyFingerprintAuthResult( |
| user.GetAccountId(), false /*success*/); |
| |
| quick_unlock::QuickUnlockStorage* quick_unlock_storage = |
| quick_unlock::QuickUnlockFactory::GetForUser(&user); |
| if (quick_unlock_storage && |
| quick_unlock_storage->IsFingerprintAuthenticationAvailable()) { |
| quick_unlock_storage->fingerprint_storage()->AddUnlockAttempt(); |
| if (quick_unlock_storage->fingerprint_storage()->ExceededUnlockAttempts()) { |
| VLOG(1) << "Fingerprint unlock is disabled because it reached maximum" |
| << " unlock attempt."; |
| ash::LoginScreen::Get()->GetModel()->SetFingerprintState( |
| user.GetAccountId(), ash::FingerprintState::DISABLED_FROM_ATTEMPTS); |
| delegate_->ShowErrorMessage(IDS_LOGIN_ERROR_FINGERPRINT_MAX_ATTEMPT, |
| HelpAppLauncher::HELP_CANT_ACCESS_ACCOUNT); |
| } |
| } |
| |
| if (auth_status_consumer_) { |
| AuthFailure failure(AuthFailure::UNLOCK_FAILED); |
| auth_status_consumer_->OnAuthFailure(failure); |
| } |
| } |
| |
| void ScreenLocker::MaybeDisablePinAndFingerprintFromTimeout( |
| const std::string& source, |
| const AccountId& account_id) { |
| VLOG(1) << "MaybeDisablePinAndFingerprintFromTimeout source=" << source; |
| |
| update_fingerprint_state_timer_.Stop(); |
| |
| // Update PIN state. |
| quick_unlock::PinBackend::GetInstance()->CanAuthenticate( |
| account_id, base::BindOnce(&ScreenLocker::OnPinCanAuthenticate, |
| weak_factory_.GetWeakPtr(), account_id)); |
| |
| quick_unlock::QuickUnlockStorage* quick_unlock_storage = |
| quick_unlock::QuickUnlockFactory::GetForAccountId(account_id); |
| if (quick_unlock_storage) { |
| if (quick_unlock_storage->HasStrongAuth()) { |
| // Call this function again when strong authentication expires. PIN may |
| // also depend on strong authentication if it is prefs-based. Fingerprint |
| // always requires strong authentication. |
| const base::TimeDelta next_strong_auth = |
| quick_unlock_storage->TimeUntilNextStrongAuth(); |
| VLOG(1) << "Scheduling next pin and fingerprint timeout check in " |
| << next_strong_auth; |
| update_fingerprint_state_timer_.Start( |
| FROM_HERE, next_strong_auth, |
| base::BindOnce( |
| &ScreenLocker::MaybeDisablePinAndFingerprintFromTimeout, |
| base::Unretained(this), "update_fingerprint_state_timer_", |
| account_id)); |
| } else { |
| // Strong auth is unavailable; disable fingerprint if it was enabled. |
| if (quick_unlock_storage->fingerprint_storage() |
| ->IsFingerprintAvailable()) { |
| VLOG(1) << "Require strong auth to make fingerprint unlock available."; |
| ash::LoginScreen::Get()->GetModel()->SetFingerprintState( |
| account_id, ash::FingerprintState::DISABLED_FROM_TIMEOUT); |
| } |
| } |
| } |
| } |
| |
| void ScreenLocker::OnPinCanAuthenticate(const AccountId& account_id, |
| bool can_authenticate) { |
| ash::LoginScreen::Get()->GetModel()->SetPinEnabledForUser(account_id, |
| can_authenticate); |
| } |
| |
| } // namespace chromeos |