blob: 888d92708601a08c3896d8a483e7bb049fda1b97 [file] [log] [blame]
// Copyright 2017 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 "ash/login/login_screen_controller.h"
#include "ash/login/lock_screen_apps_focus_observer.h"
#include "ash/login/ui/lock_screen.h"
#include "ash/login/ui/login_data_dispatcher.h"
#include "ash/public/cpp/ash_pref_names.h"
#include "ash/root_window_controller.h"
#include "ash/session/session_controller.h"
#include "ash/shell.h"
#include "ash/system/status_area_widget.h"
#include "base/strings/string_number_conversions.h"
#include "chromeos/cryptohome/system_salt_getter.h"
#include "chromeos/login/auth/authpolicy_login_helper.h"
#include "chromeos/login/auth/user_context.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "components/session_manager/session_manager_types.h"
namespace ash {
namespace {
std::string CalculateHash(const std::string& password,
const std::string& salt,
chromeos::Key::KeyType key_type) {
chromeos::Key key(password);
key.Transform(key_type, salt);
return key.GetSecret();
}
void SetSystemTrayVisibility(bool visible) {
auto* status_area =
Shell::GetPrimaryRootWindowController()->GetStatusAreaWidget();
if (status_area)
status_area->SetSystemTrayVisibility(visible);
}
} // namespace
LoginScreenController::LoginScreenController() : weak_factory_(this) {}
LoginScreenController::~LoginScreenController() = default;
// static
void LoginScreenController::RegisterProfilePrefs(PrefRegistrySimple* registry,
bool for_test) {
if (for_test) {
// There is no remote pref service, so pretend that ash owns the pref.
registry->RegisterStringPref(prefs::kQuickUnlockPinSalt, "");
return;
}
// Pref is owned by chrome and flagged as PUBLIC.
registry->RegisterForeignPref(prefs::kQuickUnlockPinSalt);
}
void LoginScreenController::BindRequest(mojom::LoginScreenRequest request) {
bindings_.AddBinding(this, std::move(request));
}
void LoginScreenController::SetClient(mojom::LoginScreenClientPtr client) {
login_screen_client_ = std::move(client);
}
void LoginScreenController::ShowLockScreen(ShowLockScreenCallback on_shown) {
ash::LockScreen::Show(ash::LockScreen::ScreenType::kLock);
std::move(on_shown).Run(true);
SetSystemTrayVisibility(true);
}
void LoginScreenController::ShowLoginScreen(ShowLoginScreenCallback on_shown) {
// Login screen can only be used during login.
if (Shell::Get()->session_controller()->GetSessionState() !=
session_manager::SessionState::LOGIN_PRIMARY) {
std::move(on_shown).Run(false);
return;
}
// TODO(jdufault): rename ash::LockScreen to ash::LoginScreen.
ash::LockScreen::Show(ash::LockScreen::ScreenType::kLogin);
std::move(on_shown).Run(true);
SetSystemTrayVisibility(true);
}
void LoginScreenController::ShowErrorMessage(int32_t login_attempts,
const std::string& error_text,
const std::string& help_link_text,
int32_t help_topic_id) {
NOTIMPLEMENTED();
}
void LoginScreenController::ClearErrors() {
NOTIMPLEMENTED();
}
void LoginScreenController::ShowUserPodCustomIcon(
const AccountId& account_id,
mojom::EasyUnlockIconOptionsPtr icon) {
DataDispatcher()->ShowEasyUnlockIcon(account_id, icon);
}
void LoginScreenController::HideUserPodCustomIcon(const AccountId& account_id) {
auto icon_options = mojom::EasyUnlockIconOptions::New();
icon_options->icon = mojom::EasyUnlockIconId::NONE;
DataDispatcher()->ShowEasyUnlockIcon(account_id, icon_options);
}
void LoginScreenController::SetAuthType(
const AccountId& account_id,
proximity_auth::mojom::AuthType auth_type,
const base::string16& initial_value) {
if (auth_type == proximity_auth::mojom::AuthType::USER_CLICK) {
DataDispatcher()->SetClickToUnlockEnabledForUser(account_id,
true /*enabled*/);
} else {
NOTIMPLEMENTED();
}
}
void LoginScreenController::LoadUsers(
std::vector<mojom::LoginUserInfoPtr> users,
bool show_guest) {
DCHECK(DataDispatcher());
DataDispatcher()->NotifyUsers(users);
}
void LoginScreenController::SetPinEnabledForUser(const AccountId& account_id,
bool is_enabled) {
// Chrome will update pin pod state every time user tries to authenticate.
// LockScreen is destroyed in the case of authentication success.
if (DataDispatcher())
DataDispatcher()->SetPinEnabledForUser(account_id, is_enabled);
}
void LoginScreenController::SetDevChannelInfo(
const std::string& os_version_label_text,
const std::string& enterprise_info_text,
const std::string& bluetooth_name) {
if (DataDispatcher()) {
DataDispatcher()->SetDevChannelInfo(os_version_label_text,
enterprise_info_text, bluetooth_name);
}
}
void LoginScreenController::IsReadyForPassword(
IsReadyForPasswordCallback callback) {
std::move(callback).Run(LockScreen::IsShown() && !is_authenticating_);
}
void LoginScreenController::AuthenticateUser(const AccountId& account_id,
const std::string& password,
bool authenticated_by_pin,
OnAuthenticateCallback callback) {
// Ignore concurrent auth attempts. This can happen if the user quickly enters
// two separate passwords and hits enter.
if (!login_screen_client_ || is_authenticating_) {
LOG_IF(ERROR, is_authenticating_) << "Ignoring concurrent auth attempt";
std::move(callback).Run(base::nullopt);
return;
}
is_authenticating_ = true;
// If auth is disabled by the debug overlay bypass the mojo call entirely, as
// it will dismiss the lock screen if the password is correct.
switch (force_fail_auth_for_debug_overlay_) {
case ForceFailAuth::kOff:
break;
case ForceFailAuth::kImmediate:
OnAuthenticateComplete(std::move(callback), false /*success*/);
return;
case ForceFailAuth::kDelayed:
base::ThreadTaskRunnerHandle::Get()->PostDelayedTask(
FROM_HERE,
base::BindOnce(&LoginScreenController::OnAuthenticateComplete,
weak_factory_.GetWeakPtr(), base::Passed(&callback),
false),
base::TimeDelta::FromSeconds(1));
return;
}
// |DoAuthenticateUser| requires the system salt, so we fetch it first, and
// then run |DoAuthenticateUser| as a continuation.
auto do_authenticate = base::BindOnce(
&LoginScreenController::DoAuthenticateUser, weak_factory_.GetWeakPtr(),
account_id, password, authenticated_by_pin, std::move(callback));
chromeos::SystemSaltGetter::Get()->GetSystemSalt(base::BindRepeating(
&LoginScreenController::OnGetSystemSalt, weak_factory_.GetWeakPtr(),
base::Passed(&do_authenticate)));
}
void LoginScreenController::HandleFocusLeavingLockScreenApps(bool reverse) {
for (auto& observer : lock_screen_apps_focus_observers_)
observer.OnFocusLeavingLockScreenApps(reverse);
}
void LoginScreenController::AttemptUnlock(const AccountId& account_id) {
if (!login_screen_client_)
return;
login_screen_client_->AttemptUnlock(account_id);
Shell::Get()->metrics()->login_metrics_recorder()->SetAuthMethod(
LoginMetricsRecorder::AuthMethod::kSmartlock);
}
void LoginScreenController::HardlockPod(const AccountId& account_id) {
if (!login_screen_client_)
return;
login_screen_client_->HardlockPod(account_id);
}
void LoginScreenController::RecordClickOnLockIcon(const AccountId& account_id) {
if (!login_screen_client_)
return;
login_screen_client_->RecordClickOnLockIcon(account_id);
}
void LoginScreenController::OnFocusPod(const AccountId& account_id) {
if (!login_screen_client_)
return;
login_screen_client_->OnFocusPod(account_id);
}
void LoginScreenController::OnNoPodFocused() {
if (!login_screen_client_)
return;
login_screen_client_->OnNoPodFocused();
}
void LoginScreenController::LoadWallpaper(const AccountId& account_id) {
if (!login_screen_client_)
return;
login_screen_client_->LoadWallpaper(account_id);
}
void LoginScreenController::SignOutUser() {
if (!login_screen_client_)
return;
login_screen_client_->SignOutUser();
}
void LoginScreenController::CancelAddUser() {
if (!login_screen_client_)
return;
login_screen_client_->CancelAddUser();
}
void LoginScreenController::LoginAsGuest() {
if (!login_screen_client_)
return;
login_screen_client_->LoginAsGuest();
}
void LoginScreenController::OnMaxIncorrectPasswordAttempted(
const AccountId& account_id) {
if (!login_screen_client_)
return;
login_screen_client_->OnMaxIncorrectPasswordAttempted(account_id);
}
void LoginScreenController::FocusLockScreenApps(bool reverse) {
if (!login_screen_client_)
return;
login_screen_client_->FocusLockScreenApps(reverse);
}
void LoginScreenController::AddLockScreenAppsFocusObserver(
LockScreenAppsFocusObserver* observer) {
lock_screen_apps_focus_observers_.AddObserver(observer);
}
void LoginScreenController::RemoveLockScreenAppsFocusObserver(
LockScreenAppsFocusObserver* observer) {
lock_screen_apps_focus_observers_.RemoveObserver(observer);
}
void LoginScreenController::FlushForTesting() {
login_screen_client_.FlushForTesting();
}
void LoginScreenController::DoAuthenticateUser(const AccountId& account_id,
const std::string& password,
bool authenticated_by_pin,
OnAuthenticateCallback callback,
const std::string& system_salt) {
int dummy_value;
bool is_pin =
authenticated_by_pin && base::StringToInt(password, &dummy_value);
std::string hashed_password = CalculateHash(
password, system_salt, chromeos::Key::KEY_TYPE_SALTED_SHA256_TOP_HALF);
PrefService* prefs =
Shell::Get()->session_controller()->GetLastActiveUserPrefService();
if (is_pin && prefs) {
hashed_password =
CalculateHash(password, prefs->GetString(prefs::kQuickUnlockPinSalt),
chromeos::Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234);
}
if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY && !is_pin) {
// Try to get kerberos TGT while we have user's password typed on the lock
// screen. Using invalid/bad password is fine. 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.
chromeos::AuthPolicyLoginHelper::TryAuthenticateUser(
account_id.GetUserEmail(), account_id.GetObjGuid(), password);
}
Shell::Get()->metrics()->login_metrics_recorder()->SetAuthMethod(
is_pin ? LoginMetricsRecorder::AuthMethod::kPin
: LoginMetricsRecorder::AuthMethod::kPassword);
login_screen_client_->AuthenticateUser(
account_id, hashed_password, is_pin,
base::BindOnce(&LoginScreenController::OnAuthenticateComplete,
weak_factory_.GetWeakPtr(), base::Passed(&callback)));
}
void LoginScreenController::OnAuthenticateComplete(
OnAuthenticateCallback callback,
bool success) {
is_authenticating_ = false;
std::move(callback).Run(success);
}
void LoginScreenController::OnGetSystemSalt(PendingDoAuthenticateUser then,
const std::string& system_salt) {
std::move(then).Run(system_salt);
}
LoginDataDispatcher* LoginScreenController::DataDispatcher() const {
if (!ash::LockScreen::IsShown())
return nullptr;
return ash::LockScreen::Get()->data_dispatcher();
}
} // namespace ash