Commit 174ab92a authored by tengs's avatar tengs Committed by Commit bot

Reauthenticate the user before launching Smart Lock setup app.

The reauth is needed to acquire the user's password to add a new cryptohome
key for sign-in.

BUG=409427

Review URL: https://codereview.chromium.org/668213003

Cr-Commit-Position: refs/heads/master@{#302541}
parent 72641dc6
......@@ -14889,6 +14889,9 @@ Do you accept?
<message name="IDS_EASY_UNLOCK_SCREENLOCK_TOOLTIP_LOGIN_FAILURE" desc="Tooltip text shown on a user's lock screen when Smart Lock signin attempt fails.">
Smart Lock couldn't verify your account. Type your password to enter.
</message>
<message name="IDS_SMART_LOCK_SCREENLOCK_TOOLTIP_HARDLOCK_REAUTH_USER" desc="Tooltip text shown on a user's lock screen pod to reauthenticate the user before setting up Smart Lock. A password has to be entered to unlock the device.">
To set up Chrome Smart Lock, Google needs to make sure it's you--type your password to get started.
</message>
<message name="IDS_EASY_UNLOCK_SCREENLOCK_USER_POD_AUTH_VALUE" desc="Message on lock screen user pod shown in place of password field when Easy Unlock is enabled and a phone that can unlock the Chromebook is detected in proximity.">
Click to enter
</message>
......
// 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 "base/bind.h"
#include "base/message_loop/message_loop.h"
#include "chrome/browser/chrome_notification_types.h"
#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_reauth.h"
#include "chrome/browser/chromeos/login/lock/screen_locker.h"
#include "chrome/browser/signin/screenlock_bridge.h"
#include "chrome/grit/generated_resources.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/login/auth/auth_status_consumer.h"
#include "chromeos/login/auth/user_context.h"
#include "content/public/browser/notification_details.h"
#include "content/public/browser/notification_observer.h"
#include "content/public/browser/notification_registrar.h"
#include "content/public/browser/notification_service.h"
#include "ui/base/l10n/l10n_util.h"
namespace chromeos {
namespace {
void EndReauthAttempt();
// Performs the actual reauth flow and returns the user context it obtains.
class ReauthHandler : public content::NotificationObserver,
public chromeos::AuthStatusConsumer {
public:
explicit ReauthHandler(EasyUnlockReauth::UserContextCallback callback)
: callback_(callback) {}
virtual ~ReauthHandler() {}
bool Start() {
ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
if (screen_locker && screen_locker->locked()) {
NOTREACHED();
return false;
}
user_manager::UserManager* user_manager = user_manager::UserManager::Get();
if (user_manager->GetPrimaryUser() != user_manager->GetActiveUser() ||
user_manager->GetUnlockUsers().size() != 1) {
LOG(WARNING) << "Only primary users in non-multiprofile sessions are "
<< "currently supported for reauth.";
return false;
}
notification_registrar_.Add(this,
chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED,
content::NotificationService::AllSources());
SessionManagerClient* session_manager =
chromeos::DBusThreadManager::Get()->GetSessionManagerClient();
session_manager->RequestLockScreen();
return true;
}
// content::NotificationObserver
void Observe(int type,
const content::NotificationSource& source,
const content::NotificationDetails& details) override {
CHECK(type == chrome::NOTIFICATION_SCREEN_LOCK_STATE_CHANGED);
bool is_screen_locked = *content::Details<bool>(details).ptr();
DCHECK(is_screen_locked);
notification_registrar_.RemoveAll();
// TODO(tengs): Add an explicit reauth state to the locker and account
// picker, so we can customize the UI.
ScreenLocker* screen_locker = ScreenLocker::default_screen_locker();
screen_locker->SetLoginStatusConsumer(this);
// Show tooltip explaining reauth.
ScreenlockBridge::UserPodCustomIconOptions icon_options;
icon_options.SetIcon(ScreenlockBridge::USER_POD_CUSTOM_ICON_NONE);
icon_options.SetTooltip(
l10n_util::GetStringUTF16(
IDS_SMART_LOCK_SCREENLOCK_TOOLTIP_HARDLOCK_REAUTH_USER),
true);
const user_manager::UserList& lock_users = screen_locker->users();
DCHECK(lock_users.size() == 1);
ScreenlockBridge::Get()->lock_handler()->ShowUserPodCustomIcon(
lock_users[0]->email(), icon_options);
}
// chromeos::AuthStatusConsumer:
virtual void OnAuthSuccess(
const chromeos::UserContext& user_context) override {
callback_.Run(user_context);
// Schedule deletion.
base::MessageLoopForUI::current()->PostTask(FROM_HERE,
base::Bind(&EndReauthAttempt));
}
virtual void OnAuthFailure(const chromeos::AuthFailure& error) override {}
private:
content::NotificationRegistrar notification_registrar_;
EasyUnlockReauth::UserContextCallback callback_;
DISALLOW_COPY_AND_ASSIGN(ReauthHandler);
};
ReauthHandler* g_reauth_handler = NULL;
void EndReauthAttempt() {
DCHECK(base::MessageLoopForUI::IsCurrent());
DCHECK(g_reauth_handler);
delete g_reauth_handler;
g_reauth_handler = NULL;
}
} // namespace
// static.
bool EasyUnlockReauth::ReauthForUserContext(
base::Callback<void(const UserContext&)> callback) {
DCHECK(base::MessageLoopForUI::IsCurrent());
if (g_reauth_handler)
return false;
g_reauth_handler = new ReauthHandler(callback);
return g_reauth_handler->Start();
}
} // namespace chromeos
// 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.
#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_EASY_UNLOCK_EASY_UNLOCK_REAUTH_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_EASY_UNLOCK_EASY_UNLOCK_REAUTH_H_
#include "base/callback_forward.h"
#include "base/macros.h"
#include "base/memory/scoped_ptr.h"
namespace chromeos {
class UserContext;
// Responsible for locking the screen and reauthenticating the user so we can
// create new cryptohome keys for passwordless sign-in.
class EasyUnlockReauth {
public:
typedef base::Callback<void(const UserContext&)> UserContextCallback;
// Launches the reauth screen to get the user context. If the screen fails
// for some reason, then this function will return false.
static bool ReauthForUserContext(UserContextCallback callback);
DISALLOW_IMPLICIT_CONSTRUCTORS(EasyUnlockReauth);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_LOGIN_EASY_UNLOCK_EASY_UNLOCK_REAUTH_H_
// 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/easy_unlock/short_lived_user_context.h"
#include "base/bind.h"
#include "base/location.h"
#include "base/task_runner.h"
#include "chrome/common/extensions/extension_constants.h"
#include "chromeos/login/auth/user_context.h"
namespace chromeos {
namespace {
// The number of minutes that the user context will be stored.
const int64 kUserContextTimeToLiveMinutes = 10;
} // namespace
ShortLivedUserContext::ShortLivedUserContext(
const UserContext& user_context,
apps::AppLifetimeMonitor* app_lifetime_monitor,
base::TaskRunner* task_runner)
: user_context_(new UserContext(user_context)),
app_lifetime_monitor_(app_lifetime_monitor),
weak_ptr_factory_(this) {
app_lifetime_monitor_->AddObserver(this);
task_runner->PostDelayedTask(
FROM_HERE,
base::Bind(&ShortLivedUserContext::Reset, weak_ptr_factory_.GetWeakPtr()),
base::TimeDelta::FromMinutes(kUserContextTimeToLiveMinutes));
}
ShortLivedUserContext::~ShortLivedUserContext() {
Reset();
}
void ShortLivedUserContext::Reset() {
if (user_context_.get()) {
user_context_->ClearSecrets();
user_context_.reset();
app_lifetime_monitor_->RemoveObserver(this);
}
}
void ShortLivedUserContext::OnAppDeactivated(Profile* profile,
const std::string& app_id) {
if (app_id == extension_misc::kEasyUnlockAppId)
Reset();
}
} // namespace chromeos
// 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.
#ifndef CHROME_BROWSER_CHROMEOS_LOGIN_EASY_UNLOCK_SHORT_LIVED_USER_CONTEXT_H_
#define CHROME_BROWSER_CHROMEOS_LOGIN_EASY_UNLOCK_SHORT_LIVED_USER_CONTEXT_H_
#include "apps/app_lifetime_monitor.h"
#include "base/memory/scoped_ptr.h"
#include "base/memory/weak_ptr.h"
namespace base {
class TaskRunner;
}
namespace chromeos {
class UserContext;
// Stores the UserContext of an authentication operation on the sign-in/lock
// screen, which is used to generate the keys for Easy Sign-in.
// The lifetime of the user context is bound the the setup app window. As a
// fail-safe, the user context will also be deleted after a set period of time
// in case the app is left open indefintely.
class ShortLivedUserContext : public apps::AppLifetimeMonitor::Observer {
public:
ShortLivedUserContext(const UserContext& user_context,
apps::AppLifetimeMonitor* app_lifetime_monitor,
base::TaskRunner* task_runner);
virtual ~ShortLivedUserContext();
// The UserContext returned here can be NULL if its time-to-live has expired.
UserContext* user_context() { return user_context_.get(); }
private:
void Reset();
// apps::AppLifetimeMonitor::Observer:
virtual void OnAppDeactivated(Profile* profile,
const std::string& app_id) override;
scoped_ptr<UserContext> user_context_;
apps::AppLifetimeMonitor* app_lifetime_monitor_;
base::WeakPtrFactory<ShortLivedUserContext> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(ShortLivedUserContext);
};
} // namespace chromeos
#endif // CHROME_BROWSER_CHROMEOS_LOGIN_EASY_UNLOCK_SHORT_LIVED_USER_CONTEXT_H_
......@@ -383,7 +383,7 @@ void EasyUnlockService::CheckCryptohomeKeysAndMaybeHardlock() {
chromeos::EasyUnlockKeyManager::RemoteDeviceListToDeviceDataList(
*device_list, &parsed_paired);
for (const auto& device_key_data : parsed_paired)
paired_devices.insert(device_key_data.public_key);
paired_devices.insert(device_key_data.psk);
}
if (paired_devices.empty()) {
SetHardlockState(EasyUnlockScreenlockStateHandler::NO_PAIRING);
......@@ -618,7 +618,7 @@ void EasyUnlockService::OnCryptohomeKeysFetchedForChecking(
std::set<std::string> devices_in_cryptohome;
for (const auto& device_key_data : key_data_list)
devices_in_cryptohome.insert(device_key_data.public_key);
devices_in_cryptohome.insert(device_key_data.psk);
if (paired_devices != devices_in_cryptohome ||
GetHardlockState() == EasyUnlockScreenlockStateHandler::NO_PAIRING) {
......
......@@ -215,6 +215,12 @@ class EasyUnlockService : public KeyedService {
return screenlock_state_handler_.get();
}
// Saves hardlock state for the given user. Update UI if the currently
// associated user is the same.
void SetHardlockStateForUser(
const std::string& user_id,
EasyUnlockScreenlockStateHandler::HardlockState state);
private:
// A class to detect whether a bluetooth adapter is present.
class BluetoothDetector;
......@@ -231,12 +237,6 @@ class EasyUnlockService : public KeyedService {
// Callback when Bluetooth adapter present state changes.
void OnBluetoothAdapterPresentChanged();
// Saves hardlock state for the given user. Update UI if the currently
// associated user is the same.
void SetHardlockStateForUser(
const std::string& user_id,
EasyUnlockScreenlockStateHandler::HardlockState state);
#if defined(OS_CHROMEOS)
// Callback for get key operation from CheckCryptohomeKeysAndMaybeHardlock.
void OnCryptohomeKeysFetchedForChecking(
......
......@@ -18,9 +18,15 @@
#include "chrome/common/extensions/extension_constants.h"
#include "chrome/common/pref_names.h"
#include "components/pref_registry/pref_registry_syncable.h"
#include "content/public/browser/browser_thread.h"
#include "extensions/browser/extension_system.h"
#if defined(OS_CHROMEOS)
#include "apps/app_lifetime_monitor_factory.h"
#include "base/thread_task_runner_handle.h"
#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_reauth.h"
#include "chrome/browser/chromeos/login/session/user_session_manager.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "components/user_manager/user_manager.h"
#endif
......@@ -40,7 +46,8 @@ const char kKeyPhoneId[] = "permitRecord.id";
EasyUnlockServiceRegular::EasyUnlockServiceRegular(Profile* profile)
: EasyUnlockService(profile),
turn_off_flow_status_(EasyUnlockService::IDLE) {
turn_off_flow_status_(EasyUnlockService::IDLE),
weak_ptr_factory_(this) {
}
EasyUnlockServiceRegular::~EasyUnlockServiceRegular() {
......@@ -55,6 +62,52 @@ std::string EasyUnlockServiceRegular::GetUserEmail() const {
}
void EasyUnlockServiceRegular::LaunchSetup() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
#if defined(OS_CHROMEOS)
// Force the user to reauthenticate by showing a modal overlay (similar to the
// lock screen). The password obtained from the reauth is cached for a short
// period of time and used to create the cryptohome keys for sign-in.
if (short_lived_user_context_ && short_lived_user_context_->user_context()) {
OpenSetupApp();
} else {
bool reauth_success = chromeos::EasyUnlockReauth::ReauthForUserContext(
base::Bind(&EasyUnlockServiceRegular::OnUserContextFromReauth,
weak_ptr_factory_.GetWeakPtr()));
if (!reauth_success)
OpenSetupApp();
}
#else
OpenSetupApp();
#endif
}
#if defined(OS_CHROMEOS)
void EasyUnlockServiceRegular::OnUserContextFromReauth(
const chromeos::UserContext& user_context) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
short_lived_user_context_.reset(new chromeos::ShortLivedUserContext(
user_context, apps::AppLifetimeMonitorFactory::GetForProfile(profile()),
base::ThreadTaskRunnerHandle::Get().get()));
OpenSetupApp();
}
void EasyUnlockServiceRegular::OnKeysRefreshedForSetDevices(bool success) {
// If the keys were refreshed successfully, the hardlock state should be
// cleared, so Smart Lock can be used normally. Otherwise, we fall back to
// a hardlock state to force the user to type in their credentials again.
if (success) {
SetHardlockStateForUser(GetUserEmail(),
EasyUnlockScreenlockStateHandler::NO_HARDLOCK);
}
// Even if the keys refresh suceeded, we still fetch the cryptohome keys as a
// sanity check.
CheckCryptohomeKeysAndMaybeHardlock();
}
#endif
void EasyUnlockServiceRegular::OpenSetupApp() {
ExtensionService* service =
extensions::ExtensionSystem::Get(profile())->extension_service();
const extensions::Extension* extension =
......@@ -103,7 +156,29 @@ void EasyUnlockServiceRegular::SetRemoteDevices(
DictionaryPrefUpdate pairing_update(profile()->GetPrefs(),
prefs::kEasyUnlockPairing);
pairing_update->SetWithoutPathExpansion(kKeyDevices, devices.DeepCopy());
#if defined(OS_CHROMEOS)
// TODO(tengs): Investigate if we can determine if the remote devices were set
// from sync or from the setup app.
if (short_lived_user_context_ && short_lived_user_context_->user_context() &&
!devices.empty()) {
// We may already have the password cached, so proceed to create the
// cryptohome keys for sign-in or the system will be hardlocked.
chromeos::UserContext* user_context =
short_lived_user_context_->user_context();
chromeos::EasyUnlockKeyManager* key_manager =
chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
key_manager->RefreshKeys(
*user_context, devices,
base::Bind(&EasyUnlockServiceRegular::OnKeysRefreshedForSetDevices,
weak_ptr_factory_.GetWeakPtr()));
} else {
CheckCryptohomeKeysAndMaybeHardlock();
}
#else
CheckCryptohomeKeysAndMaybeHardlock();
#endif
}
void EasyUnlockServiceRegular::ClearRemoteDevices() {
......@@ -184,6 +259,10 @@ void EasyUnlockServiceRegular::InitializeInternal() {
}
void EasyUnlockServiceRegular::ShutdownInternal() {
#if defined(OS_CHROMEOS)
short_lived_user_context_.reset();
#endif
turn_off_flow_.reset();
turn_off_flow_status_ = EasyUnlockService::IDLE;
registrar_.RemoveAll();
......
......@@ -12,6 +12,10 @@
#include "base/prefs/pref_change_registrar.h"
#include "chrome/browser/signin/easy_unlock_service.h"
#if defined(OS_CHROMEOS)
#include "chrome/browser/chromeos/login/easy_unlock/short_lived_user_context.h"
#endif
namespace base {
class DictionaryValue;
class ListValue;
......@@ -50,6 +54,9 @@ class EasyUnlockServiceRegular : public EasyUnlockService {
void ShutdownInternal() override;
bool IsAllowedInternal() override;
// Opens the component packaged app responsible for setting up Smart Lock.
void OpenSetupApp();
// Callback when the controlling pref changes.
void OnPrefsChanged();
......@@ -59,11 +66,20 @@ class EasyUnlockServiceRegular : public EasyUnlockService {
// Callback invoked when turn off flow has finished.
void OnTurnOffFlowFinished(bool success);
#if defined(OS_CHROMEOS)
void OnUserContextFromReauth(const chromeos::UserContext& user_context);
void OnKeysRefreshedForSetDevices(bool success);
scoped_ptr<chromeos::ShortLivedUserContext> short_lived_user_context_;
#endif
PrefChangeRegistrar registrar_;
TurnOffFlowStatus turn_off_flow_status_;
scoped_ptr<EasyUnlockToggleFlow> turn_off_flow_;
base::WeakPtrFactory<EasyUnlockServiceRegular> weak_ptr_factory_;
DISALLOW_COPY_AND_ASSIGN(EasyUnlockServiceRegular);
};
......
......@@ -62,9 +62,6 @@ scoped_ptr<base::DictionaryValue>
ScreenlockBridge::UserPodCustomIconOptions::ToDictionaryValue() const {
scoped_ptr<base::DictionaryValue> result(new base::DictionaryValue());
std::string icon_id = GetIdForIcon(icon_);
if (icon_id.empty())
return result.Pass();
result->SetString("id", icon_id);
if (!tooltip_.empty()) {
......
......@@ -1242,10 +1242,10 @@ void SigninScreenHandler::HandleAccountPickerReady() {
is_account_picker_showing_first_time_ = true;
gaia_screen_handler_->MaybePreloadAuthExtension();
ScreenlockBridge::Get()->SetLockHandler(this);
if (ScreenLocker::default_screen_locker()) {
ScreenLocker::default_screen_locker()->delegate()->OnLockWebUIReady();
}
ScreenlockBridge::Get()->SetLockHandler(this);
if (delegate_)
delegate_->OnSigninScreenReady();
......
......@@ -429,10 +429,14 @@
'browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h',
'browser/chromeos/login/easy_unlock/easy_unlock_metrics.cc',
'browser/chromeos/login/easy_unlock/easy_unlock_metrics.h',
'browser/chromeos/login/easy_unlock/easy_unlock_reauth.cc',
'browser/chromeos/login/easy_unlock/easy_unlock_reauth.h',
'browser/chromeos/login/easy_unlock/easy_unlock_remove_keys_operation.cc',
'browser/chromeos/login/easy_unlock/easy_unlock_remove_keys_operation.h',
'browser/chromeos/login/easy_unlock/easy_unlock_types.cc',
'browser/chromeos/login/easy_unlock/easy_unlock_types.h',
'browser/chromeos/login/easy_unlock/short_lived_user_context.cc',
'browser/chromeos/login/easy_unlock/short_lived_user_context.h',
'browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.cc',
'browser/chromeos/login/easy_unlock/easy_unlock_user_login_flow.h',
'browser/chromeos/login/enrollment/auto_enrollment_check_screen.cc',
......
......@@ -199,6 +199,13 @@ cr.define('login', function() {
UserPodCustomIcon.prototype = {
__proto__: HTMLDivElement.prototype,
/**
* The id of the icon being shown.
* @type {string}
* @private
*/
iconId_: '',
/**
* Tooltip to be shown when the user hovers over the icon. The icon
* properties may be set so the tooltip is shown automatically when the icon
......@@ -286,6 +293,7 @@ cr.define('login', function() {
* one of the ids listed in {@code UserPodCustomIcon.ICONS}.
*/
setIcon: function(id) {
this.iconId_ = id;
UserPodCustomIcon.ICONS.forEach(function(icon) {
this.iconElement.classList.toggle(icon.class, id == icon.id);
}, this);
......@@ -477,7 +485,9 @@ cr.define('login', function() {
bubbleContent.textContent = this.tooltip_;
/** @const */ var BUBBLE_OFFSET = CUSTOM_ICON_CONTAINER_SIZE / 2;
/** @const */ var BUBBLE_PADDING = 8;
// TODO(tengs): Introduce a special reauth state for the account picker,
// instead of showing the tooltip bubble here (crbug.com/409427).
/** @const */ var BUBBLE_PADDING = 8 + (this.iconId_ ? 0 : 23);
$('bubble').showContentForElement(this,
cr.ui.Bubble.Attachment.RIGHT,
bubbleContent,
......@@ -2265,10 +2275,11 @@ cr.define('login', function() {
return;
}
if (!icon.id)
if (!icon.id && !icon.tooltip)
return;
pod.customIconElement.setIcon(icon.id);
if (icon.id)
pod.customIconElement.setIcon(icon.id);
if (icon.hardlockOnClick) {
pod.customIconElement.setInteractive(
......
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