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/chromeos/login/supervised/supervised_user_login_flow.h"
6
7#include "base/base64.h"
8#include "base/logging.h"
9#include "base/metrics/histogram.h"
10#include "base/prefs/pref_registry_simple.h"
11#include "base/prefs/pref_service.h"
12#include "base/values.h"
13#include "chrome/browser/chromeos/login/login_utils.h"
14#include "chrome/browser/chromeos/login/supervised/supervised_user_authentication.h"
15#include "chrome/browser/chromeos/login/supervised/supervised_user_constants.h"
16#include "chrome/browser/chromeos/login/supervised/supervised_user_creation_screen.h"
17#include "chrome/browser/chromeos/login/ui/login_display_host_impl.h"
18#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
19#include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
20#include "chrome/browser/chromeos/login/wizard_controller.h"
21#include "chromeos/login/auth/key.h"
22#include "components/user_manager/user_manager.h"
23#include "content/public/browser/browser_thread.h"
24
25using content::BrowserThread;
26
27namespace chromeos {
28
29SupervisedUserLoginFlow::SupervisedUserLoginFlow(
30    const std::string& user_id)
31    : ExtendedUserFlow(user_id),
32      data_loaded_(false),
33      weak_factory_(this) {
34}
35
36SupervisedUserLoginFlow::~SupervisedUserLoginFlow() {}
37
38bool SupervisedUserLoginFlow::CanLockScreen() {
39  return true;
40}
41
42bool SupervisedUserLoginFlow::ShouldLaunchBrowser() {
43  return data_loaded_;
44}
45
46bool SupervisedUserLoginFlow::ShouldSkipPostLoginScreens() {
47  return true;
48}
49
50bool SupervisedUserLoginFlow::SupportsEarlyRestartToApplyFlags() {
51  return false;
52}
53
54bool SupervisedUserLoginFlow::HandleLoginFailure(const AuthFailure& failure) {
55  return false;
56}
57
58bool SupervisedUserLoginFlow::HandlePasswordChangeDetected() {
59  return false;
60}
61
62void SupervisedUserLoginFlow::HandleOAuthTokenStatusChange(
63    user_manager::User::OAuthTokenStatus status) {
64}
65
66void SupervisedUserLoginFlow::OnSyncSetupDataLoaded(
67    const std::string& token) {
68  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
69  ConfigureSync(token);
70}
71
72void SupervisedUserLoginFlow::ConfigureSync(const std::string& token) {
73  data_loaded_ = true;
74
75  // TODO(antrim): add error handling (no token loaded).
76  // See also: http://crbug.com/312751
77  ChromeUserManager::Get()->GetSupervisedUserManager()->ConfigureSyncWithToken(
78      profile_, token);
79  SupervisedUserAuthentication* auth =
80      ChromeUserManager::Get()->GetSupervisedUserManager()->GetAuthentication();
81
82  if (auth->HasScheduledPasswordUpdate(user_id())) {
83    auth->LoadPasswordUpdateData(
84        user_id(),
85        base::Bind(&SupervisedUserLoginFlow::OnPasswordChangeDataLoaded,
86                   weak_factory_.GetWeakPtr()),
87        base::Bind(&SupervisedUserLoginFlow::OnPasswordChangeDataLoadFailed,
88                   weak_factory_.GetWeakPtr()));
89    return;
90  }
91  Finish();
92}
93
94void SupervisedUserLoginFlow::HandleLoginSuccess(
95    const UserContext& login_context) {
96  context_ = login_context;
97}
98
99void SupervisedUserLoginFlow::OnPasswordChangeDataLoaded(
100    const base::DictionaryValue* password_data) {
101  // Edge case, when manager has signed in and already updated the password.
102  SupervisedUserAuthentication* auth =
103      ChromeUserManager::Get()->GetSupervisedUserManager()->GetAuthentication();
104  if (!auth->NeedPasswordChange(user_id(), password_data)) {
105    VLOG(1) << "Password already changed for " << user_id();
106    auth->ClearScheduledPasswordUpdate(user_id());
107    Finish();
108    return;
109  }
110
111  // Two cases now - we can currently have either old-style password, or new
112  // password.
113  std::string base64_signature;
114  std::string signature;
115  std::string password;
116  int revision = 0;
117  int schema = 0;
118  bool success = password_data->GetStringWithoutPathExpansion(
119      kPasswordSignature, &base64_signature);
120  success &= password_data->GetIntegerWithoutPathExpansion(kPasswordRevision,
121                                                           &revision);
122  success &=
123      password_data->GetIntegerWithoutPathExpansion(kSchemaVersion, &schema);
124  success &= password_data->GetStringWithoutPathExpansion(kEncryptedPassword,
125                                                          &password);
126  if (!success) {
127    LOG(ERROR) << "Incomplete data for password change";
128
129    UMA_HISTOGRAM_ENUMERATION(
130        "ManagedUsers.ChromeOS.PasswordChange",
131        SupervisedUserAuthentication::PASSWORD_CHANGE_FAILED_INCOMPLETE_DATA,
132        SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
133    Finish();
134    return;
135  }
136  base::Base64Decode(base64_signature, &signature);
137  scoped_ptr<base::DictionaryValue> data_copy(password_data->DeepCopy());
138  cryptohome::KeyDefinition key(password,
139                                kCryptohomeSupervisedUserKeyLabel,
140                                kCryptohomeSupervisedUserKeyPrivileges);
141
142  authenticator_ = ExtendedAuthenticator::Create(this);
143  SupervisedUserAuthentication::Schema current_schema =
144      auth->GetPasswordSchema(user_id());
145
146  key.revision = revision;
147
148  if (SupervisedUserAuthentication::SCHEMA_PLAIN == current_schema) {
149    // We need to add new key, and block old one. As we don't actually have
150    // signature key, use Migrate privilege instead of AuthorizedUpdate.
151    key.privileges = kCryptohomeSupervisedUserIncompleteKeyPrivileges;
152
153    VLOG(1) << "Adding new schema key";
154    DCHECK(context_.GetKey()->GetLabel().empty());
155    authenticator_->AddKey(context_,
156                           key,
157                           false /* no key exists */,
158                           base::Bind(&SupervisedUserLoginFlow::OnNewKeyAdded,
159                                      weak_factory_.GetWeakPtr(),
160                                      Passed(&data_copy)));
161  } else if (SupervisedUserAuthentication::SCHEMA_SALT_HASHED ==
162             current_schema) {
163    VLOG(1) << "Updating the key";
164
165    if (auth->HasIncompleteKey(user_id())) {
166      // We need to use Migrate instead of Authorized Update privilege.
167      key.privileges = kCryptohomeSupervisedUserIncompleteKeyPrivileges;
168    }
169    // Just update the key.
170    DCHECK_EQ(context_.GetKey()->GetLabel(), kCryptohomeSupervisedUserKeyLabel);
171    authenticator_->UpdateKeyAuthorized(
172        context_,
173        key,
174        signature,
175        base::Bind(&SupervisedUserLoginFlow::OnPasswordUpdated,
176                   weak_factory_.GetWeakPtr(),
177                   Passed(&data_copy)));
178  } else {
179    NOTREACHED() << "Unsupported password schema";
180  }
181}
182
183void SupervisedUserLoginFlow::OnNewKeyAdded(
184    scoped_ptr<base::DictionaryValue> password_data) {
185  VLOG(1) << "New key added";
186  SupervisedUserAuthentication* auth =
187      ChromeUserManager::Get()->GetSupervisedUserManager()->GetAuthentication();
188  auth->StorePasswordData(user_id(), *password_data.get());
189  auth->MarkKeyIncomplete(user_id(), true /* incomplete */);
190  authenticator_->RemoveKey(
191      context_,
192      kLegacyCryptohomeSupervisedUserKeyLabel,
193      base::Bind(&SupervisedUserLoginFlow::OnOldKeyRemoved,
194                 weak_factory_.GetWeakPtr()));
195}
196
197void SupervisedUserLoginFlow::OnOldKeyRemoved() {
198  UMA_HISTOGRAM_ENUMERATION(
199      "ManagedUsers.ChromeOS.PasswordChange",
200      SupervisedUserAuthentication::PASSWORD_CHANGED_IN_USER_SESSION,
201      SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
202  Finish();
203}
204
205void SupervisedUserLoginFlow::OnPasswordChangeDataLoadFailed() {
206  LOG(ERROR) << "Could not load data for password change";
207
208  UMA_HISTOGRAM_ENUMERATION(
209      "ManagedUsers.ChromeOS.PasswordChange",
210      SupervisedUserAuthentication::PASSWORD_CHANGE_FAILED_LOADING_DATA,
211      SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
212  Finish();
213}
214
215void SupervisedUserLoginFlow::OnAuthenticationFailure(
216    ExtendedAuthenticator::AuthState state) {
217  LOG(ERROR) << "Authentication error during password change";
218
219  UMA_HISTOGRAM_ENUMERATION(
220      "ManagedUsers.ChromeOS.PasswordChange",
221      SupervisedUserAuthentication::
222          PASSWORD_CHANGE_FAILED_AUTHENTICATION_FAILURE,
223      SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
224  Finish();
225}
226
227void SupervisedUserLoginFlow::OnPasswordUpdated(
228    scoped_ptr<base::DictionaryValue> password_data) {
229  VLOG(1) << "Updated password for supervised user";
230
231  SupervisedUserAuthentication* auth =
232      ChromeUserManager::Get()->GetSupervisedUserManager()->GetAuthentication();
233
234  // Incomplete state is not there in password_data, carry it from old state.
235  bool was_incomplete = auth->HasIncompleteKey(user_id());
236  auth->StorePasswordData(user_id(), *password_data.get());
237  if (was_incomplete)
238    auth->MarkKeyIncomplete(user_id(), true /* incomplete */);
239
240  UMA_HISTOGRAM_ENUMERATION(
241      "ManagedUsers.ChromeOS.PasswordChange",
242      SupervisedUserAuthentication::PASSWORD_CHANGED_IN_USER_SESSION,
243      SupervisedUserAuthentication::PASSWORD_CHANGE_RESULT_MAX_VALUE);
244  Finish();
245}
246
247void SupervisedUserLoginFlow::Finish() {
248  LoginUtils::Get()->DoBrowserLaunch(profile_, host());
249  profile_ = NULL;
250  UnregisterFlowSoon();
251}
252
253void SupervisedUserLoginFlow::LaunchExtraSteps(
254    Profile* profile) {
255  profile_ = profile;
256  ChromeUserManager::Get()->GetSupervisedUserManager()->LoadSupervisedUserToken(
257      profile,
258      base::Bind(&SupervisedUserLoginFlow::OnSyncSetupDataLoaded,
259                 weak_factory_.GetWeakPtr()));
260}
261
262}  // namespace chromeos
263