// Copyright (c) 2012 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/parallel_authenticator.h" #include "base/bind.h" #include "base/command_line.h" #include "base/files/file_path.h" #include "base/logging.h" #include "base/strings/string_number_conversions.h" #include "base/strings/string_util.h" #include "chrome/browser/chrome_notification_types.h" #include "chrome/browser/chromeos/boot_times_loader.h" #include "chrome/browser/chromeos/login/authentication_notification_details.h" #include "chrome/browser/chromeos/login/login_status_consumer.h" #include "chrome/browser/chromeos/login/user.h" #include "chrome/browser/chromeos/login/user_manager.h" #include "chrome/browser/chromeos/settings/cros_settings.h" #include "chrome/common/chrome_switches.h" #include "chromeos/cryptohome/async_method_caller.h" #include "chromeos/cryptohome/system_salt_getter.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/login/login_state.h" #include "content/public/browser/browser_thread.h" #include "content/public/browser/notification_service.h" #include "crypto/sha2.h" #include "google_apis/gaia/gaia_auth_util.h" #include "third_party/cros_system_api/dbus/service_constants.h" using content::BrowserThread; namespace chromeos { namespace { // Length of password hashed with SHA-256. const int kPasswordHashLength = 32; // Records status and calls resolver->Resolve(). void TriggerResolve(AuthAttemptState* attempt, scoped_refptr resolver, bool success, cryptohome::MountError return_code) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); attempt->RecordCryptohomeStatus(success, return_code); resolver->Resolve(); } // Records get hash status and calls resolver->Resolve(). void TriggerResolveHash(AuthAttemptState* attempt, scoped_refptr resolver, bool success, const std::string& username_hash) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (success) attempt->RecordUsernameHash(username_hash); else attempt->RecordUsernameHashFailed(); resolver->Resolve(); } // Calls TriggerResolve while adding login time marker. void TriggerResolveWithLoginTimeMarker( const std::string& marker_name, AuthAttemptState* attempt, scoped_refptr resolver, bool success, cryptohome::MountError return_code) { chromeos::BootTimesLoader::Get()->AddLoginTimeMarker(marker_name, false); TriggerResolve(attempt, resolver, success, return_code); } // Calls cryptohome's mount method. void Mount(AuthAttemptState* attempt, scoped_refptr resolver, int flags, const std::string& system_salt) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( "CryptohomeMount-Start", false); // Set state that username_hash is requested here so that test implementation // that returns directly would not generate 2 OnLoginSucces() calls. attempt->UsernameHashRequested(); cryptohome::AsyncMethodCaller::GetInstance()->AsyncMount( attempt->user_context.username, ParallelAuthenticator::HashPassword(attempt->user_context.password, system_salt), flags, base::Bind(&TriggerResolveWithLoginTimeMarker, "CryptohomeMount-End", attempt, resolver)); cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( attempt->user_context.username, base::Bind(&TriggerResolveHash, attempt, resolver)); } // Calls cryptohome's mount method for guest. void MountGuest(AuthAttemptState* attempt, scoped_refptr resolver) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountGuest( base::Bind(&TriggerResolveWithLoginTimeMarker, "CryptohomeMount-End", attempt, resolver)); } // Calls cryptohome's MountPublic method void MountPublic(AuthAttemptState* attempt, scoped_refptr resolver, int flags) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic( attempt->user_context.username, flags, base::Bind(&TriggerResolveWithLoginTimeMarker, "CryptohomeMountPublic-End", attempt, resolver)); cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( attempt->user_context.username, base::Bind(&TriggerResolveHash, attempt, resolver)); } // Calls cryptohome's key migration method. void Migrate(AuthAttemptState* attempt, scoped_refptr resolver, bool passing_old_hash, const std::string& old_password, const std::string& system_salt) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( "CryptohomeMigrate-Start", false); cryptohome::AsyncMethodCaller* caller = cryptohome::AsyncMethodCaller::GetInstance(); if (passing_old_hash) { caller->AsyncMigrateKey( attempt->user_context.username, ParallelAuthenticator::HashPassword(old_password, system_salt), ParallelAuthenticator::HashPassword(attempt->user_context.password, system_salt), base::Bind(&TriggerResolveWithLoginTimeMarker, "CryptohomeMount-End", attempt, resolver)); } else { caller->AsyncMigrateKey( attempt->user_context.username, ParallelAuthenticator::HashPassword(attempt->user_context.password, system_salt), ParallelAuthenticator::HashPassword(old_password, system_salt), base::Bind(&TriggerResolveWithLoginTimeMarker, "CryptohomeMount-End", attempt, resolver)); } } // Calls cryptohome's remove method. void Remove(AuthAttemptState* attempt, scoped_refptr resolver) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( "CryptohomeRemove-Start", false); cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove( attempt->user_context.username, base::Bind(&TriggerResolveWithLoginTimeMarker, "CryptohomeRemove-End", attempt, resolver)); } // Calls cryptohome's key check method. void CheckKey(AuthAttemptState* attempt, scoped_refptr resolver, const std::string& system_salt) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); cryptohome::AsyncMethodCaller::GetInstance()->AsyncCheckKey( attempt->user_context.username, ParallelAuthenticator::HashPassword(attempt->user_context.password, system_salt), base::Bind(&TriggerResolve, attempt, resolver)); } } // namespace ParallelAuthenticator::ParallelAuthenticator(LoginStatusConsumer* consumer) : Authenticator(consumer), migrate_attempted_(false), remove_attempted_(false), resync_attempted_(false), ephemeral_mount_attempted_(false), check_key_attempted_(false), already_reported_success_(false), owner_is_verified_(false), user_can_login_(false), remove_user_data_on_failure_(false), delayed_login_failure_(NULL) { } void ParallelAuthenticator::AuthenticateToLogin( Profile* profile, const UserContext& user_context) { std::string canonicalized = gaia::CanonicalizeEmail(user_context.username); authentication_profile_ = profile; current_state_.reset( new AuthAttemptState( UserContext(canonicalized, user_context.password, user_context.auth_code), std::string(), // login_token, not used. std::string(), // login_captcha, not used. User::USER_TYPE_REGULAR, !UserManager::Get()->IsKnownUser(canonicalized))); // Reset the verified flag. owner_is_verified_ = false; SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr(this), cryptohome::MOUNT_FLAGS_NONE)); } void ParallelAuthenticator::CompleteLogin(Profile* profile, const UserContext& user_context) { std::string canonicalized = gaia::CanonicalizeEmail(user_context.username); authentication_profile_ = profile; current_state_.reset( new AuthAttemptState( UserContext(canonicalized, user_context.password, user_context.auth_code), !UserManager::Get()->IsKnownUser(canonicalized))); // Reset the verified flag. owner_is_verified_ = false; SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr(this), cryptohome::MOUNT_FLAGS_NONE)); // For login completion from extension, we just need to resolve the current // auth attempt state, the rest of OAuth related tasks will be done in // parallel. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::ResolveLoginCompletionStatus, this)); } void ParallelAuthenticator::AuthenticateToUnlock( const UserContext& user_context) { current_state_.reset( new AuthAttemptState( gaia::CanonicalizeEmail(user_context.username), user_context.password)); remove_user_data_on_failure_ = false; check_key_attempted_ = true; SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&CheckKey, current_state_.get(), scoped_refptr(this))); } void ParallelAuthenticator::LoginAsLocallyManagedUser( const UserContext& user_context) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // TODO(nkostylev): Pass proper value for |user_is_new| or remove (not used). current_state_.reset( new AuthAttemptState(user_context, "", // login_token "", // login_captcha User::USER_TYPE_LOCALLY_MANAGED, false)); remove_user_data_on_failure_ = false; SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr(this), cryptohome::MOUNT_FLAGS_NONE)); } void ParallelAuthenticator::LoginRetailMode() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Note: |kRetailModeUserEMail| is used in other places to identify a retail // mode session. current_state_.reset(new AuthAttemptState( UserContext(UserManager::kRetailModeUserName, std::string(), // password std::string()), // auth_code std::string(), // login_token std::string(), // login_captcha User::USER_TYPE_RETAIL_MODE, false)); remove_user_data_on_failure_ = false; ephemeral_mount_attempted_ = true; MountGuest(current_state_.get(), scoped_refptr(this)); } void ParallelAuthenticator::LoginOffTheRecord() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); current_state_.reset(new AuthAttemptState( UserContext(UserManager::kGuestUserName, // username std::string(), // password std::string()), // auth_code std::string(), // login_token std::string(), // login_captcha User::USER_TYPE_GUEST, false)); remove_user_data_on_failure_ = false; ephemeral_mount_attempted_ = true; MountGuest(current_state_.get(), scoped_refptr(this)); } void ParallelAuthenticator::LoginAsPublicAccount(const std::string& username) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); current_state_.reset(new AuthAttemptState( UserContext(username, std::string(), // password std::string()), // auth_code std::string(), // login_token std::string(), // login_captcha User::USER_TYPE_PUBLIC_ACCOUNT, false)); remove_user_data_on_failure_ = false; ephemeral_mount_attempted_ = true; SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr(this), cryptohome::CREATE_IF_MISSING | cryptohome::ENSURE_EPHEMERAL)); } void ParallelAuthenticator::LoginAsKioskAccount( const std::string& app_user_id) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); current_state_.reset(new AuthAttemptState( UserContext(app_user_id, std::string(), // password std::string()), // auth_code std::string(), // login_token std::string(), // login_captcha User::USER_TYPE_KIOSK_APP, false)); remove_user_data_on_failure_ = true; MountPublic(current_state_.get(), scoped_refptr(this), cryptohome::CREATE_IF_MISSING); } void ParallelAuthenticator::OnRetailModeLoginSuccess() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); VLOG(1) << "Retail mode login success"; // Send notification of success AuthenticationNotificationDetails details(true); content::NotificationService::current()->Notify( chrome::NOTIFICATION_LOGIN_AUTHENTICATION, content::NotificationService::AllSources(), content::Details(&details)); if (consumer_) consumer_->OnRetailModeLoginSuccess(current_state_->user_context); } void ParallelAuthenticator::OnLoginSuccess() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); VLOG(1) << "Login success"; // Send notification of success AuthenticationNotificationDetails details(true); content::NotificationService::current()->Notify( chrome::NOTIFICATION_LOGIN_AUTHENTICATION, content::NotificationService::AllSources(), content::Details(&details)); { base::AutoLock for_this_block(success_lock_); already_reported_success_ = true; } if (consumer_) consumer_->OnLoginSuccess(current_state_->user_context); } void ParallelAuthenticator::OnOffTheRecordLoginSuccess() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // Send notification of success AuthenticationNotificationDetails details(true); content::NotificationService::current()->Notify( chrome::NOTIFICATION_LOGIN_AUTHENTICATION, content::NotificationService::AllSources(), content::Details(&details)); if (consumer_) consumer_->OnOffTheRecordLoginSuccess(); } void ParallelAuthenticator::OnPasswordChangeDetected() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (consumer_) consumer_->OnPasswordChangeDetected(); } void ParallelAuthenticator::OnLoginFailure(const LoginFailure& error) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // OnLoginFailure will be called again with the same |error| // after the cryptohome has been removed. if (remove_user_data_on_failure_) { delayed_login_failure_ = &error; RemoveEncryptedData(); return; } // Send notification of failure AuthenticationNotificationDetails details(false); content::NotificationService::current()->Notify( chrome::NOTIFICATION_LOGIN_AUTHENTICATION, content::NotificationService::AllSources(), content::Details(&details)); LOG(WARNING) << "Login failed: " << error.GetErrorString(); if (consumer_) consumer_->OnLoginFailure(error); } void ParallelAuthenticator::RecoverEncryptedData( const std::string& old_password) { migrate_attempted_ = true; current_state_->ResetCryptohomeStatus(); SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Migrate, current_state_.get(), scoped_refptr(this), true, old_password)); } void ParallelAuthenticator::RemoveEncryptedData() { remove_attempted_ = true; current_state_->ResetCryptohomeStatus(); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&Remove, current_state_.get(), scoped_refptr(this))); } void ParallelAuthenticator::ResyncEncryptedData() { resync_attempted_ = true; current_state_->ResetCryptohomeStatus(); BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&Remove, current_state_.get(), scoped_refptr(this))); } bool ParallelAuthenticator::VerifyOwner() { if (owner_is_verified_) return true; // Check if policy data is fine and continue in safe mode if needed. bool is_safe_mode = false; CrosSettings::Get()->GetBoolean(kPolicyMissingMitigationMode, &is_safe_mode); if (!is_safe_mode) { // Now we can continue with the login and report mount success. user_can_login_ = true; owner_is_verified_ = true; return true; } // Now we can continue reading the private key. DeviceSettingsService::Get()->SetUsername( current_state_->user_context.username); // This should trigger certificate loading, which is needed in order to // correctly determine if the current user is the owner. if (LoginState::IsInitialized()) { LoginState::Get()->SetLoggedInState(LoginState::LOGGED_IN_SAFE_MODE, LoginState::LOGGED_IN_USER_NONE); } DeviceSettingsService::Get()->IsCurrentUserOwnerAsync( base::Bind(&ParallelAuthenticator::OnOwnershipChecked, this)); return false; } void ParallelAuthenticator::OnOwnershipChecked(bool is_owner) { // Now we can check if this user is the owner. user_can_login_ = is_owner; owner_is_verified_ = true; Resolve(); } void ParallelAuthenticator::Resolve() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); int mount_flags = cryptohome::MOUNT_FLAGS_NONE; ParallelAuthenticator::AuthState state = ResolveState(); VLOG(1) << "Resolved state to: " << state; switch (state) { case CONTINUE: case POSSIBLE_PW_CHANGE: case NO_MOUNT: // These are intermediate states; we need more info from a request that // is still pending. break; case FAILED_MOUNT: // In this case, whether login succeeded or not, we can't log // the user in because their data is horked. So, override with // the appropriate failure. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnLoginFailure, this, LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME))); break; case FAILED_REMOVE: // In this case, we tried to remove the user's old cryptohome at her // request, and the remove failed. remove_user_data_on_failure_ = false; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnLoginFailure, this, LoginFailure(LoginFailure::DATA_REMOVAL_FAILED))); break; case FAILED_TMPFS: // In this case, we tried to mount a tmpfs for guest and failed. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnLoginFailure, this, LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS))); break; case FAILED_TPM: // In this case, we tried to create/mount cryptohome and failed // because of the critical TPM error. // Chrome will notify user and request reboot. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnLoginFailure, this, LoginFailure(LoginFailure::TPM_ERROR))); break; case FAILED_USERNAME_HASH: // In this case, we failed the GetSanitizedUsername request to // cryptohomed. This can happen for any login attempt. BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnLoginFailure, this, LoginFailure(LoginFailure::USERNAME_HASH_FAILED))); break; case REMOVED_DATA_AFTER_FAILURE: remove_user_data_on_failure_ = false; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnLoginFailure, this, *delayed_login_failure_)); break; case CREATE_NEW: mount_flags |= cryptohome::CREATE_IF_MISSING; case RECOVER_MOUNT: current_state_->ResetCryptohomeStatus(); SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Mount, current_state_.get(), scoped_refptr(this), mount_flags)); break; case NEED_OLD_PW: BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnPasswordChangeDetected, this)); break; case ONLINE_FAILED: case NEED_NEW_PW: case HAVE_NEW_PW: NOTREACHED() << "Using obsolete ClientLogin code path."; break; case OFFLINE_LOGIN: VLOG(2) << "Offline login"; // Fall through. case UNLOCK: VLOG(2) << "Unlock"; // Fall through. case ONLINE_LOGIN: VLOG(2) << "Online login"; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnLoginSuccess, this)); break; case DEMO_LOGIN: VLOG(2) << "Retail mode login"; current_state_->user_context.using_oauth = false; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnRetailModeLoginSuccess, this)); break; case GUEST_LOGIN: BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnOffTheRecordLoginSuccess, this)); break; case KIOSK_ACCOUNT_LOGIN: case PUBLIC_ACCOUNT_LOGIN: current_state_->user_context.using_oauth = false; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnLoginSuccess, this)); break; case LOCALLY_MANAGED_USER_LOGIN: current_state_->user_context.using_oauth = false; BrowserThread::PostTask( BrowserThread::UI, FROM_HERE, base::Bind(&ParallelAuthenticator::OnLoginSuccess, this)); break; case LOGIN_FAILED: current_state_->ResetCryptohomeStatus(); BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( &ParallelAuthenticator::OnLoginFailure, this, current_state_->online_outcome())); break; case OWNER_REQUIRED: { current_state_->ResetCryptohomeStatus(); bool success = false; DBusThreadManager::Get()->GetCryptohomeClient()->Unmount(&success); if (!success) { // Maybe we should reboot immediately here? LOG(ERROR) << "Couldn't unmount users home!"; } BrowserThread::PostTask(BrowserThread::UI, FROM_HERE, base::Bind( &ParallelAuthenticator::OnLoginFailure, this, LoginFailure(LoginFailure::OWNER_REQUIRED))); break; } default: NOTREACHED(); break; } } // static. std::string ParallelAuthenticator::HashPassword(const std::string& password, const std::string& ascii_salt) { // Update sha with ascii encoded salt, then update with ascii of password, // then end. // TODO(stevenjb/nkostylev): Handle empty system salt gracefully. CHECK(!ascii_salt.empty()); char passhash_buf[kPasswordHashLength]; // Hash salt and password crypto::SHA256HashString(ascii_salt + password, &passhash_buf, sizeof(passhash_buf)); // Only want the top half for 'weak' hashing so that the passphrase is not // immediately exposed even if the output is reversed. const int encoded_length = sizeof(passhash_buf) / 2; return StringToLowerASCII(base::HexEncode( reinterpret_cast(passhash_buf), encoded_length)); } ParallelAuthenticator::~ParallelAuthenticator() {} ParallelAuthenticator::AuthState ParallelAuthenticator::ResolveState() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); // If we haven't mounted the user's home dir yet or // haven't got sanitized username value, we can't be done. // We never get past here if any of these two cryptohome ops is still pending. // This is an important invariant. if (!current_state_->cryptohome_complete() || !current_state_->username_hash_obtained()) { return CONTINUE; } AuthState state = CONTINUE; if (current_state_->cryptohome_outcome() && current_state_->username_hash_valid()) { state = ResolveCryptohomeSuccessState(); } else { state = ResolveCryptohomeFailureState(); } DCHECK(current_state_->cryptohome_complete()); // Ensure invariant holds. migrate_attempted_ = false; remove_attempted_ = false; resync_attempted_ = false; ephemeral_mount_attempted_ = false; check_key_attempted_ = false; if (state != POSSIBLE_PW_CHANGE && state != NO_MOUNT && state != OFFLINE_LOGIN) return state; if (current_state_->online_complete()) { if (current_state_->online_outcome().reason() == LoginFailure::NONE) { // Online attempt succeeded as well, so combine the results. return ResolveOnlineSuccessState(state); } NOTREACHED() << "Using obsolete ClientLogin code path."; } // if online isn't complete yet, just return the offline result. return state; } ParallelAuthenticator::AuthState ParallelAuthenticator::ResolveCryptohomeFailureState() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (remove_attempted_ || resync_attempted_) return FAILED_REMOVE; if (ephemeral_mount_attempted_) return FAILED_TMPFS; if (migrate_attempted_) return NEED_OLD_PW; if (check_key_attempted_) return LOGIN_FAILED; if (current_state_->cryptohome_code() == cryptohome::MOUNT_ERROR_TPM_NEEDS_REBOOT) { // Critical TPM error detected, reboot needed. return FAILED_TPM; } // Return intermediate states in the following case: // when there is an online result to use; // This is the case after user finishes Gaia login; if (current_state_->online_complete()) { if (current_state_->cryptohome_code() == cryptohome::MOUNT_ERROR_KEY_FAILURE) { // If we tried a mount but they used the wrong key, we may need to // ask the user for her old password. We'll only know once we've // done the online check. return POSSIBLE_PW_CHANGE; } if (current_state_->cryptohome_code() == cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST) { // If we tried a mount but the user did not exist, then we should wait // for online login to succeed and try again with the "create" flag set. return NO_MOUNT; } } if (!current_state_->username_hash_valid()) return FAILED_USERNAME_HASH; return FAILED_MOUNT; } ParallelAuthenticator::AuthState ParallelAuthenticator::ResolveCryptohomeSuccessState() { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); if (resync_attempted_) return CREATE_NEW; if (remove_attempted_) return REMOVED_DATA_AFTER_FAILURE; if (migrate_attempted_) return RECOVER_MOUNT; if (check_key_attempted_) return UNLOCK; if (current_state_->user_type == User::USER_TYPE_GUEST) return GUEST_LOGIN; if (current_state_->user_type == User::USER_TYPE_RETAIL_MODE) return DEMO_LOGIN; if (current_state_->user_type == User::USER_TYPE_PUBLIC_ACCOUNT) return PUBLIC_ACCOUNT_LOGIN; if (current_state_->user_type == User::USER_TYPE_KIOSK_APP) return KIOSK_ACCOUNT_LOGIN; if (current_state_->user_type == User::USER_TYPE_LOCALLY_MANAGED) return LOCALLY_MANAGED_USER_LOGIN; if (!VerifyOwner()) return CONTINUE; return user_can_login_ ? OFFLINE_LOGIN : OWNER_REQUIRED; } ParallelAuthenticator::AuthState ParallelAuthenticator::ResolveOnlineSuccessState( ParallelAuthenticator::AuthState offline_state) { DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); switch (offline_state) { case POSSIBLE_PW_CHANGE: return NEED_OLD_PW; case NO_MOUNT: return CREATE_NEW; case OFFLINE_LOGIN: return ONLINE_LOGIN; default: NOTREACHED(); return offline_state; } } void ParallelAuthenticator::ResolveLoginCompletionStatus() { // Shortcut online state resolution process. current_state_->RecordOnlineLoginStatus(LoginFailure::LoginFailureNone()); Resolve(); } void ParallelAuthenticator::SetOwnerState(bool owner_check_finished, bool check_result) { owner_is_verified_ = owner_check_finished; user_can_login_ = check_result; } } // namespace chromeos