| // Copyright 2014 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/auth/parallel_authenticator.h" |
| |
| #include "base/bind.h" |
| #include "base/command_line.h" |
| #include "base/files/file_path.h" |
| #include "base/logging.h" |
| #include "chrome/browser/chrome_notification_types.h" |
| #include "chrome/browser/chromeos/boot_times_loader.h" |
| #include "chrome/browser/chromeos/login/auth/authentication_notification_details.h" |
| #include "chrome/browser/chromeos/login/auth/key.h" |
| #include "chrome/browser/chromeos/login/auth/login_status_consumer.h" |
| #include "chrome/browser/chromeos/login/auth/user_context.h" |
| #include "chrome/browser/chromeos/login/users/user.h" |
| #include "chrome/browser/chromeos/login/users/user_manager.h" |
| #include "chrome/browser/chromeos/ownership/owner_settings_service.h" |
| #include "chrome/browser/chromeos/ownership/owner_settings_service_factory.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 "third_party/cros_system_api/dbus/service_constants.h" |
| |
| using content::BrowserThread; |
| |
| namespace chromeos { |
| |
| namespace { |
| |
| // Hashes |key| with |system_salt| if it its type is KEY_TYPE_PASSWORD_PLAIN. |
| // Returns the keys unmodified otherwise. |
| scoped_ptr<Key> TransformKeyIfNeeded(const Key& key, |
| const std::string& system_salt) { |
| scoped_ptr<Key> result(new Key(key)); |
| if (result->GetKeyType() == Key::KEY_TYPE_PASSWORD_PLAIN) |
| result->Transform(Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt); |
| |
| return result.Pass(); |
| } |
| |
| // Records status and calls resolver->Resolve(). |
| void TriggerResolve(AuthAttemptState* attempt, |
| scoped_refptr<ParallelAuthenticator> 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<ParallelAuthenticator> 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<ParallelAuthenticator> 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<ParallelAuthenticator> 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(); |
| |
| scoped_ptr<Key> key = |
| TransformKeyIfNeeded(*attempt->user_context.GetKey(), system_salt); |
| cryptohome::AsyncMethodCaller::GetInstance()->AsyncMount( |
| attempt->user_context.GetUserID(), |
| key->GetSecret(), |
| flags, |
| base::Bind(&TriggerResolveWithLoginTimeMarker, |
| "CryptohomeMount-End", |
| attempt, |
| resolver)); |
| cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( |
| attempt->user_context.GetUserID(), |
| base::Bind(&TriggerResolveHash, |
| attempt, |
| resolver)); |
| } |
| |
| // Calls cryptohome's mount method for guest. |
| void MountGuest(AuthAttemptState* attempt, |
| scoped_refptr<ParallelAuthenticator> resolver) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountGuest( |
| base::Bind(&TriggerResolveWithLoginTimeMarker, |
| "CryptohomeMount-End", |
| attempt, |
| resolver)); |
| } |
| |
| // Calls cryptohome's mount method for guest and also get the user hash from |
| // cryptohome. |
| void MountGuestAndGetHash(AuthAttemptState* attempt, |
| scoped_refptr<ParallelAuthenticator> resolver) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| attempt->UsernameHashRequested(); |
| cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountGuest( |
| base::Bind(&TriggerResolveWithLoginTimeMarker, |
| "CryptohomeMount-End", |
| attempt, |
| resolver)); |
| cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( |
| attempt->user_context.GetUserID(), |
| base::Bind(&TriggerResolveHash, |
| attempt, |
| resolver)); |
| } |
| |
| // Calls cryptohome's MountPublic method |
| void MountPublic(AuthAttemptState* attempt, |
| scoped_refptr<ParallelAuthenticator> resolver, |
| int flags) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| cryptohome::AsyncMethodCaller::GetInstance()->AsyncMountPublic( |
| attempt->user_context.GetUserID(), |
| flags, |
| base::Bind(&TriggerResolveWithLoginTimeMarker, |
| "CryptohomeMountPublic-End", |
| attempt, |
| resolver)); |
| cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername( |
| attempt->user_context.GetUserID(), |
| base::Bind(&TriggerResolveHash, |
| attempt, |
| resolver)); |
| } |
| |
| // Calls cryptohome's key migration method. |
| void Migrate(AuthAttemptState* attempt, |
| scoped_refptr<ParallelAuthenticator> 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(); |
| |
| // TODO(bartfab): Retrieve the hashing algorithm and salt to use for |old_key| |
| // from cryptohomed. |
| scoped_ptr<Key> old_key = |
| TransformKeyIfNeeded(Key(old_password), system_salt); |
| scoped_ptr<Key> new_key = |
| TransformKeyIfNeeded(*attempt->user_context.GetKey(), system_salt); |
| if (passing_old_hash) { |
| caller->AsyncMigrateKey(attempt->user_context.GetUserID(), |
| old_key->GetSecret(), |
| new_key->GetSecret(), |
| base::Bind(&TriggerResolveWithLoginTimeMarker, |
| "CryptohomeMount-End", |
| attempt, |
| resolver)); |
| } else { |
| caller->AsyncMigrateKey(attempt->user_context.GetUserID(), |
| new_key->GetSecret(), |
| old_key->GetSecret(), |
| base::Bind(&TriggerResolveWithLoginTimeMarker, |
| "CryptohomeMount-End", |
| attempt, |
| resolver)); |
| } |
| } |
| |
| // Calls cryptohome's remove method. |
| void Remove(AuthAttemptState* attempt, |
| scoped_refptr<ParallelAuthenticator> resolver) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| chromeos::BootTimesLoader::Get()->AddLoginTimeMarker( |
| "CryptohomeRemove-Start", false); |
| cryptohome::AsyncMethodCaller::GetInstance()->AsyncRemove( |
| attempt->user_context.GetUserID(), |
| base::Bind(&TriggerResolveWithLoginTimeMarker, |
| "CryptohomeRemove-End", |
| attempt, |
| resolver)); |
| } |
| |
| // Calls cryptohome's key check method. |
| void CheckKey(AuthAttemptState* attempt, |
| scoped_refptr<ParallelAuthenticator> resolver, |
| const std::string& system_salt) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| scoped_ptr<Key> key = |
| TransformKeyIfNeeded(*attempt->user_context.GetKey(), system_salt); |
| cryptohome::AsyncMethodCaller::GetInstance()->AsyncCheckKey( |
| attempt->user_context.GetUserID(), |
| key->GetSecret(), |
| 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) { |
| authentication_profile_ = profile; |
| current_state_.reset(new AuthAttemptState( |
| user_context, |
| User::USER_TYPE_REGULAR, |
| false, // unlock |
| false, // online_complete |
| !UserManager::Get()->IsKnownUser(user_context.GetUserID()))); |
| // Reset the verified flag. |
| owner_is_verified_ = false; |
| |
| SystemSaltGetter::Get()->GetSystemSalt( |
| base::Bind(&Mount, |
| current_state_.get(), |
| scoped_refptr<ParallelAuthenticator>(this), |
| cryptohome::MOUNT_FLAGS_NONE)); |
| } |
| |
| void ParallelAuthenticator::CompleteLogin(Profile* profile, |
| const UserContext& user_context) { |
| authentication_profile_ = profile; |
| current_state_.reset(new AuthAttemptState( |
| user_context, |
| User::USER_TYPE_REGULAR, |
| true, // unlock |
| false, // online_complete |
| !UserManager::Get()->IsKnownUser(user_context.GetUserID()))); |
| |
| // Reset the verified flag. |
| owner_is_verified_ = false; |
| |
| SystemSaltGetter::Get()->GetSystemSalt( |
| base::Bind(&Mount, |
| current_state_.get(), |
| scoped_refptr<ParallelAuthenticator>(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(user_context, |
| User::USER_TYPE_REGULAR, |
| true, // unlock |
| true, // online_complete |
| false)); // user_is_new |
| remove_user_data_on_failure_ = false; |
| check_key_attempted_ = true; |
| SystemSaltGetter::Get()->GetSystemSalt( |
| base::Bind(&CheckKey, |
| current_state_.get(), |
| scoped_refptr<ParallelAuthenticator>(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, |
| User::USER_TYPE_LOCALLY_MANAGED, |
| false, // unlock |
| false, // online_complete |
| false)); // user_is_new |
| remove_user_data_on_failure_ = false; |
| SystemSaltGetter::Get()->GetSystemSalt( |
| base::Bind(&Mount, |
| current_state_.get(), |
| scoped_refptr<ParallelAuthenticator>(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), |
| User::USER_TYPE_RETAIL_MODE, |
| false, // unlock |
| false, // online_complete |
| false)); // user_is_new |
| remove_user_data_on_failure_ = false; |
| ephemeral_mount_attempted_ = true; |
| MountGuest(current_state_.get(), |
| scoped_refptr<ParallelAuthenticator>(this)); |
| } |
| |
| void ParallelAuthenticator::LoginOffTheRecord() { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| current_state_.reset(new AuthAttemptState( |
| UserContext(UserManager::kGuestUserName), |
| User::USER_TYPE_GUEST, |
| false, // unlock |
| false, // online_complete |
| false)); // user_is_new |
| remove_user_data_on_failure_ = false; |
| ephemeral_mount_attempted_ = true; |
| MountGuest(current_state_.get(), |
| scoped_refptr<ParallelAuthenticator>(this)); |
| } |
| |
| void ParallelAuthenticator::LoginAsPublicAccount(const std::string& username) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| current_state_.reset(new AuthAttemptState(UserContext(username), |
| User::USER_TYPE_PUBLIC_ACCOUNT, |
| false, // unlock |
| false, // online_complete |
| false)); // user_is_new |
| remove_user_data_on_failure_ = false; |
| ephemeral_mount_attempted_ = true; |
| SystemSaltGetter::Get()->GetSystemSalt( |
| base::Bind(&Mount, |
| current_state_.get(), |
| scoped_refptr<ParallelAuthenticator>(this), |
| cryptohome::CREATE_IF_MISSING | cryptohome::ENSURE_EPHEMERAL)); |
| } |
| |
| void ParallelAuthenticator::LoginAsKioskAccount( |
| const std::string& app_user_id, |
| bool use_guest_mount) { |
| DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI)); |
| |
| const std::string user_id = |
| use_guest_mount ? UserManager::kGuestUserName : app_user_id; |
| current_state_.reset(new AuthAttemptState(UserContext(user_id), |
| User::USER_TYPE_KIOSK_APP, |
| false, // unlock |
| false, // online_complete |
| false)); // user_is_new |
| |
| remove_user_data_on_failure_ = true; |
| if (!use_guest_mount) { |
| MountPublic(current_state_.get(), |
| scoped_refptr<ParallelAuthenticator>(this), |
| cryptohome::CREATE_IF_MISSING); |
| } else { |
| ephemeral_mount_attempted_ = true; |
| MountGuestAndGetHash(current_state_.get(), |
| scoped_refptr<ParallelAuthenticator>(this)); |
| } |
| } |
| |
| 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<AuthenticationNotificationDetails>(&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<AuthenticationNotificationDetails>(&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<AuthenticationNotificationDetails>(&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<AuthenticationNotificationDetails>(&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<ParallelAuthenticator>(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<ParallelAuthenticator>(this))); |
| } |
| |
| void ParallelAuthenticator::ResyncEncryptedData() { |
| resync_attempted_ = true; |
| current_state_->ResetCryptohomeStatus(); |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| base::Bind(&Remove, |
| current_state_.get(), |
| scoped_refptr<ParallelAuthenticator>(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; |
| } |
| |
| const std::string& user_id = current_state_->user_context.GetUserID(); |
| OwnerSettingsServiceFactory::GetInstance()->SetUsername(user_id); |
| |
| // 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); |
| } |
| |
| OwnerSettingsService::IsPrivateKeyExistAsync( |
| 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<ParallelAuthenticator>(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.SetIsUsingOAuth(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.SetIsUsingOAuth(false); |
| BrowserThread::PostTask( |
| BrowserThread::UI, FROM_HERE, |
| base::Bind(&ParallelAuthenticator::OnLoginSuccess, this)); |
| break; |
| case LOCALLY_MANAGED_USER_LOGIN: |
| current_state_->user_context.SetIsUsingOAuth(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; |
| } |
| } |
| |
| 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 |