// 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 "chromeos/login/auth/cryptohome_authenticator.h" #include <vector> #include "base/basictypes.h" #include "base/bind.h" #include "base/files/file_path.h" #include "base/location.h" #include "base/logging.h" #include "chromeos/cryptohome/async_method_caller.h" #include "chromeos/cryptohome/cryptohome_parameters.h" #include "chromeos/cryptohome/homedir_methods.h" #include "chromeos/cryptohome/system_salt_getter.h" #include "chromeos/dbus/cryptohome_client.h" #include "chromeos/dbus/dbus_thread_manager.h" #include "chromeos/login/auth/auth_status_consumer.h" #include "chromeos/login/auth/key.h" #include "chromeos/login/auth/user_context.h" #include "chromeos/login/login_state.h" #include "chromeos/login/user_names.h" #include "chromeos/login_event_recorder.h" #include "components/user_manager/user_type.h" #include "third_party/cros_system_api/dbus/service_constants.h" namespace chromeos { namespace { // The label used for the key derived from the user's GAIA credentials. const char kCryptohomeGAIAKeyLabel[] = "gaia"; // The name under which the type of key generated from the user's GAIA // credentials is stored. const char kKeyProviderDataTypeName[] = "type"; // The name under which the salt used to generate a key from the user's GAIA // credentials is stored. const char kKeyProviderDataSaltName[] = "salt"; // 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<CryptohomeAuthenticator> resolver, bool success, cryptohome::MountError return_code) { attempt->RecordCryptohomeStatus(success, return_code); resolver->Resolve(); } // Records get hash status and calls resolver->Resolve(). void TriggerResolveHash(AuthAttemptState* attempt, scoped_refptr<CryptohomeAuthenticator> resolver, bool success, const std::string& username_hash) { 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<CryptohomeAuthenticator> resolver, bool success, cryptohome::MountError return_code) { chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(marker_name, false); TriggerResolve(attempt, resolver, success, return_code); } // Records an error in accessing the user's cryptohome with the given key and // calls resolver->Resolve() after adding a login time marker. void RecordKeyErrorAndResolve(AuthAttemptState* attempt, scoped_refptr<CryptohomeAuthenticator> resolver) { chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End", false); attempt->RecordCryptohomeStatus(false /* success */, cryptohome::MOUNT_ERROR_KEY_FAILURE); resolver->Resolve(); } // Callback invoked when cryptohome's MountEx() method has finished. void OnMount(AuthAttemptState* attempt, scoped_refptr<CryptohomeAuthenticator> resolver, bool success, cryptohome::MountError return_code, const std::string& mount_hash) { chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End", false); attempt->RecordCryptohomeStatus(success, return_code); if (success) attempt->RecordUsernameHash(mount_hash); else attempt->RecordUsernameHashFailed(); resolver->Resolve(); } // Calls cryptohome's MountEx() method. The key in |attempt->user_context| must // not be a plain text password. If the user provided a plain text password, // that password must be transformed to another key type (by salted hashing) // before calling this method. void DoMount(AuthAttemptState* attempt, scoped_refptr<CryptohomeAuthenticator> resolver, bool ephemeral, bool create_if_nonexistent) { const Key* key = attempt->user_context.GetKey(); // If the |key| is a plain text password, crash rather than attempting to // mount the cryptohome with a plain text password. CHECK_NE(Key::KEY_TYPE_PASSWORD_PLAIN, key->GetKeyType()); // Set state that username_hash is requested here so that test implementation // that returns directly would not generate 2 OnLoginSucces() calls. attempt->UsernameHashRequested(); // Set the authentication's key label to an empty string, which is a wildcard // allowing any key to match. This is necessary because cryptohomes created by // Chrome OS M38 and older will have a legacy key with no label while those // created by Chrome OS M39 and newer will have a key with the label // kCryptohomeGAIAKeyLabel. const cryptohome::KeyDefinition auth_key(key->GetSecret(), std::string(), cryptohome::PRIV_DEFAULT); cryptohome::MountParameters mount(ephemeral); if (create_if_nonexistent) { mount.create_keys.push_back(cryptohome::KeyDefinition( key->GetSecret(), kCryptohomeGAIAKeyLabel, cryptohome::PRIV_DEFAULT)); } cryptohome::HomedirMethods::GetInstance()->MountEx( cryptohome::Identification(attempt->user_context.GetUserID()), cryptohome::Authorization(auth_key), mount, base::Bind(&OnMount, attempt, resolver)); } // Callback invoked when the system salt has been retrieved. Transforms the key // in |attempt->user_context| using Chrome's default hashing algorithm and the // system salt, then calls MountEx(). void OnGetSystemSalt(AuthAttemptState* attempt, scoped_refptr<CryptohomeAuthenticator> resolver, bool ephemeral, bool create_if_nonexistent, const std::string& system_salt) { DCHECK_EQ(Key::KEY_TYPE_PASSWORD_PLAIN, attempt->user_context.GetKey()->GetKeyType()); attempt->user_context.GetKey()->Transform( Key::KEY_TYPE_SALTED_SHA256_TOP_HALF, system_salt); DoMount(attempt, resolver, ephemeral, create_if_nonexistent); } // Callback invoked when cryptohome's GetKeyDataEx() method has finished. // * If GetKeyDataEx() returned metadata indicating the hashing algorithm and // salt that were used to generate the key for this user's cryptohome, // transforms the key in |attempt->user_context| with the same parameters. // * Otherwise, starts the retrieval of the system salt so that the key in // |attempt->user_context| can be transformed with Chrome's default hashing // algorithm and the system salt. // The resulting key is then passed to cryptohome's MountEx(). void OnGetKeyDataEx( AuthAttemptState* attempt, scoped_refptr<CryptohomeAuthenticator> resolver, bool ephemeral, bool create_if_nonexistent, bool success, cryptohome::MountError return_code, const std::vector<cryptohome::KeyDefinition>& key_definitions) { if (success) { if (key_definitions.size() == 1) { const cryptohome::KeyDefinition& key_definition = key_definitions.front(); DCHECK_EQ(kCryptohomeGAIAKeyLabel, key_definition.label); // Extract the key type and salt from |key_definition|, if present. scoped_ptr<int64> type; scoped_ptr<std::string> salt; for (std::vector<cryptohome::KeyDefinition::ProviderData>:: const_iterator it = key_definition.provider_data.begin(); it != key_definition.provider_data.end(); ++it) { if (it->name == kKeyProviderDataTypeName) { if (it->number) type.reset(new int64(*it->number)); else NOTREACHED(); } else if (it->name == kKeyProviderDataSaltName) { if (it->bytes) salt.reset(new std::string(*it->bytes)); else NOTREACHED(); } } if (type) { if (*type < 0 || *type >= Key::KEY_TYPE_COUNT) { LOG(ERROR) << "Invalid key type: " << *type; RecordKeyErrorAndResolve(attempt, resolver); return; } if (!salt) { LOG(ERROR) << "Missing salt."; RecordKeyErrorAndResolve(attempt, resolver); return; } attempt->user_context.GetKey()->Transform( static_cast<Key::KeyType>(*type), *salt); DoMount(attempt, resolver, ephemeral, create_if_nonexistent); return; } } else { LOG(ERROR) << "GetKeyDataEx() returned " << key_definitions.size() << " entries."; } } SystemSaltGetter::Get()->GetSystemSalt(base::Bind(&OnGetSystemSalt, attempt, resolver, ephemeral, create_if_nonexistent)); } // Starts the process that will mount a user's cryptohome. // * If the key in |attempt->user_context| is not a plain text password, // cryptohome's MountEx() method is called directly with the key. // * Otherwise, the key must be transformed (by salted hashing) before being // passed to MountEx(). In that case, cryptohome's GetKeyDataEx() method is // called to retrieve metadata indicating the hashing algorithm and salt that // were used to generate the key for this user's cryptohome and the key is // transformed accordingly before calling MountEx(). void StartMount(AuthAttemptState* attempt, scoped_refptr<CryptohomeAuthenticator> resolver, bool ephemeral, bool create_if_nonexistent) { chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker( "CryptohomeMount-Start", false); if (attempt->user_context.GetKey()->GetKeyType() != Key::KEY_TYPE_PASSWORD_PLAIN) { DoMount(attempt, resolver, ephemeral, create_if_nonexistent); return; } cryptohome::HomedirMethods::GetInstance()->GetKeyDataEx( cryptohome::Identification(attempt->user_context.GetUserID()), kCryptohomeGAIAKeyLabel, base::Bind(&OnGetKeyDataEx, attempt, resolver, ephemeral, create_if_nonexistent)); } // Calls cryptohome's mount method for guest and also get the user hash from // cryptohome. void MountGuestAndGetHash(AuthAttemptState* attempt, scoped_refptr<CryptohomeAuthenticator> resolver) { 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<CryptohomeAuthenticator> resolver, int flags) { 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<CryptohomeAuthenticator> resolver, bool passing_old_hash, const std::string& old_password, const std::string& system_salt) { chromeos::LoginEventRecorder::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<CryptohomeAuthenticator> resolver) { chromeos::LoginEventRecorder::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<CryptohomeAuthenticator> resolver, const std::string& system_salt) { 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 CryptohomeAuthenticator::CryptohomeAuthenticator( scoped_refptr<base::TaskRunner> task_runner, AuthStatusConsumer* consumer) : Authenticator(consumer), task_runner_(task_runner), 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 CryptohomeAuthenticator::AuthenticateToLogin( Profile* profile, const UserContext& user_context) { authentication_profile_ = profile; current_state_.reset(new AuthAttemptState(user_context, user_manager::USER_TYPE_REGULAR, false, // unlock false, // online_complete !IsKnownUser(user_context))); // Reset the verified flag. owner_is_verified_ = false; StartMount(current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this), false /* ephemeral */, false /* create_if_nonexistent */); } void CryptohomeAuthenticator::CompleteLogin(Profile* profile, const UserContext& user_context) { authentication_profile_ = profile; current_state_.reset(new AuthAttemptState(user_context, user_manager::USER_TYPE_REGULAR, true, // unlock false, // online_complete !IsKnownUser(user_context))); // Reset the verified flag. owner_is_verified_ = false; StartMount(current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this), false /* ephemeral */, false /* create_if_nonexistent */); // 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. task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::ResolveLoginCompletionStatus, this)); } void CryptohomeAuthenticator::AuthenticateToUnlock( const UserContext& user_context) { current_state_.reset(new AuthAttemptState(user_context, user_manager::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<CryptohomeAuthenticator>(this))); } void CryptohomeAuthenticator::LoginAsSupervisedUser( const UserContext& user_context) { DCHECK(task_runner_->RunsTasksOnCurrentThread()); // TODO(nkostylev): Pass proper value for |user_is_new| or remove (not used). current_state_.reset(new AuthAttemptState(user_context, user_manager::USER_TYPE_SUPERVISED, false, // unlock false, // online_complete false)); // user_is_new remove_user_data_on_failure_ = false; StartMount(current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this), false /* ephemeral */, false /* create_if_nonexistent */); } void CryptohomeAuthenticator::LoginRetailMode() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); // Note: |kRetailModeUserEMail| is used in other places to identify a retail // mode session. current_state_.reset( new AuthAttemptState(UserContext(chromeos::login::kRetailModeUserName), user_manager::USER_TYPE_RETAIL_MODE, false, // unlock false, // online_complete false)); // user_is_new remove_user_data_on_failure_ = false; ephemeral_mount_attempted_ = true; MountGuestAndGetHash(current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this)); } void CryptohomeAuthenticator::LoginOffTheRecord() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); current_state_.reset( new AuthAttemptState(UserContext(chromeos::login::kGuestUserName), user_manager::USER_TYPE_GUEST, false, // unlock false, // online_complete false)); // user_is_new remove_user_data_on_failure_ = false; ephemeral_mount_attempted_ = true; MountGuestAndGetHash(current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this)); } void CryptohomeAuthenticator::LoginAsPublicSession( const UserContext& user_context) { DCHECK(task_runner_->RunsTasksOnCurrentThread()); current_state_.reset( new AuthAttemptState(user_context, user_manager::USER_TYPE_PUBLIC_ACCOUNT, false, // unlock false, // online_complete false)); // user_is_new remove_user_data_on_failure_ = false; ephemeral_mount_attempted_ = true; StartMount(current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this), true /* ephemeral */, true /* create_if_nonexistent */); } void CryptohomeAuthenticator::LoginAsKioskAccount( const std::string& app_user_id, bool use_guest_mount) { DCHECK(task_runner_->RunsTasksOnCurrentThread()); const std::string user_id = use_guest_mount ? chromeos::login::kGuestUserName : app_user_id; current_state_.reset(new AuthAttemptState(UserContext(user_id), user_manager::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<CryptohomeAuthenticator>(this), cryptohome::CREATE_IF_MISSING); } else { ephemeral_mount_attempted_ = true; MountGuestAndGetHash(current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this)); } } void CryptohomeAuthenticator::OnRetailModeAuthSuccess() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); VLOG(1) << "Retail mode login success"; chromeos::LoginEventRecorder::Get()->RecordAuthenticationSuccess(); if (consumer_) consumer_->OnRetailModeAuthSuccess(current_state_->user_context); } void CryptohomeAuthenticator::OnAuthSuccess() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); VLOG(1) << "Login success"; // Send notification of success chromeos::LoginEventRecorder::Get()->RecordAuthenticationSuccess(); { base::AutoLock for_this_block(success_lock_); already_reported_success_ = true; } if (consumer_) consumer_->OnAuthSuccess(current_state_->user_context); } void CryptohomeAuthenticator::OnOffTheRecordAuthSuccess() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); chromeos::LoginEventRecorder::Get()->RecordAuthenticationSuccess(); if (consumer_) consumer_->OnOffTheRecordAuthSuccess(); } void CryptohomeAuthenticator::OnPasswordChangeDetected() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); if (consumer_) consumer_->OnPasswordChangeDetected(); } void CryptohomeAuthenticator::OnAuthFailure(const AuthFailure& error) { DCHECK(task_runner_->RunsTasksOnCurrentThread()); // OnAuthFailure 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; } chromeos::LoginEventRecorder::Get()->RecordAuthenticationFailure(); LOG(WARNING) << "Login failed: " << error.GetErrorString(); if (consumer_) consumer_->OnAuthFailure(error); } void CryptohomeAuthenticator::RecoverEncryptedData( const std::string& old_password) { migrate_attempted_ = true; current_state_->ResetCryptohomeStatus(); SystemSaltGetter::Get()->GetSystemSalt( base::Bind(&Migrate, current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this), true, old_password)); } void CryptohomeAuthenticator::RemoveEncryptedData() { remove_attempted_ = true; current_state_->ResetCryptohomeStatus(); task_runner_->PostTask( FROM_HERE, base::Bind(&Remove, current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this))); } void CryptohomeAuthenticator::ResyncEncryptedData() { resync_attempted_ = true; current_state_->ResetCryptohomeStatus(); task_runner_->PostTask( FROM_HERE, base::Bind(&Remove, current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this))); } bool CryptohomeAuthenticator::VerifyOwner() { if (owner_is_verified_) return true; // Check if policy data is fine and continue in safe mode if needed. if (!IsSafeMode()) { // Now we can continue with the login and report mount success. user_can_login_ = true; owner_is_verified_ = true; return true; } CheckSafeModeOwnership( current_state_->user_context, base::Bind(&CryptohomeAuthenticator::OnOwnershipChecked, this)); return false; } void CryptohomeAuthenticator::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 CryptohomeAuthenticator::Resolve() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); bool create_if_nonexistent = false; CryptohomeAuthenticator::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. task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthFailure, this, AuthFailure(AuthFailure::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; task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthFailure, this, AuthFailure(AuthFailure::DATA_REMOVAL_FAILED))); break; case FAILED_TMPFS: // In this case, we tried to mount a tmpfs for guest and failed. task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthFailure, this, AuthFailure(AuthFailure::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. task_runner_->PostTask(FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthFailure, this, AuthFailure(AuthFailure::TPM_ERROR))); break; case FAILED_USERNAME_HASH: // In this case, we failed the GetSanitizedUsername request to // cryptohomed. This can happen for any login attempt. task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthFailure, this, AuthFailure(AuthFailure::USERNAME_HASH_FAILED))); break; case REMOVED_DATA_AFTER_FAILURE: remove_user_data_on_failure_ = false; task_runner_->PostTask(FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthFailure, this, *delayed_login_failure_)); break; case CREATE_NEW: create_if_nonexistent = true; case RECOVER_MOUNT: current_state_->ResetCryptohomeStatus(); StartMount(current_state_.get(), scoped_refptr<CryptohomeAuthenticator>(this), false /*ephemeral*/, create_if_nonexistent); break; case NEED_OLD_PW: task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::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"; task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthSuccess, this)); break; case DEMO_LOGIN: VLOG(2) << "Retail mode login"; current_state_->user_context.SetIsUsingOAuth(false); task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnRetailModeAuthSuccess, this)); break; case GUEST_LOGIN: task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnOffTheRecordAuthSuccess, this)); break; case KIOSK_ACCOUNT_LOGIN: case PUBLIC_ACCOUNT_LOGIN: current_state_->user_context.SetIsUsingOAuth(false); task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthSuccess, this)); break; case SUPERVISED_USER_LOGIN: current_state_->user_context.SetIsUsingOAuth(false); task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthSuccess, this)); break; case LOGIN_FAILED: current_state_->ResetCryptohomeStatus(); task_runner_->PostTask(FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthFailure, 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!"; } task_runner_->PostTask( FROM_HERE, base::Bind(&CryptohomeAuthenticator::OnAuthFailure, this, AuthFailure(AuthFailure::OWNER_REQUIRED))); break; } default: NOTREACHED(); break; } } CryptohomeAuthenticator::~CryptohomeAuthenticator() { } CryptohomeAuthenticator::AuthState CryptohomeAuthenticator::ResolveState() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); // 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() == AuthFailure::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; } CryptohomeAuthenticator::AuthState CryptohomeAuthenticator::ResolveCryptohomeFailureState() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); 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; } CryptohomeAuthenticator::AuthState CryptohomeAuthenticator::ResolveCryptohomeSuccessState() { DCHECK(task_runner_->RunsTasksOnCurrentThread()); 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_manager::USER_TYPE_GUEST) return GUEST_LOGIN; if (current_state_->user_type == user_manager::USER_TYPE_RETAIL_MODE) return DEMO_LOGIN; if (current_state_->user_type == user_manager::USER_TYPE_PUBLIC_ACCOUNT) return PUBLIC_ACCOUNT_LOGIN; if (current_state_->user_type == user_manager::USER_TYPE_KIOSK_APP) return KIOSK_ACCOUNT_LOGIN; if (current_state_->user_type == user_manager::USER_TYPE_SUPERVISED) return SUPERVISED_USER_LOGIN; if (!VerifyOwner()) return CONTINUE; return user_can_login_ ? OFFLINE_LOGIN : OWNER_REQUIRED; } CryptohomeAuthenticator::AuthState CryptohomeAuthenticator::ResolveOnlineSuccessState( CryptohomeAuthenticator::AuthState offline_state) { DCHECK(task_runner_->RunsTasksOnCurrentThread()); 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 CryptohomeAuthenticator::ResolveLoginCompletionStatus() { // Shortcut online state resolution process. current_state_->RecordOnlineLoginStatus(AuthFailure::AuthFailureNone()); Resolve(); } void CryptohomeAuthenticator::SetOwnerState(bool owner_check_finished, bool check_result) { owner_is_verified_ = owner_check_finished; user_can_login_ = check_result; } } // namespace chromeos