1// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/signin/easy_unlock_service_regular.h"
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "base/metrics/field_trial.h"
10#include "base/prefs/pref_service.h"
11#include "base/prefs/scoped_user_pref_update.h"
12#include "base/values.h"
13#include "chrome/browser/extensions/extension_service.h"
14#include "chrome/browser/profiles/profile.h"
15#include "chrome/browser/signin/easy_unlock_toggle_flow.h"
16#include "chrome/browser/signin/screenlock_bridge.h"
17#include "chrome/browser/ui/extensions/application_launch.h"
18#include "chrome/common/extensions/extension_constants.h"
19#include "chrome/common/pref_names.h"
20#include "components/pref_registry/pref_registry_syncable.h"
21#include "content/public/browser/browser_thread.h"
22#include "extensions/browser/extension_system.h"
23
24#if defined(OS_CHROMEOS)
25#include "apps/app_lifetime_monitor_factory.h"
26#include "base/thread_task_runner_handle.h"
27#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_key_manager.h"
28#include "chrome/browser/chromeos/login/easy_unlock/easy_unlock_reauth.h"
29#include "chrome/browser/chromeos/login/session/user_session_manager.h"
30#include "chrome/browser/chromeos/profiles/profile_helper.h"
31#include "components/user_manager/user_manager.h"
32#endif
33
34namespace {
35
36// Key name of the local device permit record dictonary in kEasyUnlockPairing.
37const char kKeyPermitAccess[] = "permitAccess";
38
39// Key name of the remote device list in kEasyUnlockPairing.
40const char kKeyDevices[] = "devices";
41
42// Key name of the phone public key in a device dictionary.
43const char kKeyPhoneId[] = "permitRecord.id";
44
45}  // namespace
46
47EasyUnlockServiceRegular::EasyUnlockServiceRegular(Profile* profile)
48    : EasyUnlockService(profile),
49      turn_off_flow_status_(EasyUnlockService::IDLE),
50      weak_ptr_factory_(this) {
51}
52
53EasyUnlockServiceRegular::~EasyUnlockServiceRegular() {
54}
55
56EasyUnlockService::Type EasyUnlockServiceRegular::GetType() const {
57  return EasyUnlockService::TYPE_REGULAR;
58}
59
60std::string EasyUnlockServiceRegular::GetUserEmail() const {
61  return ScreenlockBridge::GetAuthenticatedUserEmail(profile());
62}
63
64void EasyUnlockServiceRegular::LaunchSetup() {
65  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
66#if defined(OS_CHROMEOS)
67  // Force the user to reauthenticate by showing a modal overlay (similar to the
68  // lock screen). The password obtained from the reauth is cached for a short
69  // period of time and used to create the cryptohome keys for sign-in.
70  if (short_lived_user_context_ && short_lived_user_context_->user_context()) {
71    OpenSetupApp();
72  } else {
73    bool reauth_success = chromeos::EasyUnlockReauth::ReauthForUserContext(
74        base::Bind(&EasyUnlockServiceRegular::OnUserContextFromReauth,
75                   weak_ptr_factory_.GetWeakPtr()));
76    if (!reauth_success)
77      OpenSetupApp();
78  }
79#else
80  OpenSetupApp();
81#endif
82}
83
84#if defined(OS_CHROMEOS)
85void EasyUnlockServiceRegular::OnUserContextFromReauth(
86    const chromeos::UserContext& user_context) {
87  DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
88  short_lived_user_context_.reset(new chromeos::ShortLivedUserContext(
89      user_context, apps::AppLifetimeMonitorFactory::GetForProfile(profile()),
90      base::ThreadTaskRunnerHandle::Get().get()));
91
92  OpenSetupApp();
93}
94
95void EasyUnlockServiceRegular::OnKeysRefreshedForSetDevices(bool success) {
96  // If the keys were refreshed successfully, the hardlock state should be
97  // cleared, so Smart Lock can be used normally. Otherwise, we fall back to
98  // a hardlock state to force the user to type in their credentials again.
99  if (success) {
100    SetHardlockStateForUser(GetUserEmail(),
101                            EasyUnlockScreenlockStateHandler::NO_HARDLOCK);
102  }
103
104  // Even if the keys refresh suceeded, we still fetch the cryptohome keys as a
105  // sanity check.
106  CheckCryptohomeKeysAndMaybeHardlock();
107}
108#endif
109
110void EasyUnlockServiceRegular::OpenSetupApp() {
111  ExtensionService* service =
112      extensions::ExtensionSystem::Get(profile())->extension_service();
113  const extensions::Extension* extension =
114      service->GetExtensionById(extension_misc::kEasyUnlockAppId, false);
115
116  OpenApplication(AppLaunchParams(
117      profile(), extension, extensions::LAUNCH_CONTAINER_WINDOW, NEW_WINDOW));
118}
119
120const base::DictionaryValue* EasyUnlockServiceRegular::GetPermitAccess() const {
121  const base::DictionaryValue* pairing_dict =
122      profile()->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing);
123  const base::DictionaryValue* permit_dict = NULL;
124  if (pairing_dict &&
125      pairing_dict->GetDictionary(kKeyPermitAccess, &permit_dict))
126    return permit_dict;
127
128  return NULL;
129}
130
131void EasyUnlockServiceRegular::SetPermitAccess(
132    const base::DictionaryValue& permit) {
133  DictionaryPrefUpdate pairing_update(profile()->GetPrefs(),
134                                      prefs::kEasyUnlockPairing);
135  pairing_update->SetWithoutPathExpansion(kKeyPermitAccess, permit.DeepCopy());
136}
137
138void EasyUnlockServiceRegular::ClearPermitAccess() {
139  DictionaryPrefUpdate pairing_update(profile()->GetPrefs(),
140                                      prefs::kEasyUnlockPairing);
141  pairing_update->RemoveWithoutPathExpansion(kKeyPermitAccess, NULL);
142}
143
144const base::ListValue* EasyUnlockServiceRegular::GetRemoteDevices() const {
145  const base::DictionaryValue* pairing_dict =
146      profile()->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing);
147  const base::ListValue* devices = NULL;
148  if (pairing_dict && pairing_dict->GetList(kKeyDevices, &devices))
149    return devices;
150
151  return NULL;
152}
153
154void EasyUnlockServiceRegular::SetRemoteDevices(
155    const base::ListValue& devices) {
156  DictionaryPrefUpdate pairing_update(profile()->GetPrefs(),
157                                      prefs::kEasyUnlockPairing);
158  pairing_update->SetWithoutPathExpansion(kKeyDevices, devices.DeepCopy());
159
160#if defined(OS_CHROMEOS)
161  // TODO(tengs): Investigate if we can determine if the remote devices were set
162  // from sync or from the setup app.
163  if (short_lived_user_context_ && short_lived_user_context_->user_context() &&
164      !devices.empty()) {
165    // We may already have the password cached, so proceed to create the
166    // cryptohome keys for sign-in or the system will be hardlocked.
167    chromeos::UserContext* user_context =
168        short_lived_user_context_->user_context();
169    chromeos::EasyUnlockKeyManager* key_manager =
170        chromeos::UserSessionManager::GetInstance()->GetEasyUnlockKeyManager();
171
172    key_manager->RefreshKeys(
173        *user_context, devices,
174        base::Bind(&EasyUnlockServiceRegular::OnKeysRefreshedForSetDevices,
175                   weak_ptr_factory_.GetWeakPtr()));
176  } else {
177    CheckCryptohomeKeysAndMaybeHardlock();
178  }
179#else
180  CheckCryptohomeKeysAndMaybeHardlock();
181#endif
182}
183
184void EasyUnlockServiceRegular::ClearRemoteDevices() {
185  DictionaryPrefUpdate pairing_update(profile()->GetPrefs(),
186                                      prefs::kEasyUnlockPairing);
187  pairing_update->RemoveWithoutPathExpansion(kKeyDevices, NULL);
188  CheckCryptohomeKeysAndMaybeHardlock();
189}
190
191void EasyUnlockServiceRegular::RunTurnOffFlow() {
192  if (turn_off_flow_status_ == PENDING)
193    return;
194
195  SetTurnOffFlowStatus(PENDING);
196
197  // Currently there should only be one registered phone.
198  // TODO(xiyuan): Revisit this when server supports toggle for all or
199  // there are multiple phones.
200  const base::DictionaryValue* pairing_dict =
201      profile()->GetPrefs()->GetDictionary(prefs::kEasyUnlockPairing);
202  const base::ListValue* devices_list = NULL;
203  const base::DictionaryValue* first_device = NULL;
204  std::string phone_public_key;
205  if (!pairing_dict || !pairing_dict->GetList(kKeyDevices, &devices_list) ||
206      !devices_list || !devices_list->GetDictionary(0, &first_device) ||
207      !first_device ||
208      !first_device->GetString(kKeyPhoneId, &phone_public_key)) {
209    LOG(WARNING) << "Bad easy unlock pairing data, wiping out local data";
210    OnTurnOffFlowFinished(true);
211    return;
212  }
213
214  turn_off_flow_.reset(new EasyUnlockToggleFlow(
215      profile(),
216      phone_public_key,
217      false,
218      base::Bind(&EasyUnlockServiceRegular::OnTurnOffFlowFinished,
219                 base::Unretained(this))));
220  turn_off_flow_->Start();
221}
222
223void EasyUnlockServiceRegular::ResetTurnOffFlow() {
224  turn_off_flow_.reset();
225  SetTurnOffFlowStatus(IDLE);
226}
227
228EasyUnlockService::TurnOffFlowStatus
229    EasyUnlockServiceRegular::GetTurnOffFlowStatus() const {
230  return turn_off_flow_status_;
231}
232
233std::string EasyUnlockServiceRegular::GetChallenge() const {
234  return std::string();
235}
236
237std::string EasyUnlockServiceRegular::GetWrappedSecret() const {
238  return std::string();
239}
240
241void EasyUnlockServiceRegular::RecordEasySignInOutcome(
242    const std::string& user_id,
243    bool success) const {
244  NOTREACHED();
245}
246
247void EasyUnlockServiceRegular::RecordPasswordLoginEvent(
248    const std::string& user_id) const {
249  NOTREACHED();
250}
251
252void EasyUnlockServiceRegular::InitializeInternal() {
253  registrar_.Init(profile()->GetPrefs());
254  registrar_.Add(
255      prefs::kEasyUnlockAllowed,
256      base::Bind(&EasyUnlockServiceRegular::OnPrefsChanged,
257                 base::Unretained(this)));
258  OnPrefsChanged();
259}
260
261void EasyUnlockServiceRegular::ShutdownInternal() {
262#if defined(OS_CHROMEOS)
263  short_lived_user_context_.reset();
264#endif
265
266  turn_off_flow_.reset();
267  turn_off_flow_status_ = EasyUnlockService::IDLE;
268  registrar_.RemoveAll();
269}
270
271bool EasyUnlockServiceRegular::IsAllowedInternal() {
272#if defined(OS_CHROMEOS)
273  if (!user_manager::UserManager::Get()->IsLoggedInAsRegularUser())
274    return false;
275
276  if (!chromeos::ProfileHelper::IsPrimaryProfile(profile()))
277    return false;
278
279  if (!profile()->GetPrefs()->GetBoolean(prefs::kEasyUnlockAllowed))
280    return false;
281
282  // Respect existing policy and skip finch test.
283  if (!profile()->GetPrefs()->IsManagedPreference(prefs::kEasyUnlockAllowed)) {
284    // It is enabled when the trial exists and is in "Enable" group.
285    return base::FieldTrialList::FindFullName("EasyUnlock") == "Enable";
286  }
287
288  return true;
289#else
290  // TODO(xiyuan): Revisit when non-chromeos platforms are supported.
291  return false;
292#endif
293}
294
295void EasyUnlockServiceRegular::OnPrefsChanged() {
296  UpdateAppState();
297}
298
299void EasyUnlockServiceRegular::SetTurnOffFlowStatus(TurnOffFlowStatus status) {
300  turn_off_flow_status_ = status;
301  NotifyTurnOffOperationStatusChanged();
302}
303
304void EasyUnlockServiceRegular::OnTurnOffFlowFinished(bool success) {
305  turn_off_flow_.reset();
306
307  if (!success) {
308    SetTurnOffFlowStatus(FAIL);
309    return;
310  }
311
312  ClearRemoteDevices();
313  SetTurnOffFlowStatus(IDLE);
314  ReloadApp();
315}
316