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 "chromeos/login/auth/cryptohome_authenticator.h"
6
7#include <vector>
8
9#include "base/basictypes.h"
10#include "base/bind.h"
11#include "base/files/file_path.h"
12#include "base/location.h"
13#include "base/logging.h"
14#include "chromeos/cryptohome/async_method_caller.h"
15#include "chromeos/cryptohome/cryptohome_parameters.h"
16#include "chromeos/cryptohome/homedir_methods.h"
17#include "chromeos/cryptohome/system_salt_getter.h"
18#include "chromeos/dbus/cryptohome_client.h"
19#include "chromeos/dbus/dbus_thread_manager.h"
20#include "chromeos/login/auth/auth_status_consumer.h"
21#include "chromeos/login/auth/key.h"
22#include "chromeos/login/auth/user_context.h"
23#include "chromeos/login/login_state.h"
24#include "chromeos/login/user_names.h"
25#include "chromeos/login_event_recorder.h"
26#include "components/user_manager/user_type.h"
27#include "third_party/cros_system_api/dbus/service_constants.h"
28
29namespace chromeos {
30
31namespace {
32
33// The label used for the key derived from the user's GAIA credentials.
34const char kCryptohomeGAIAKeyLabel[] = "gaia";
35
36// The name under which the type of key generated from the user's GAIA
37// credentials is stored.
38const char kKeyProviderDataTypeName[] = "type";
39
40// The name under which the salt used to generate a key from the user's GAIA
41// credentials is stored.
42const char kKeyProviderDataSaltName[] = "salt";
43
44// Hashes |key| with |system_salt| if it its type is KEY_TYPE_PASSWORD_PLAIN.
45// Returns the keys unmodified otherwise.
46scoped_ptr<Key> TransformKeyIfNeeded(const Key& key,
47                                     const std::string& system_salt) {
48  scoped_ptr<Key> result(new Key(key));
49  if (result->GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN)
50    result->Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt);
51
52  return result.Pass();
53}
54
55// Records status and calls resolver->Resolve().
56void TriggerResolve(AuthAttemptState* attempt,
57                    scoped_refptr<CryptohomeAuthenticator> resolver,
58                    bool success,
59                    cryptohome::MountError return_code) {
60  attempt->RecordCryptohomeStatus(success, return_code);
61  resolver->Resolve();
62}
63
64// Records get hash status and calls resolver->Resolve().
65void TriggerResolveHash(AuthAttemptState* attempt,
66                        scoped_refptr<CryptohomeAuthenticator> resolver,
67                        bool success,
68                        const std::string& username_hash) {
69  if (success)
70    attempt->RecordUsernameHash(username_hash);
71  else
72    attempt->RecordUsernameHashFailed();
73  resolver->Resolve();
74}
75
76// Calls TriggerResolve while adding login time marker.
77void TriggerResolveWithLoginTimeMarker(
78    const std::string& marker_name,
79    AuthAttemptState* attempt,
80    scoped_refptr<CryptohomeAuthenticator> resolver,
81    bool success,
82    cryptohome::MountError return_code) {
83  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(marker_name, false);
84  TriggerResolve(attempt, resolver, success, return_code);
85}
86
87// Records an error in accessing the user's cryptohome with the given key and
88// calls resolver->Resolve() after adding a login time marker.
89void RecordKeyErrorAndResolve(AuthAttemptState* attempt,
90                              scoped_refptr<CryptohomeAuthenticator> resolver) {
91  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End",
92                                                          false);
93  attempt->RecordCryptohomeStatus(false /* success */,
94                                  cryptohome::MOUNT_ERROR_KEY_FAILURE);
95  resolver->Resolve();
96}
97
98// Callback invoked when cryptohome's MountEx() method has finished.
99void OnMount(AuthAttemptState* attempt,
100             scoped_refptr<CryptohomeAuthenticator> resolver,
101             bool success,
102             cryptohome::MountError return_code,
103             const std::string& mount_hash) {
104  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End",
105                                                          false);
106  attempt->RecordCryptohomeStatus(success, return_code);
107  if (success)
108    attempt->RecordUsernameHash(mount_hash);
109  else
110    attempt->RecordUsernameHashFailed();
111  resolver->Resolve();
112}
113
114// Calls cryptohome's MountEx() method. The key in |attempt->user_context| must
115// not be a plain text password. If the user provided a plain text password,
116// that password must be transformed to another key type (by salted hashing)
117// before calling this method.
118void DoMount(AuthAttemptState* attempt,
119             scoped_refptr<CryptohomeAuthenticator> resolver,
120             bool ephemeral,
121             bool create_if_nonexistent) {
122  const Key* key = attempt->user_context.GetKey();
123  // If the |key| is a plain text password, crash rather than attempting to
124  // mount the cryptohome with a plain text password.
125  CHECK_NE(Key::KEY_TYPE_PASSWORD_PLAIN, key->GetKeyType());
126
127  // Set state that username_hash is requested here so that test implementation
128  // that returns directly would not generate 2 OnLoginSucces() calls.
129  attempt->UsernameHashRequested();
130
131  // Set the authentication's key label to an empty string, which is a wildcard
132  // allowing any key to match. This is necessary because cryptohomes created by
133  // Chrome OS M38 and older will have a legacy key with no label while those
134  // created by Chrome OS M39 and newer will have a key with the label
135  // kCryptohomeGAIAKeyLabel.
136  const cryptohome::KeyDefinition auth_key(key->GetSecret(),
137                                           std::string(),
138                                           cryptohome::PRIV_DEFAULT);
139  cryptohome::MountParameters mount(ephemeral);
140  if (create_if_nonexistent) {
141    mount.create_keys.push_back(cryptohome::KeyDefinition(
142        key->GetSecret(),
143        kCryptohomeGAIAKeyLabel,
144        cryptohome::PRIV_DEFAULT));
145  }
146
147  cryptohome::HomedirMethods::GetInstance()->MountEx(
148      cryptohome::Identification(attempt->user_context.GetUserID()),
149      cryptohome::Authorization(auth_key),
150      mount,
151      base::Bind(&OnMount, attempt, resolver));
152}
153
154// Callback invoked when the system salt has been retrieved. Transforms the key
155// in |attempt->user_context| using Chrome's default hashing algorithm and the
156// system salt, then calls MountEx().
157void OnGetSystemSalt(AuthAttemptState* attempt,
158                    scoped_refptr<CryptohomeAuthenticator> resolver,
159                    bool ephemeral,
160                    bool create_if_nonexistent,
161                    const std::string& system_salt) {
162  DCHECK_EQ(Key::KEY_TYPE_PASSWORD_PLAIN,
163            attempt->user_context.GetKey()->GetKeyType());
164
165  attempt->user_context.GetKey()->Transform(
166      Key::KEY_TYPE_SALTED_SHA256_TOP_HALF,
167      system_salt);
168
169  DoMount(attempt, resolver, ephemeral, create_if_nonexistent);
170}
171
172// Callback invoked when cryptohome's GetKeyDataEx() method has finished.
173// * If GetKeyDataEx() returned metadata indicating the hashing algorithm and
174//   salt that were used to generate the key for this user's cryptohome,
175//   transforms the key in |attempt->user_context| with the same parameters.
176// * Otherwise, starts the retrieval of the system salt so that the key in
177//   |attempt->user_context| can be transformed with Chrome's default hashing
178//   algorithm and the system salt.
179// The resulting key is then passed to cryptohome's MountEx().
180void OnGetKeyDataEx(
181    AuthAttemptState* attempt,
182    scoped_refptr<CryptohomeAuthenticator> resolver,
183    bool ephemeral,
184    bool create_if_nonexistent,
185    bool success,
186    cryptohome::MountError return_code,
187    const std::vector<cryptohome::KeyDefinition>& key_definitions) {
188  if (success) {
189    if (key_definitions.size() == 1) {
190      const cryptohome::KeyDefinition& key_definition = key_definitions.front();
191      DCHECK_EQ(kCryptohomeGAIAKeyLabel, key_definition.label);
192
193      // Extract the key type and salt from |key_definition|, if present.
194      scoped_ptr<int64> type;
195      scoped_ptr<std::string> salt;
196      for (std::vector<cryptohome::KeyDefinition::ProviderData>::
197               const_iterator it = key_definition.provider_data.begin();
198           it != key_definition.provider_data.end(); ++it) {
199        if (it->name == kKeyProviderDataTypeName) {
200          if (it->number)
201            type.reset(new int64(*it->number));
202          else
203            NOTREACHED();
204        } else if (it->name == kKeyProviderDataSaltName) {
205          if (it->bytes)
206            salt.reset(new std::string(*it->bytes));
207          else
208            NOTREACHED();
209        }
210      }
211
212      if (type) {
213        if (*type < 0 || *type >= Key::KEY_TYPE_COUNT) {
214          LOG(ERROR) << "Invalid key type: " << *type;
215          RecordKeyErrorAndResolve(attempt, resolver);
216          return;
217        }
218
219        if (!salt) {
220          LOG(ERROR) << "Missing salt.";
221          RecordKeyErrorAndResolve(attempt, resolver);
222          return;
223        }
224
225        attempt->user_context.GetKey()->Transform(
226            static_cast<Key::KeyType>(*type),
227            *salt);
228        DoMount(attempt, resolver, ephemeral, create_if_nonexistent);
229        return;
230      }
231    } else {
232      LOG(ERROR) << "GetKeyDataEx() returned " << key_definitions.size()
233                 << " entries.";
234    }
235  }
236
237  SystemSaltGetter::Get()->GetSystemSalt(base::Bind(&OnGetSystemSalt,
238                                                    attempt,
239                                                    resolver,
240                                                    ephemeral,
241                                                    create_if_nonexistent));
242}
243
244// Starts the process that will mount a user's cryptohome.
245// * If the key in |attempt->user_context| is not a plain text password,
246//   cryptohome's MountEx() method is called directly with the key.
247// * Otherwise, the key must be transformed (by salted hashing) before being
248//   passed to MountEx(). In that case, cryptohome's GetKeyDataEx() method is
249//   called to retrieve metadata indicating the hashing algorithm and salt that
250//   were used to generate the key for this user's cryptohome and the key is
251//   transformed accordingly before calling MountEx().
252void StartMount(AuthAttemptState* attempt,
253                scoped_refptr<CryptohomeAuthenticator> resolver,
254                bool ephemeral,
255                bool create_if_nonexistent) {
256  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(
257      "CryptohomeMount-Start", false);
258
259  if (attempt->user_context.GetKey()->GetKeyType() !=
260          Key::KEY_TYPE_PASSWORD_PLAIN) {
261    DoMount(attempt, resolver, ephemeral, create_if_nonexistent);
262    return;
263  }
264
265  cryptohome::HomedirMethods::GetInstance()->GetKeyDataEx(
266      cryptohome::Identification(attempt->user_context.GetUserID()),
267      kCryptohomeGAIAKeyLabel,
268      base::Bind(&OnGetKeyDataEx,
269                 attempt,
270                 resolver,
271                 ephemeral,
272                 create_if_nonexistent));
273}
274
275// Calls cryptohome's mount method for guest and also get the user hash from
276// cryptohome.
277void MountGuestAndGetHash(AuthAttemptState* attempt,
278                          scoped_refptr<CryptohomeAuthenticator> resolver) {
279  attempt->UsernameHashRequested();
280  cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountGuest(
281      base::Bind(&TriggerResolveWithLoginTimeMarker,
282                 "CryptohomeMount-End",
283                 attempt,
284                 resolver));
285  cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
286      attempt->user_context.GetUserID(),
287      base::Bind(&TriggerResolveHash, attempt, resolver));
288}
289
290// Calls cryptohome's MountPublic method
291void MountPublic(AuthAttemptState* attempt,
292                 scoped_refptr<CryptohomeAuthenticator> resolver,
293                 int flags) {
294  cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic(
295      attempt->user_context.GetUserID(),
296      flags,
297      base::Bind(&TriggerResolveWithLoginTimeMarker,
298                 "CryptohomeMountPublic-End",
299                 attempt,
300                 resolver));
301  cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
302      attempt->user_context.GetUserID(),
303      base::Bind(&TriggerResolveHash, attempt, resolver));
304}
305
306// Calls cryptohome's key migration method.
307void Migrate(AuthAttemptState* attempt,
308             scoped_refptr<CryptohomeAuthenticator> resolver,
309             bool passing_old_hash,
310             const std::string& old_password,
311             const std::string& system_salt) {
312  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(
313      "CryptohomeMigrate-Start", false);
314  cryptohome::AsyncMethodCaller* caller =
315      cryptohome::AsyncMethodCaller::GetInstance();
316
317  // TODO(bartfab): Retrieve the hashing algorithm and salt to use for |old_key|
318  // from cryptohomed.
319  scoped_ptr<Key> old_key =
320      TransformKeyIfNeeded(Key(old_password), system_salt);
321  scoped_ptr<Key> new_key =
322      TransformKeyIfNeeded(*attempt->user_context.GetKey(), system_salt);
323  if (passing_old_hash) {
324    caller->AsyncMigrateKey(attempt->user_context.GetUserID(),
325                            old_key->GetSecret(),
326                            new_key->GetSecret(),
327                            base::Bind(&TriggerResolveWithLoginTimeMarker,
328                                       "CryptohomeMount-End",
329                                       attempt,
330                                       resolver));
331  } else {
332    caller->AsyncMigrateKey(attempt->user_context.GetUserID(),
333                            new_key->GetSecret(),
334                            old_key->GetSecret(),
335                            base::Bind(&TriggerResolveWithLoginTimeMarker,
336                                       "CryptohomeMount-End",
337                                       attempt,
338                                       resolver));
339  }
340}
341
342// Calls cryptohome's remove method.
343void Remove(AuthAttemptState* attempt,
344            scoped_refptr<CryptohomeAuthenticator> resolver) {
345  chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(
346      "CryptohomeRemove-Start", false);
347  cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove(
348      attempt->user_context.GetUserID(),
349      base::Bind(&TriggerResolveWithLoginTimeMarker,
350                 "CryptohomeRemove-End",
351                 attempt,
352                 resolver));
353}
354
355// Calls cryptohome's key check method.
356void CheckKey(AuthAttemptState* attempt,
357              scoped_refptr<CryptohomeAuthenticator> resolver,
358              const std::string& system_salt) {
359  scoped_ptr<Key> key =
360      TransformKeyIfNeeded(*attempt->user_context.GetKey(), system_salt);
361  cryptohome::AsyncMethodCaller::GetInstance()->AsyncCheckKey(
362      attempt->user_context.GetUserID(),
363      key->GetSecret(),
364      base::Bind(&TriggerResolve, attempt, resolver));
365}
366
367}  // namespace
368
369CryptohomeAuthenticator::CryptohomeAuthenticator(
370    scoped_refptr<base::TaskRunner> task_runner,
371    AuthStatusConsumer* consumer)
372    : Authenticator(consumer),
373      task_runner_(task_runner),
374      migrate_attempted_(false),
375      remove_attempted_(false),
376      resync_attempted_(false),
377      ephemeral_mount_attempted_(false),
378      check_key_attempted_(false),
379      already_reported_success_(false),
380      owner_is_verified_(false),
381      user_can_login_(false),
382      remove_user_data_on_failure_(false),
383      delayed_login_failure_(NULL) {
384}
385
386void CryptohomeAuthenticator::AuthenticateToLogin(
387    Profile* profile,
388    const UserContext& user_context) {
389  authentication_profile_ = profile;
390  current_state_.reset(new AuthAttemptState(user_context,
391                                            user_manager::USER_TYPE_REGULAR,
392                                            false,  // unlock
393                                            false,  // online_complete
394                                            !IsKnownUser(user_context)));
395  // Reset the verified flag.
396  owner_is_verified_ = false;
397
398  StartMount(current_state_.get(),
399             scoped_refptr<CryptohomeAuthenticator>(this),
400             false /* ephemeral */,
401             false /* create_if_nonexistent */);
402}
403
404void CryptohomeAuthenticator::CompleteLogin(Profile* profile,
405                                            const UserContext& user_context) {
406  authentication_profile_ = profile;
407  current_state_.reset(new AuthAttemptState(user_context,
408                                            user_manager::USER_TYPE_REGULAR,
409                                            true,   // unlock
410                                            false,  // online_complete
411                                            !IsKnownUser(user_context)));
412
413  // Reset the verified flag.
414  owner_is_verified_ = false;
415
416  StartMount(current_state_.get(),
417             scoped_refptr<CryptohomeAuthenticator>(this),
418             false /* ephemeral */,
419             false /* create_if_nonexistent */);
420
421  // For login completion from extension, we just need to resolve the current
422  // auth attempt state, the rest of OAuth related tasks will be done in
423  // parallel.
424  task_runner_->PostTask(
425      FROM_HERE,
426      base::Bind(&CryptohomeAuthenticator::ResolveLoginCompletionStatus, this));
427}
428
429void CryptohomeAuthenticator::AuthenticateToUnlock(
430    const UserContext& user_context) {
431  current_state_.reset(new AuthAttemptState(user_context,
432                                            user_manager::USER_TYPE_REGULAR,
433                                            true,     // unlock
434                                            true,     // online_complete
435                                            false));  // user_is_new
436  remove_user_data_on_failure_ = false;
437  check_key_attempted_ = true;
438  SystemSaltGetter::Get()->GetSystemSalt(
439      base::Bind(&CheckKey,
440                 current_state_.get(),
441                 scoped_refptr<CryptohomeAuthenticator>(this)));
442}
443
444void CryptohomeAuthenticator::LoginAsSupervisedUser(
445    const UserContext& user_context) {
446  DCHECK(task_runner_->RunsTasksOnCurrentThread());
447  // TODO(nkostylev): Pass proper value for |user_is_new| or remove (not used).
448  current_state_.reset(new AuthAttemptState(user_context,
449                                            user_manager::USER_TYPE_SUPERVISED,
450                                            false,    // unlock
451                                            false,    // online_complete
452                                            false));  // user_is_new
453  remove_user_data_on_failure_ = false;
454  StartMount(current_state_.get(),
455             scoped_refptr<CryptohomeAuthenticator>(this),
456             false /* ephemeral */,
457             false /* create_if_nonexistent */);
458}
459
460void CryptohomeAuthenticator::LoginRetailMode() {
461  DCHECK(task_runner_->RunsTasksOnCurrentThread());
462  // Note: |kRetailModeUserEMail| is used in other places to identify a retail
463  // mode session.
464  current_state_.reset(
465      new AuthAttemptState(UserContext(chromeos::login::kRetailModeUserName),
466                           user_manager::USER_TYPE_RETAIL_MODE,
467                           false,    // unlock
468                           false,    // online_complete
469                           false));  // user_is_new
470  remove_user_data_on_failure_ = false;
471  ephemeral_mount_attempted_ = true;
472  MountGuestAndGetHash(current_state_.get(),
473                       scoped_refptr<CryptohomeAuthenticator>(this));
474}
475
476void CryptohomeAuthenticator::LoginOffTheRecord() {
477  DCHECK(task_runner_->RunsTasksOnCurrentThread());
478  current_state_.reset(
479      new AuthAttemptState(UserContext(chromeos::login::kGuestUserName),
480                           user_manager::USER_TYPE_GUEST,
481                           false,    // unlock
482                           false,    // online_complete
483                           false));  // user_is_new
484  remove_user_data_on_failure_ = false;
485  ephemeral_mount_attempted_ = true;
486  MountGuestAndGetHash(current_state_.get(),
487                       scoped_refptr<CryptohomeAuthenticator>(this));
488}
489
490void CryptohomeAuthenticator::LoginAsPublicSession(
491    const UserContext& user_context) {
492  DCHECK(task_runner_->RunsTasksOnCurrentThread());
493  current_state_.reset(
494      new AuthAttemptState(user_context,
495                           user_manager::USER_TYPE_PUBLIC_ACCOUNT,
496                           false,    // unlock
497                           false,    // online_complete
498                           false));  // user_is_new
499  remove_user_data_on_failure_ = false;
500  ephemeral_mount_attempted_ = true;
501  StartMount(current_state_.get(),
502             scoped_refptr<CryptohomeAuthenticator>(this),
503             true /* ephemeral */,
504             true /* create_if_nonexistent */);
505}
506
507void CryptohomeAuthenticator::LoginAsKioskAccount(
508    const std::string& app_user_id,
509    bool use_guest_mount) {
510  DCHECK(task_runner_->RunsTasksOnCurrentThread());
511
512  const std::string user_id =
513      use_guest_mount ? chromeos::login::kGuestUserName : app_user_id;
514  current_state_.reset(new AuthAttemptState(UserContext(user_id),
515                                            user_manager::USER_TYPE_KIOSK_APP,
516                                            false,    // unlock
517                                            false,    // online_complete
518                                            false));  // user_is_new
519
520  remove_user_data_on_failure_ = true;
521  if (!use_guest_mount) {
522    MountPublic(current_state_.get(),
523                scoped_refptr<CryptohomeAuthenticator>(this),
524                cryptohome::CREATE_IF_MISSING);
525  } else {
526    ephemeral_mount_attempted_ = true;
527    MountGuestAndGetHash(current_state_.get(),
528                         scoped_refptr<CryptohomeAuthenticator>(this));
529  }
530}
531
532void CryptohomeAuthenticator::OnRetailModeAuthSuccess() {
533  DCHECK(task_runner_->RunsTasksOnCurrentThread());
534  VLOG(1) << "Retail mode login success";
535  chromeos::LoginEventRecorder::Get()->RecordAuthenticationSuccess();
536  if (consumer_)
537    consumer_->OnRetailModeAuthSuccess(current_state_->user_context);
538}
539
540void CryptohomeAuthenticator::OnAuthSuccess() {
541  DCHECK(task_runner_->RunsTasksOnCurrentThread());
542  VLOG(1) << "Login success";
543  // Send notification of success
544  chromeos::LoginEventRecorder::Get()->RecordAuthenticationSuccess();
545  {
546    base::AutoLock for_this_block(success_lock_);
547    already_reported_success_ = true;
548  }
549  if (consumer_)
550    consumer_->OnAuthSuccess(current_state_->user_context);
551}
552
553void CryptohomeAuthenticator::OnOffTheRecordAuthSuccess() {
554  DCHECK(task_runner_->RunsTasksOnCurrentThread());
555  chromeos::LoginEventRecorder::Get()->RecordAuthenticationSuccess();
556  if (consumer_)
557    consumer_->OnOffTheRecordAuthSuccess();
558}
559
560void CryptohomeAuthenticator::OnPasswordChangeDetected() {
561  DCHECK(task_runner_->RunsTasksOnCurrentThread());
562  if (consumer_)
563    consumer_->OnPasswordChangeDetected();
564}
565
566void CryptohomeAuthenticator::OnAuthFailure(const AuthFailure& error) {
567  DCHECK(task_runner_->RunsTasksOnCurrentThread());
568
569  // OnAuthFailure will be called again with the same |error|
570  // after the cryptohome has been removed.
571  if (remove_user_data_on_failure_) {
572    delayed_login_failure_ = &error;
573    RemoveEncryptedData();
574    return;
575  }
576  chromeos::LoginEventRecorder::Get()->RecordAuthenticationFailure();
577  LOG(WARNING) << "Login failed: " << error.GetErrorString();
578  if (consumer_)
579    consumer_->OnAuthFailure(error);
580}
581
582void CryptohomeAuthenticator::RecoverEncryptedData(
583    const std::string& old_password) {
584  migrate_attempted_ = true;
585  current_state_->ResetCryptohomeStatus();
586  SystemSaltGetter::Get()->GetSystemSalt(
587      base::Bind(&Migrate,
588                 current_state_.get(),
589                 scoped_refptr<CryptohomeAuthenticator>(this),
590                 true,
591                 old_password));
592}
593
594void CryptohomeAuthenticator::RemoveEncryptedData() {
595  remove_attempted_ = true;
596  current_state_->ResetCryptohomeStatus();
597  task_runner_->PostTask(
598      FROM_HERE,
599      base::Bind(&Remove,
600                 current_state_.get(),
601                 scoped_refptr<CryptohomeAuthenticator>(this)));
602}
603
604void CryptohomeAuthenticator::ResyncEncryptedData() {
605  resync_attempted_ = true;
606  current_state_->ResetCryptohomeStatus();
607  task_runner_->PostTask(
608      FROM_HERE,
609      base::Bind(&Remove,
610                 current_state_.get(),
611                 scoped_refptr<CryptohomeAuthenticator>(this)));
612}
613
614bool CryptohomeAuthenticator::VerifyOwner() {
615  if (owner_is_verified_)
616    return true;
617  // Check if policy data is fine and continue in safe mode if needed.
618  if (!IsSafeMode()) {
619    // Now we can continue with the login and report mount success.
620    user_can_login_ = true;
621    owner_is_verified_ = true;
622    return true;
623  }
624
625  CheckSafeModeOwnership(
626      current_state_->user_context,
627      base::Bind(&CryptohomeAuthenticator::OnOwnershipChecked, this));
628  return false;
629}
630
631void CryptohomeAuthenticator::OnOwnershipChecked(bool is_owner) {
632  // Now we can check if this user is the owner.
633  user_can_login_ = is_owner;
634  owner_is_verified_ = true;
635  Resolve();
636}
637
638void CryptohomeAuthenticator::Resolve() {
639  DCHECK(task_runner_->RunsTasksOnCurrentThread());
640  bool create_if_nonexistent = false;
641  CryptohomeAuthenticator::AuthState state = ResolveState();
642  VLOG(1) << "Resolved state to: " << state;
643  switch (state) {
644    case CONTINUE:
645    case POSSIBLE_PW_CHANGE:
646    case NO_MOUNT:
647      // These are intermediate states; we need more info from a request that
648      // is still pending.
649      break;
650    case FAILED_MOUNT:
651      // In this case, whether login succeeded or not, we can't log
652      // the user in because their data is horked.  So, override with
653      // the appropriate failure.
654      task_runner_->PostTask(
655          FROM_HERE,
656          base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
657                     this,
658                     AuthFailure(AuthFailure::COULD_NOT_MOUNT_CRYPTOHOME)));
659      break;
660    case FAILED_REMOVE:
661      // In this case, we tried to remove the user's old cryptohome at her
662      // request, and the remove failed.
663      remove_user_data_on_failure_ = false;
664      task_runner_->PostTask(
665          FROM_HERE,
666          base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
667                     this,
668                     AuthFailure(AuthFailure::DATA_REMOVAL_FAILED)));
669      break;
670    case FAILED_TMPFS:
671      // In this case, we tried to mount a tmpfs for guest and failed.
672      task_runner_->PostTask(
673          FROM_HERE,
674          base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
675                     this,
676                     AuthFailure(AuthFailure::COULD_NOT_MOUNT_TMPFS)));
677      break;
678    case FAILED_TPM:
679      // In this case, we tried to create/mount cryptohome and failed
680      // because of the critical TPM error.
681      // Chrome will notify user and request reboot.
682      task_runner_->PostTask(FROM_HERE,
683                             base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
684                                        this,
685                                        AuthFailure(AuthFailure::TPM_ERROR)));
686      break;
687    case FAILED_USERNAME_HASH:
688      // In this case, we failed the GetSanitizedUsername request to
689      // cryptohomed. This can happen for any login attempt.
690      task_runner_->PostTask(
691          FROM_HERE,
692          base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
693                     this,
694                     AuthFailure(AuthFailure::USERNAME_HASH_FAILED)));
695      break;
696    case REMOVED_DATA_AFTER_FAILURE:
697      remove_user_data_on_failure_ = false;
698      task_runner_->PostTask(FROM_HERE,
699                             base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
700                                        this,
701                                        *delayed_login_failure_));
702      break;
703    case CREATE_NEW:
704      create_if_nonexistent = true;
705    case RECOVER_MOUNT:
706      current_state_->ResetCryptohomeStatus();
707      StartMount(current_state_.get(),
708                 scoped_refptr<CryptohomeAuthenticator>(this),
709                 false /*ephemeral*/,
710                 create_if_nonexistent);
711      break;
712    case NEED_OLD_PW:
713      task_runner_->PostTask(
714          FROM_HERE,
715          base::Bind(&CryptohomeAuthenticator::OnPasswordChangeDetected, this));
716      break;
717    case ONLINE_FAILED:
718    case NEED_NEW_PW:
719    case HAVE_NEW_PW:
720      NOTREACHED() << "Using obsolete ClientLogin code path.";
721      break;
722    case OFFLINE_LOGIN:
723      VLOG(2) << "Offline login";
724    // Fall through.
725    case UNLOCK:
726      VLOG(2) << "Unlock";
727    // Fall through.
728    case ONLINE_LOGIN:
729      VLOG(2) << "Online login";
730      task_runner_->PostTask(
731          FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthSuccess, this));
732      break;
733    case DEMO_LOGIN:
734      VLOG(2) << "Retail mode login";
735      current_state_->user_context.SetIsUsingOAuth(false);
736      task_runner_->PostTask(
737          FROM_HERE,
738          base::Bind(&CryptohomeAuthenticator::OnRetailModeAuthSuccess, this));
739      break;
740    case GUEST_LOGIN:
741      task_runner_->PostTask(
742          FROM_HERE,
743          base::Bind(&CryptohomeAuthenticator::OnOffTheRecordAuthSuccess,
744                     this));
745      break;
746    case KIOSK_ACCOUNT_LOGIN:
747    case PUBLIC_ACCOUNT_LOGIN:
748      current_state_->user_context.SetIsUsingOAuth(false);
749      task_runner_->PostTask(
750          FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthSuccess, this));
751      break;
752    case SUPERVISED_USER_LOGIN:
753      current_state_->user_context.SetIsUsingOAuth(false);
754      task_runner_->PostTask(
755          FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthSuccess, this));
756      break;
757    case LOGIN_FAILED:
758      current_state_->ResetCryptohomeStatus();
759      task_runner_->PostTask(FROM_HERE,
760                             base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
761                                        this,
762                                        current_state_->online_outcome()));
763      break;
764    case OWNER_REQUIRED: {
765      current_state_->ResetCryptohomeStatus();
766      bool success = false;
767      DBusThreadManager::Get()->GetCryptohomeClient()->Unmount(&success);
768      if (!success) {
769        // Maybe we should reboot immediately here?
770        LOG(ERROR) << "Couldn't unmount users home!";
771      }
772      task_runner_->PostTask(
773          FROM_HERE,
774          base::Bind(&CryptohomeAuthenticator::OnAuthFailure,
775                     this,
776                     AuthFailure(AuthFailure::OWNER_REQUIRED)));
777      break;
778    }
779    default:
780      NOTREACHED();
781      break;
782  }
783}
784
785CryptohomeAuthenticator::~CryptohomeAuthenticator() {
786}
787
788CryptohomeAuthenticator::AuthState CryptohomeAuthenticator::ResolveState() {
789  DCHECK(task_runner_->RunsTasksOnCurrentThread());
790  // If we haven't mounted the user's home dir yet or
791  // haven't got sanitized username value, we can't be done.
792  // We never get past here if any of these two cryptohome ops is still pending.
793  // This is an important invariant.
794  if (!current_state_->cryptohome_complete() ||
795      !current_state_->username_hash_obtained()) {
796    return CONTINUE;
797  }
798
799  AuthState state = CONTINUE;
800
801  if (current_state_->cryptohome_outcome() &&
802      current_state_->username_hash_valid()) {
803    state = ResolveCryptohomeSuccessState();
804  } else {
805    state = ResolveCryptohomeFailureState();
806  }
807
808  DCHECK(current_state_->cryptohome_complete());  // Ensure invariant holds.
809  migrate_attempted_ = false;
810  remove_attempted_ = false;
811  resync_attempted_ = false;
812  ephemeral_mount_attempted_ = false;
813  check_key_attempted_ = false;
814
815  if (state != POSSIBLE_PW_CHANGE && state != NO_MOUNT &&
816      state != OFFLINE_LOGIN)
817    return state;
818
819  if (current_state_->online_complete()) {
820    if (current_state_->online_outcome().reason() == AuthFailure::NONE) {
821      // Online attempt succeeded as well, so combine the results.
822      return ResolveOnlineSuccessState(state);
823    }
824    NOTREACHED() << "Using obsolete ClientLogin code path.";
825  }
826  // if online isn't complete yet, just return the offline result.
827  return state;
828}
829
830CryptohomeAuthenticator::AuthState
831CryptohomeAuthenticator::ResolveCryptohomeFailureState() {
832  DCHECK(task_runner_->RunsTasksOnCurrentThread());
833  if (remove_attempted_ || resync_attempted_)
834    return FAILED_REMOVE;
835  if (ephemeral_mount_attempted_)
836    return FAILED_TMPFS;
837  if (migrate_attempted_)
838    return NEED_OLD_PW;
839  if (check_key_attempted_)
840    return LOGIN_FAILED;
841
842  if (current_state_->cryptohome_code() ==
843      cryptohome::MOUNT_ERROR_TPM_NEEDS_REBOOT) {
844    // Critical TPM error detected, reboot needed.
845    return FAILED_TPM;
846  }
847
848  // Return intermediate states in the following case:
849  // when there is an online result to use;
850  // This is the case after user finishes Gaia login;
851  if (current_state_->online_complete()) {
852    if (current_state_->cryptohome_code() ==
853        cryptohome::MOUNT_ERROR_KEY_FAILURE) {
854      // If we tried a mount but they used the wrong key, we may need to
855      // ask the user for her old password.  We'll only know once we've
856      // done the online check.
857      return POSSIBLE_PW_CHANGE;
858    }
859    if (current_state_->cryptohome_code() ==
860        cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST) {
861      // If we tried a mount but the user did not exist, then we should wait
862      // for online login to succeed and try again with the "create" flag set.
863      return NO_MOUNT;
864    }
865  }
866
867  if (!current_state_->username_hash_valid())
868    return FAILED_USERNAME_HASH;
869
870  return FAILED_MOUNT;
871}
872
873CryptohomeAuthenticator::AuthState
874CryptohomeAuthenticator::ResolveCryptohomeSuccessState() {
875  DCHECK(task_runner_->RunsTasksOnCurrentThread());
876  if (resync_attempted_)
877    return CREATE_NEW;
878  if (remove_attempted_)
879    return REMOVED_DATA_AFTER_FAILURE;
880  if (migrate_attempted_)
881    return RECOVER_MOUNT;
882  if (check_key_attempted_)
883    return UNLOCK;
884
885  if (current_state_->user_type == user_manager::USER_TYPE_GUEST)
886    return GUEST_LOGIN;
887  if (current_state_->user_type == user_manager::USER_TYPE_RETAIL_MODE)
888    return DEMO_LOGIN;
889  if (current_state_->user_type == user_manager::USER_TYPE_PUBLIC_ACCOUNT)
890    return PUBLIC_ACCOUNT_LOGIN;
891  if (current_state_->user_type == user_manager::USER_TYPE_KIOSK_APP)
892    return KIOSK_ACCOUNT_LOGIN;
893  if (current_state_->user_type == user_manager::USER_TYPE_SUPERVISED)
894    return SUPERVISED_USER_LOGIN;
895
896  if (!VerifyOwner())
897    return CONTINUE;
898  return user_can_login_ ? OFFLINE_LOGIN : OWNER_REQUIRED;
899}
900
901CryptohomeAuthenticator::AuthState
902CryptohomeAuthenticator::ResolveOnlineSuccessState(
903    CryptohomeAuthenticator::AuthState offline_state) {
904  DCHECK(task_runner_->RunsTasksOnCurrentThread());
905  switch (offline_state) {
906    case POSSIBLE_PW_CHANGE:
907      return NEED_OLD_PW;
908    case NO_MOUNT:
909      return CREATE_NEW;
910    case OFFLINE_LOGIN:
911      return ONLINE_LOGIN;
912    default:
913      NOTREACHED();
914      return offline_state;
915  }
916}
917
918void CryptohomeAuthenticator::ResolveLoginCompletionStatus() {
919  // Shortcut online state resolution process.
920  current_state_->RecordOnlineLoginStatus(AuthFailure::AuthFailureNone());
921  Resolve();
922}
923
924void CryptohomeAuthenticator::SetOwnerState(bool owner_check_finished,
925                                            bool check_result) {
926  owner_is_verified_ = owner_check_finished;
927  user_can_login_ = check_result;
928}
929
930}  // namespace chromeos
931