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_authenticator.h"
6
7#include "base/bind.h"
8#include "base/strings/string_number_conversions.h"
9#include "base/strings/string_util.h"
10#include "chrome/browser/chromeos/boot_times_loader.h"
11#include "chromeos/cryptohome/async_method_caller.h"
12#include "chromeos/cryptohome/cryptohome_parameters.h"
13#include "chromeos/cryptohome/system_salt_getter.h"
14#include "chromeos/dbus/cryptohome_client.h"
15#include "chromeos/dbus/dbus_thread_manager.h"
16#include "chromeos/login/auth/key.h"
17#include "content/public/browser/browser_thread.h"
18#include "crypto/sha2.h"
19#include "google_apis/gaia/gaia_auth_util.h"
20#include "third_party/cros_system_api/dbus/service_constants.h"
21
22using content::BrowserThread;
23
24namespace chromeos {
25
26namespace {
27
28// Records status and calls resolver->Resolve().
29void TriggerResolve(SupervisedUserAuthenticator::AuthAttempt* attempt,
30                    scoped_refptr<SupervisedUserAuthenticator> resolver,
31                    bool success,
32                    cryptohome::MountError return_code) {
33  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
34  attempt->RecordCryptohomeStatus(success, return_code);
35  resolver->Resolve();
36}
37
38// Records status and calls resolver->Resolve().
39void TriggerResolveResult(SupervisedUserAuthenticator::AuthAttempt* attempt,
40                          scoped_refptr<SupervisedUserAuthenticator> resolver,
41                          bool success,
42                          const std::string& result) {
43  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
44  attempt->RecordHash(result);
45  resolver->Resolve();
46}
47
48// Calls TriggerResolve while adding login time marker.
49void TriggerResolveWithLoginTimeMarker(
50    const std::string& marker_name,
51    SupervisedUserAuthenticator::AuthAttempt* attempt,
52    scoped_refptr<SupervisedUserAuthenticator> resolver,
53    bool success,
54    cryptohome::MountError return_code) {
55  chromeos::BootTimesLoader::Get()->AddLoginTimeMarker(marker_name, false);
56  TriggerResolve(attempt, resolver, success, return_code);
57}
58
59// Calls cryptohome's mount method.
60void Mount(SupervisedUserAuthenticator::AuthAttempt* attempt,
61           scoped_refptr<SupervisedUserAuthenticator> resolver,
62           int flags,
63           const std::string& system_salt) {
64  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
65  chromeos::BootTimesLoader::Get()->AddLoginTimeMarker(
66      "CryptohomeMount-LMU-Start", false);
67
68  Key key(attempt->password);
69  key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
70  cryptohome::AsyncMethodCaller::GetInstance()->AsyncMount(
71      attempt->username,
72      key.GetSecret(),
73      flags,
74      base::Bind(&TriggerResolveWithLoginTimeMarker,
75                 "CryptohomeMount-LMU-End",
76                 attempt,
77                 resolver));
78
79  cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
80      attempt->username,
81      base::Bind(&TriggerResolveResult, attempt, resolver));
82}
83
84// Calls cryptohome's addKey method.
85void AddKey(SupervisedUserAuthenticator::AuthAttempt* attempt,
86            scoped_refptr<SupervisedUserAuthenticator> resolver,
87            const std::string& plain_text_master_key,
88            const std::string& system_salt) {
89  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
90  chromeos::BootTimesLoader::Get()->AddLoginTimeMarker(
91      "CryptohomeAddKey-LMU-Start", false);
92
93  Key user_key(attempt->password);
94  user_key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
95  Key master_key(plain_text_master_key);
96  master_key.Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
97  cryptohome::AsyncMethodCaller::GetInstance()->AsyncAddKey(
98      attempt->username,
99      user_key.GetSecret(),
100      master_key.GetSecret(),
101      base::Bind(&TriggerResolveWithLoginTimeMarker,
102                 "CryptohomeAddKey-LMU-End",
103                 attempt,
104                 resolver));
105}
106
107}  // namespace
108
109SupervisedUserAuthenticator::SupervisedUserAuthenticator(
110    AuthStatusConsumer* consumer)
111    : consumer_(consumer) {}
112
113void SupervisedUserAuthenticator::AuthenticateToMount(
114    const std::string& username,
115    const std::string& password) {
116  std::string canonicalized = gaia::CanonicalizeEmail(username);
117
118  current_state_.reset(new SupervisedUserAuthenticator::AuthAttempt(
119      canonicalized, password, false));
120
121  SystemSaltGetter::Get()->GetSystemSalt(
122      base::Bind(&Mount,
123                 current_state_.get(),
124                 scoped_refptr<SupervisedUserAuthenticator>(this),
125                 cryptohome::MOUNT_FLAGS_NONE));
126}
127
128void SupervisedUserAuthenticator::AuthenticateToCreate(
129    const std::string& username,
130    const std::string& password) {
131  std::string canonicalized = gaia::CanonicalizeEmail(username);
132
133  current_state_.reset(new SupervisedUserAuthenticator::AuthAttempt(
134      canonicalized, password, false));
135
136  SystemSaltGetter::Get()->GetSystemSalt(
137      base::Bind(&Mount,
138                 current_state_.get(),
139                 scoped_refptr<SupervisedUserAuthenticator>(this),
140                 cryptohome::CREATE_IF_MISSING));
141}
142
143void SupervisedUserAuthenticator::AddMasterKey(
144    const std::string& username,
145    const std::string& password,
146    const std::string& master_key) {
147  std::string canonicalized = gaia::CanonicalizeEmail(username);
148
149  current_state_.reset(new SupervisedUserAuthenticator::AuthAttempt(
150      canonicalized, password, true));
151
152  SystemSaltGetter::Get()->GetSystemSalt(
153      base::Bind(&AddKey,
154                 current_state_.get(),
155                 scoped_refptr<SupervisedUserAuthenticator>(this),
156                 master_key));
157}
158
159void SupervisedUserAuthenticator::OnAuthenticationSuccess(
160    const std::string& mount_hash,
161    bool add_key) {
162  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
163  VLOG(1) << "Supervised user authentication success";
164  if (consumer_) {
165    if (add_key)
166      consumer_->OnAddKeySuccess();
167    else
168      consumer_->OnMountSuccess(mount_hash);
169  }
170}
171
172void SupervisedUserAuthenticator::OnAuthenticationFailure(
173    SupervisedUserAuthenticator::AuthState state) {
174  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
175  LOG(WARNING) << "Supervised user authentication failure";
176  if (consumer_)
177    consumer_->OnAuthenticationFailure(state);
178}
179
180void SupervisedUserAuthenticator::Resolve() {
181  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
182  SupervisedUserAuthenticator::AuthState state = ResolveState();
183  VLOG(1) << "Resolved state to: " << state;
184  switch (state) {
185    case CONTINUE:
186      // These are intermediate states; we need more info from a request that
187      // is still pending.
188      break;
189    case FAILED_MOUNT:
190      // In this case, whether login succeeded or not, we can't log
191      // the user in because their data is horked.  So, override with
192      // the appropriate failure.
193      BrowserThread::PostTask(
194          BrowserThread::UI,
195          FROM_HERE,
196          base::Bind(&SupervisedUserAuthenticator::OnAuthenticationFailure,
197                     this,
198                     state));
199      break;
200    case NO_MOUNT:
201      // In this case, whether login succeeded or not, we can't log
202      // the user in because no data exist. So, override with
203      // the appropriate failure.
204      BrowserThread::PostTask(
205          BrowserThread::UI,
206          FROM_HERE,
207          base::Bind(&SupervisedUserAuthenticator::OnAuthenticationFailure,
208                     this,
209                     state));
210      break;
211    case FAILED_TPM:
212      // In this case, we tried to create/mount cryptohome and failed
213      // because of the critical TPM error.
214      // Chrome will notify user and request reboot.
215      BrowserThread::PostTask(
216          BrowserThread::UI,
217          FROM_HERE,
218          base::Bind(&SupervisedUserAuthenticator::OnAuthenticationFailure,
219                     this,
220                     state));
221      break;
222    case SUCCESS:
223      VLOG(2) << "Supervised user login";
224      BrowserThread::PostTask(
225          BrowserThread::UI,
226          FROM_HERE,
227          base::Bind(&SupervisedUserAuthenticator::OnAuthenticationSuccess,
228                     this,
229                     current_state_->hash(),
230                     current_state_->add_key));
231      break;
232    default:
233      NOTREACHED();
234      break;
235  }
236}
237
238SupervisedUserAuthenticator::~SupervisedUserAuthenticator() {}
239
240SupervisedUserAuthenticator::AuthState
241SupervisedUserAuthenticator::ResolveState() {
242  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
243  // If we haven't mounted the user's home dir yet, we can't be done.
244  // We never get past here if a cryptohome op is still pending.
245  // This is an important invariant.
246  if (!current_state_->cryptohome_complete())
247    return CONTINUE;
248  if (!current_state_->add_key && !current_state_->hash_obtained())
249    return CONTINUE;
250
251  AuthState state;
252
253  if (current_state_->cryptohome_outcome())
254    state = ResolveCryptohomeSuccessState();
255  else
256    state = ResolveCryptohomeFailureState();
257
258  DCHECK(current_state_->cryptohome_complete());
259  DCHECK(current_state_->hash_obtained() || current_state_->add_key);
260  return state;
261}
262
263SupervisedUserAuthenticator::AuthState
264    SupervisedUserAuthenticator::ResolveCryptohomeFailureState() {
265  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
266  LOG(ERROR) << "Failed to authenticate supervised user, code: "
267             << current_state_->cryptohome_code();
268  if (current_state_->cryptohome_code() ==
269      cryptohome::MOUNT_ERROR_TPM_NEEDS_REBOOT) {
270    // Critical TPM error detected, reboot needed.
271    return FAILED_TPM;
272  }
273
274  if (current_state_->cryptohome_code() ==
275      cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST) {
276    // If we tried a mount but the user did not exist, then we should wait
277    // for online login to succeed and try again with the "create" flag set.
278    return NO_MOUNT;
279  }
280
281  return FAILED_MOUNT;
282}
283
284SupervisedUserAuthenticator::AuthState
285    SupervisedUserAuthenticator::ResolveCryptohomeSuccessState() {
286  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
287  return SUCCESS;
288}
289
290SupervisedUserAuthenticator::AuthAttempt::AuthAttempt(
291    const std::string& username,
292    const std::string& password,
293    bool add_key_attempt)
294    : username(username),
295      password(password),
296      add_key(add_key_attempt),
297      cryptohome_complete_(false),
298      cryptohome_outcome_(false),
299      hash_obtained_(false),
300      cryptohome_code_(cryptohome::MOUNT_ERROR_NONE) {}
301
302SupervisedUserAuthenticator::AuthAttempt::~AuthAttempt() {}
303
304void SupervisedUserAuthenticator::AuthAttempt::RecordCryptohomeStatus(
305    bool cryptohome_outcome,
306    cryptohome::MountError cryptohome_code) {
307  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
308  cryptohome_complete_ = true;
309  cryptohome_outcome_ = cryptohome_outcome;
310  cryptohome_code_ = cryptohome_code;
311}
312
313void SupervisedUserAuthenticator::AuthAttempt::RecordHash(
314    const std::string& hash) {
315  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
316  hash_obtained_ = true;
317  hash_ = hash;
318}
319
320bool SupervisedUserAuthenticator::AuthAttempt::cryptohome_complete() {
321  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
322  return cryptohome_complete_;
323}
324
325bool SupervisedUserAuthenticator::AuthAttempt::cryptohome_outcome() {
326  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
327  return cryptohome_outcome_;
328}
329
330cryptohome::MountError
331    SupervisedUserAuthenticator::AuthAttempt::cryptohome_code() {
332  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
333  return cryptohome_code_;
334}
335
336bool SupervisedUserAuthenticator::AuthAttempt::hash_obtained() {
337  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
338  return hash_obtained_;
339}
340
341std::string SupervisedUserAuthenticator::AuthAttempt::hash() {
342  DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
343  return hash_;
344}
345
346}  // namespace chromeos
347