| // 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 <stdint.h> |
| |
| #include <vector> |
| |
| #include "base/bind.h" |
| #include "base/files/file_path.h" |
| #include "base/location.h" |
| #include "base/logging.h" |
| #include "base/memory/weak_ptr.h" |
| #include "base/metrics/histogram_macros.h" |
| #include "chromeos/constants/chromeos_switches.h" |
| #include "chromeos/cryptohome/async_method_caller.h" |
| #include "chromeos/cryptohome/cryptohome_parameters.h" |
| #include "chromeos/cryptohome/cryptohome_util.h" |
| #include "chromeos/cryptohome/homedir_methods.h" |
| #include "chromeos/cryptohome/system_salt_getter.h" |
| #include "chromeos/dbus/cryptohome/cryptohome_client.h" |
| #include "chromeos/login/auth/auth_status_consumer.h" |
| #include "chromeos/login/auth/key.h" |
| #include "chromeos/login/auth/login_event_recorder.h" |
| #include "chromeos/login/auth/user_context.h" |
| #include "chromeos/login/login_state/login_state.h" |
| #include "components/account_id/account_id.h" |
| #include "components/device_event_log/device_event_log.h" |
| #include "components/user_manager/known_user.h" |
| #include "components/user_manager/user_names.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 label used for the key generated by Cryptohome for public mount. |
| const char kCryptohomePublicMountKeyLabel[] = "publicmount"; |
| |
| // 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"; |
| |
| // Name of UMA histogram. |
| const char kCryptohomeMigrationToGaiaId[] = "Cryptohome.MigrationToGaiaId"; |
| |
| // This enum is used to define the buckets for an enumerated UMA histogram. |
| // Hence, |
| // (a) existing enumerated constants should never be deleted or reordered, and |
| // (b) new constants should only be appended at the end of the enumeration. |
| // |
| // This must be kept in sync with enum CryptohomeMigrationToGaiaId in |
| // histograms.xml . |
| enum CryptohomeMigrationToGaiaId { |
| NOT_STARTED = 0, |
| ALREADY_MIGRATED = 1, |
| SUCCESS = 2, |
| FAILURE = 3, |
| ENTRIES_COUNT |
| }; |
| |
| // Report to UMA. |
| void UMACryptohomeMigrationToGaiaId(const CryptohomeMigrationToGaiaId status) { |
| UMA_HISTOGRAM_ENUMERATION(kCryptohomeMigrationToGaiaId, status, |
| CryptohomeMigrationToGaiaId::ENTRIES_COUNT); |
| } |
| |
| // Returns a human-readable string describing |state|. |
| const char* AuthStateToString(CryptohomeAuthenticator::AuthState state) { |
| switch (state) { |
| case CryptohomeAuthenticator::CONTINUE: |
| return "CONTINUE"; |
| case CryptohomeAuthenticator::NO_MOUNT: |
| return "NO_MOUNT"; |
| case CryptohomeAuthenticator::FAILED_MOUNT: |
| return "FAILED_MOUNT"; |
| case CryptohomeAuthenticator::FAILED_REMOVE: |
| return "FAILED_REMOVE"; |
| case CryptohomeAuthenticator::FAILED_TMPFS: |
| return "FAILED_TMPFS"; |
| case CryptohomeAuthenticator::FAILED_TPM: |
| return "FAILED_TPM"; |
| case CryptohomeAuthenticator::CREATE_NEW: |
| return "CREATE_NEW"; |
| case CryptohomeAuthenticator::RECOVER_MOUNT: |
| return "RECOVER_MONUT"; |
| case CryptohomeAuthenticator::POSSIBLE_PW_CHANGE: |
| return "POSSIBLE_PW_CHANGE"; |
| case CryptohomeAuthenticator::NEED_NEW_PW: |
| return "NEED_NEW_PW"; |
| case CryptohomeAuthenticator::NEED_OLD_PW: |
| return "NEED_OLD_PW"; |
| case CryptohomeAuthenticator::HAVE_NEW_PW: |
| return "HAVE_NEW_PW"; |
| case CryptohomeAuthenticator::OFFLINE_LOGIN: |
| return "OFFLINE_LOGIN"; |
| case CryptohomeAuthenticator::ONLINE_LOGIN: |
| return "ONLINE_LOGIN"; |
| case CryptohomeAuthenticator::UNLOCK: |
| return "UNLOCK"; |
| case CryptohomeAuthenticator::ONLINE_FAILED: |
| return "ONLINE_FAILED"; |
| case CryptohomeAuthenticator::GUEST_LOGIN: |
| return "GUEST_LOGIN"; |
| case CryptohomeAuthenticator::PUBLIC_ACCOUNT_LOGIN: |
| return "PUBLIC_ACCOUNT_LOGIN"; |
| case CryptohomeAuthenticator::SUPERVISED_USER_LOGIN: |
| return "SUPERVISED_USER_LOGIN"; |
| case CryptohomeAuthenticator::LOGIN_FAILED: |
| return "LOGIN_FAILED"; |
| case CryptohomeAuthenticator::OWNER_REQUIRED: |
| return "OWNER_REQUIRED"; |
| case CryptohomeAuthenticator::FAILED_USERNAME_HASH: |
| return "FAILED_USERNAME_HASH"; |
| case CryptohomeAuthenticator::KIOSK_ACCOUNT_LOGIN: |
| return "KIOSK_ACCOUNT_LOGIN"; |
| case CryptohomeAuthenticator::REMOVED_DATA_AFTER_FAILURE: |
| return "REMOVED_DATA_AFTER_FAILURE"; |
| case CryptohomeAuthenticator::FAILED_OLD_ENCRYPTION: |
| return "FAILED_OLD_ENCRYPTION"; |
| case CryptohomeAuthenticator::FAILED_PREVIOUS_MIGRATION_INCOMPLETE: |
| return "FAILED_PREVIOUS_MIGRATION_INCOMPLETE"; |
| case CryptohomeAuthenticator::OFFLINE_NO_MOUNT: |
| return "OFFLINE_NO_MOUNT"; |
| } |
| return "UNKNOWN"; |
| } |
| |
| // Hashes |key| with |system_salt| if it its type is KEY_TYPE_PASSWORD_PLAIN. |
| // Returns the keys unmodified otherwise. |
| std::unique_ptr<Key> TransformKeyIfNeeded(const Key& key, |
| const std::string& system_salt) { |
| std::unique_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; |
| } |
| |
| // Records get hash status and calls resolver->Resolve(). |
| void TriggerResolveHash(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver, |
| bool success, |
| const std::string& username_hash) { |
| if (success) |
| attempt->RecordUsernameHash(username_hash); |
| else |
| attempt->RecordUsernameHashFailed(); |
| resolver->Resolve(); |
| } |
| |
| // 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(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver) { |
| chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End", |
| false); |
| attempt->RecordCryptohomeStatus(cryptohome::MOUNT_ERROR_KEY_FAILURE); |
| resolver->Resolve(); |
| } |
| |
| // Callback invoked when cryptohome's GetSantiziedUsername() method has |
| // finished. |
| void OnGetSanitizedUsername( |
| base::OnceCallback<void(bool, const std::string&)> callback, |
| base::Optional<std::string> result) { |
| std::move(callback).Run(result.has_value(), result.value_or(std::string())); |
| } |
| |
| // Callback invoked when a crypotyhome *Ex method, which only returns a |
| // base::Reply, finishes. |
| void OnBaseReplyMethod(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver, |
| const std::string& time_marker, |
| base::Optional<cryptohome::BaseReply> reply) { |
| chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(time_marker, false); |
| attempt->RecordCryptohomeStatus(BaseReplyToMountError(reply)); |
| resolver->Resolve(); |
| } |
| |
| // Callback invoked when cryptohome's MountEx() method has finished. |
| void OnMount(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver, |
| base::Optional<cryptohome::BaseReply> reply) { |
| const bool public_mount = attempt->user_context.GetUserType() == |
| user_manager::USER_TYPE_KIOSK_APP || |
| attempt->user_context.GetUserType() == |
| user_manager::USER_TYPE_ARC_KIOSK_APP; |
| |
| chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker( |
| public_mount ? "CryptohomeMountPublic-End" : "CryptohomeMount-End", |
| false); |
| attempt->RecordCryptohomeStatus(MountExReplyToMountError(reply)); |
| if (attempt->cryptohome_code() == cryptohome::MOUNT_ERROR_NONE) { |
| attempt->RecordUsernameHash(MountExReplyToMountHash(reply.value())); |
| } else { |
| LOGIN_LOG(ERROR) << "MountEx failed. Error: " << attempt->cryptohome_code(); |
| 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(const base::WeakPtr<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(); |
| |
| cryptohome::MountRequest mount; |
| if (ephemeral) |
| mount.set_require_ephemeral(true); |
| if (create_if_nonexistent) { |
| cryptohome::KeyDefinitionToKey( |
| cryptohome::KeyDefinition::CreateForPassword(key->GetSecret(), |
| kCryptohomeGAIAKeyLabel, |
| cryptohome::PRIV_DEFAULT), |
| mount.mutable_create()->add_keys()); |
| } |
| if (attempt->user_context.IsForcingDircrypto()) |
| mount.set_force_dircrypto_if_available(true); |
| cryptohome::AuthorizationRequest auth; |
| cryptohome::Key* auth_key = auth.mutable_key(); |
| // Don't set the authorization's key label, implicitly setting it 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. |
| // |
| // This logic does not apply to PIN and weak keys in general, as those do not |
| // authenticate against a wildcard label. |
| if (attempt->user_context.IsUsingPin()) |
| auth_key->mutable_data()->set_label(key->GetLabel()); |
| auth_key->set_secret(key->GetSecret()); |
| CryptohomeClient::Get()->MountEx( |
| cryptohome::CreateAccountIdentifierFromAccountId( |
| attempt->user_context.GetAccountId()), |
| auth, mount, base::BindOnce(&OnMount, attempt, resolver)); |
| } |
| |
| // Handle cryptohome migration status. |
| void OnCryptohomeRenamed(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver, |
| bool ephemeral, |
| bool create_if_nonexistent, |
| base::Optional<cryptohome::BaseReply> reply) { |
| cryptohome::MountError return_code = cryptohome::BaseReplyToMountError(reply); |
| chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker( |
| "CryptohomeRename-End", false); |
| const AccountId& account_id = attempt->user_context.GetAccountId(); |
| if (return_code == cryptohome::MOUNT_ERROR_NONE) { |
| cryptohome::SetGaiaIdMigrationStatusDone(account_id); |
| UMACryptohomeMigrationToGaiaId(CryptohomeMigrationToGaiaId::SUCCESS); |
| } else { |
| LOG(ERROR) << "Failed to rename cryptohome for account of type " |
| << AccountId::AccountTypeToString(account_id.GetAccountType()) |
| << " (return_code=" << return_code << ")"; |
| // If rename fails, we can still use legacy cryptohome identifier. |
| // Proceed to DoMount. |
| UMACryptohomeMigrationToGaiaId(CryptohomeMigrationToGaiaId::FAILURE); |
| } |
| DoMount(attempt, resolver, ephemeral, create_if_nonexistent); |
| } |
| |
| // This method migrates cryptohome identifier to gaia id (if needed), |
| // and then calls Mount. |
| void EnsureCryptohomeMigratedToGaiaId( |
| const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver, |
| bool ephemeral, |
| bool create_if_nonexistent) { |
| // Set the migration flag for Active Directory accounts since they're using |
| // the account id key as cryptohome id. |
| const AccountId& account_id = attempt->user_context.GetAccountId(); |
| if (account_id.GetAccountType() == AccountType::ACTIVE_DIRECTORY) { |
| cryptohome::SetGaiaIdMigrationStatusDone(account_id); |
| } |
| |
| // Only Google accounts have to be migrated. |
| if (account_id.GetAccountType() != AccountType::GOOGLE) { |
| DoMount(attempt, resolver, ephemeral, create_if_nonexistent); |
| return; |
| } |
| |
| const bool is_gaiaid_migration_started = switches::IsGaiaIdMigrationStarted(); |
| if (!is_gaiaid_migration_started) { |
| UMACryptohomeMigrationToGaiaId(CryptohomeMigrationToGaiaId::NOT_STARTED); |
| DoMount(attempt, resolver, ephemeral, create_if_nonexistent); |
| return; |
| } |
| const bool already_migrated = |
| cryptohome::GetGaiaIdMigrationStatus(account_id); |
| const bool has_account_key = account_id.HasAccountIdKey(); |
| |
| bool need_migration = false; |
| if (!create_if_nonexistent && !already_migrated) { |
| if (has_account_key) { |
| need_migration = true; |
| } else { |
| LOG(WARNING) |
| << "Google account has no gaia id. Cryptohome migration skipped."; |
| } |
| } |
| if (need_migration) { |
| chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker( |
| "CryptohomeRename-Start", false); |
| cryptohome::AccountIdentifier cryptohome_id_from; |
| cryptohome_id_from.set_account_id(account_id.GetUserEmail()); // Migrated |
| cryptohome::AccountIdentifier cryptohome_id_to; |
| cryptohome_id_to.set_account_id(account_id.GetAccountIdKey()); |
| |
| CryptohomeClient::Get()->RenameCryptohome( |
| cryptohome_id_from, cryptohome_id_to, |
| base::BindOnce(&OnCryptohomeRenamed, attempt, resolver, ephemeral, |
| create_if_nonexistent)); |
| return; |
| } |
| if (!already_migrated && has_account_key) { |
| // Mark new users migrated. |
| cryptohome::SetGaiaIdMigrationStatusDone(account_id); |
| } |
| if (already_migrated) { |
| UMACryptohomeMigrationToGaiaId( |
| CryptohomeMigrationToGaiaId::ALREADY_MIGRATED); |
| } |
| |
| DoMount(attempt, resolver, ephemeral, create_if_nonexistent); |
| } |
| |
| // 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(const base::WeakPtr<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); |
| |
| EnsureCryptohomeMigratedToGaiaId(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(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver, |
| bool ephemeral, |
| bool create_if_nonexistent, |
| base::Optional<cryptohome::BaseReply> reply) { |
| if (cryptohome::GetKeyDataReplyToMountError(reply) == |
| cryptohome::MOUNT_ERROR_NONE) { |
| std::vector<cryptohome::KeyDefinition> key_definitions = |
| cryptohome::GetKeyDataReplyToKeyDefinitions(reply); |
| 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. |
| std::unique_ptr<int64_t> type; |
| std::unique_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_t(*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) { |
| LOGIN_LOG(ERROR) << "Invalid key type: " << *type; |
| RecordKeyErrorAndResolve(attempt, resolver); |
| return; |
| } |
| |
| if (!salt) { |
| LOGIN_LOG(ERROR) << "Missing salt."; |
| RecordKeyErrorAndResolve(attempt, resolver); |
| return; |
| } |
| |
| attempt->user_context.GetKey()->Transform( |
| static_cast<Key::KeyType>(*type), *salt); |
| EnsureCryptohomeMigratedToGaiaId(attempt, resolver, ephemeral, |
| create_if_nonexistent); |
| return; |
| } |
| } else { |
| LOGIN_LOG(EVENT) << "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(const base::WeakPtr<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) { |
| EnsureCryptohomeMigratedToGaiaId(attempt, resolver, ephemeral, |
| create_if_nonexistent); |
| return; |
| } |
| |
| cryptohome::GetKeyDataRequest request; |
| request.mutable_key()->mutable_data()->set_label(kCryptohomeGAIAKeyLabel); |
| CryptohomeClient::Get()->GetKeyDataEx( |
| cryptohome::CreateAccountIdentifierFromAccountId( |
| attempt->user_context.GetAccountId()), |
| cryptohome::AuthorizationRequest(), request, |
| base::BindOnce(&OnGetKeyDataEx, attempt, resolver, ephemeral, |
| create_if_nonexistent)); |
| } |
| |
| // Calls cryptohome's mount method for guest and also get the user hash from |
| // cryptohome. |
| void MountGuestAndGetHash(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver) { |
| chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker( |
| "CryptohomeMountGuest-Start", false); |
| attempt->UsernameHashRequested(); |
| |
| CryptohomeClient::Get()->MountGuestEx( |
| cryptohome::MountGuestRequest(), |
| base::BindOnce(&OnBaseReplyMethod, attempt, resolver, |
| "CryptohomeMountGuest-End")); |
| |
| CryptohomeClient::Get()->GetSanitizedUsername( |
| cryptohome::CreateAccountIdentifierFromAccountId( |
| attempt->user_context.GetAccountId()), |
| base::BindOnce(&OnGetSanitizedUsername, |
| base::BindOnce(&TriggerResolveHash, attempt, resolver))); |
| } |
| |
| // Calls cryptohome's MountEx method with the public_mount option. |
| void MountPublic(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver, |
| bool force_dircrypto_if_available) { |
| cryptohome::MountRequest mount; |
| if (force_dircrypto_if_available) |
| mount.set_force_dircrypto_if_available(true); |
| mount.set_public_mount(true); |
| // Set the request to create a new homedir when missing. |
| cryptohome::KeyDefinitionToKey( |
| cryptohome::KeyDefinition::CreateForPassword( |
| std::string(), kCryptohomePublicMountKeyLabel, |
| cryptohome::PRIV_DEFAULT), |
| mount.mutable_create()->add_keys()); |
| |
| // For public mounts, authorization secret is filled by cryptohomed, hence it |
| // is left empty. Authentication's key label is also set to an empty string, |
| // which is a wildcard allowing any key to match to allow cryptohomes created |
| // in a legacy way. (See comments in DoMount.) |
| CryptohomeClient::Get()->MountEx( |
| cryptohome::CreateAccountIdentifierFromAccountId( |
| attempt->user_context.GetAccountId()), |
| cryptohome::AuthorizationRequest(), mount, |
| base::BindOnce(&OnMount, attempt, resolver)); |
| } |
| |
| // Calls cryptohome's key migration method. |
| void Migrate(const base::WeakPtr<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::AccountIdentifier account_id = |
| cryptohome::CreateAccountIdentifierFromAccountId( |
| attempt->user_context.GetAccountId()); |
| |
| cryptohome::AuthorizationRequest auth_request; |
| cryptohome::MigrateKeyRequest migrate_request; |
| // TODO(bartfab): Retrieve the hashing algorithm and salt to use for |old_key| |
| // from cryptohomed. |
| std::unique_ptr<Key> old_key = |
| TransformKeyIfNeeded(Key(old_password), system_salt); |
| std::unique_ptr<Key> new_key = |
| TransformKeyIfNeeded(*attempt->user_context.GetKey(), system_salt); |
| if (passing_old_hash) { |
| auth_request.mutable_key()->set_secret(old_key->GetSecret()); |
| migrate_request.set_secret(new_key->GetSecret()); |
| } else { |
| auth_request.mutable_key()->set_secret(new_key->GetSecret()); |
| migrate_request.set_secret(old_key->GetSecret()); |
| } |
| |
| CryptohomeClient::Get()->MigrateKeyEx( |
| account_id, auth_request, migrate_request, |
| base::BindOnce(&OnBaseReplyMethod, attempt, resolver, |
| "CryptohomeMigrate-End")); |
| } |
| |
| // Calls cryptohome's remove method. |
| void Remove(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver) { |
| chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker( |
| "CryptohomeRemove-Start", false); |
| |
| cryptohome::AccountIdentifier account_id; |
| account_id.set_account_id( |
| cryptohome::Identification(attempt->user_context.GetAccountId()).id()); |
| |
| CryptohomeClient::Get()->RemoveEx( |
| account_id, base::BindOnce(&OnBaseReplyMethod, attempt, resolver, |
| "CryptohomeRemove-End")); |
| } |
| |
| void OnKeyChecked(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver, |
| base::Optional<cryptohome::BaseReply> reply) { |
| attempt->RecordCryptohomeStatus(BaseReplyToMountError(reply)); |
| resolver->Resolve(); |
| } |
| |
| // Calls cryptohome's key check method. |
| void CheckKey(const base::WeakPtr<AuthAttemptState>& attempt, |
| scoped_refptr<CryptohomeAuthenticator> resolver, |
| const std::string& system_salt) { |
| std::unique_ptr<Key> key = |
| TransformKeyIfNeeded(*attempt->user_context.GetKey(), system_salt); |
| cryptohome::AuthorizationRequest auth; |
| auth.mutable_key()->set_secret(key->GetSecret()); |
| CryptohomeClient::Get()->CheckKeyEx( |
| cryptohome::CreateAccountIdentifierFromAccountId( |
| attempt->user_context.GetAccountId()), |
| auth, cryptohome::CheckKeyRequest(), |
| base::BindOnce(&OnKeyChecked, 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_(AuthFailure::NONE) {} |
| |
| void CryptohomeAuthenticator::AuthenticateToLogin( |
| content::BrowserContext* context, |
| const UserContext& user_context) { |
| DCHECK(user_context.GetUserType() == user_manager::USER_TYPE_REGULAR || |
| user_context.GetUserType() == user_manager::USER_TYPE_CHILD || |
| user_context.GetUserType() == |
| user_manager::USER_TYPE_ACTIVE_DIRECTORY); |
| authentication_context_ = context; |
| current_state_.reset(new AuthAttemptState(user_context, |
| false, // unlock |
| false, // online_complete |
| !IsKnownUser(user_context))); |
| // Reset the verified flag. |
| owner_is_verified_ = false; |
| |
| StartMount(current_state_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this), |
| false /* ephemeral */, false /* create_if_nonexistent */); |
| } |
| |
| void CryptohomeAuthenticator::CompleteLogin(content::BrowserContext* context, |
| const UserContext& user_context) { |
| DCHECK(user_context.GetUserType() == user_manager::USER_TYPE_REGULAR || |
| user_context.GetUserType() == user_manager::USER_TYPE_CHILD || |
| user_context.GetUserType() == |
| user_manager::USER_TYPE_ACTIVE_DIRECTORY); |
| authentication_context_ = context; |
| current_state_.reset(new AuthAttemptState(user_context, |
| true, // unlock |
| false, // online_complete |
| !IsKnownUser(user_context))); |
| |
| // Reset the verified flag. |
| owner_is_verified_ = false; |
| if (!user_manager::known_user::FindPrefs(user_context.GetAccountId(), |
| nullptr)) { |
| // Save logged in user into local state as early as possible. |
| user_manager::known_user::SaveKnownUser(user_context.GetAccountId()); |
| } |
| |
| StartMount(current_state_->AsWeakPtr(), |
| 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::BindOnce(&CryptohomeAuthenticator::ResolveLoginCompletionStatus, |
| this)); |
| } |
| |
| void CryptohomeAuthenticator::AuthenticateToUnlock( |
| const UserContext& user_context) { |
| DCHECK_EQ(user_manager::USER_TYPE_REGULAR, user_context.GetUserType()); |
| current_state_.reset(new AuthAttemptState(user_context, |
| 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_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this))); |
| } |
| |
| void CryptohomeAuthenticator::LoginAsSupervisedUser( |
| const UserContext& user_context) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| DCHECK_EQ(user_manager::USER_TYPE_SUPERVISED, user_context.GetUserType()); |
| |
| // TODO(nkostylev): Pass proper value for |user_is_new| or remove (not used). |
| current_state_.reset(new AuthAttemptState(user_context, |
| false, // unlock |
| false, // online_complete |
| false)); // user_is_new |
| remove_user_data_on_failure_ = false; |
| StartMount(current_state_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this), |
| false /* ephemeral */, false /* create_if_nonexistent */); |
| } |
| |
| void CryptohomeAuthenticator::LoginOffTheRecord() { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| current_state_.reset( |
| new AuthAttemptState(UserContext(user_manager::USER_TYPE_GUEST, |
| user_manager::GuestAccountId()), |
| false, // unlock |
| false, // online_complete |
| false)); // user_is_new |
| remove_user_data_on_failure_ = false; |
| ephemeral_mount_attempted_ = true; |
| MountGuestAndGetHash(current_state_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this)); |
| } |
| |
| void CryptohomeAuthenticator::LoginAsPublicSession( |
| const UserContext& user_context) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| DCHECK_EQ(user_manager::USER_TYPE_PUBLIC_ACCOUNT, user_context.GetUserType()); |
| |
| current_state_.reset(new AuthAttemptState(user_context, |
| false, // unlock |
| false, // online_complete |
| false)); // user_is_new |
| remove_user_data_on_failure_ = false; |
| ephemeral_mount_attempted_ = true; |
| StartMount(current_state_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this), true /* ephemeral */, |
| true /* create_if_nonexistent */); |
| } |
| |
| void CryptohomeAuthenticator::LoginAsKioskAccount( |
| const AccountId& app_account_id, |
| bool use_guest_mount) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| |
| const AccountId& account_id = |
| use_guest_mount ? user_manager::GuestAccountId() : app_account_id; |
| current_state_.reset(new AuthAttemptState( |
| UserContext(user_manager::USER_TYPE_KIOSK_APP, account_id), |
| false, // unlock |
| false, // online_complete |
| false)); // user_is_new |
| |
| remove_user_data_on_failure_ = true; |
| if (!use_guest_mount) { |
| MountPublic(current_state_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this), |
| false); // force_dircrypto_if_available |
| } else { |
| ephemeral_mount_attempted_ = true; |
| MountGuestAndGetHash(current_state_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this)); |
| } |
| } |
| |
| void CryptohomeAuthenticator::LoginAsArcKioskAccount( |
| const AccountId& app_account_id) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| |
| current_state_.reset(new AuthAttemptState( |
| UserContext(user_manager::USER_TYPE_ARC_KIOSK_APP, app_account_id), |
| false, // unlock |
| false, // online_complete |
| false)); // user_is_new |
| |
| remove_user_data_on_failure_ = true; |
| MountPublic(current_state_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this), |
| true); // force_dircrypto_if_available |
| } |
| |
| void CryptohomeAuthenticator::OnAuthSuccess() { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 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_->RunsTasksInCurrentSequence()); |
| chromeos::LoginEventRecorder::Get()->RecordAuthenticationSuccess(); |
| if (consumer_) |
| consumer_->OnOffTheRecordAuthSuccess(); |
| } |
| |
| void CryptohomeAuthenticator::OnPasswordChangeDetected() { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| if (consumer_) |
| consumer_->OnPasswordChangeDetected(); |
| } |
| |
| void CryptohomeAuthenticator::OnOldEncryptionDetected( |
| bool has_incomplete_migration) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| if (consumer_) { |
| consumer_->OnOldEncryptionDetected(current_state_->user_context, |
| has_incomplete_migration); |
| } |
| } |
| |
| // Callback invoked when UnmountEx returns. |
| void CryptohomeAuthenticator::OnUnmountEx( |
| base::Optional<cryptohome::BaseReply> reply) { |
| if (BaseReplyToMountError(reply) != cryptohome::MOUNT_ERROR_NONE) |
| LOGIN_LOG(ERROR) << "Couldn't unmount user's homedir"; |
| OnAuthFailure(AuthFailure(AuthFailure::OWNER_REQUIRED)); |
| } |
| |
| void CryptohomeAuthenticator::OnAuthFailure(const AuthFailure& error) { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| |
| // 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(); |
| LOGIN_LOG(ERROR) << "Login failed: " << error.GetErrorString(); |
| if (consumer_) |
| consumer_->OnAuthFailure(error); |
| } |
| |
| void CryptohomeAuthenticator::MigrateKey(const UserContext& user_context, |
| const std::string& old_password) { |
| current_state_.reset(new AuthAttemptState(user_context, |
| false, // unlock |
| false, // online_complete |
| !IsKnownUser(user_context))); |
| RecoverEncryptedData(old_password); |
| } |
| |
| void CryptohomeAuthenticator::RecoverEncryptedData( |
| const std::string& old_password) { |
| migrate_attempted_ = true; |
| current_state_->ResetCryptohomeStatus(); |
| SystemSaltGetter::Get()->GetSystemSalt(base::Bind( |
| &Migrate, current_state_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this), true, old_password)); |
| } |
| |
| void CryptohomeAuthenticator::RemoveEncryptedData() { |
| remove_attempted_ = true; |
| current_state_->ResetCryptohomeStatus(); |
| task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&Remove, current_state_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this))); |
| } |
| |
| void CryptohomeAuthenticator::ResyncEncryptedData() { |
| resync_attempted_ = true; |
| current_state_->ResetCryptohomeStatus(); |
| task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&Remove, current_state_->AsWeakPtr(), |
| 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_->RunsTasksInCurrentSequence()); |
| bool create_if_nonexistent = false; |
| CryptohomeAuthenticator::AuthState state = ResolveState(); |
| VLOG(1) << "Resolved state to " << state << " (" << AuthStateToString(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::BindOnce(&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 their |
| // request, and the remove failed. |
| remove_user_data_on_failure_ = false; |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&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::BindOnce(&CryptohomeAuthenticator::OnAuthFailure, |
| this, delayed_login_failure_)); |
| break; |
| case CREATE_NEW: |
| create_if_nonexistent = true; |
| FALLTHROUGH; |
| case RECOVER_MOUNT: |
| current_state_->ResetCryptohomeStatus(); |
| StartMount(current_state_->AsWeakPtr(), |
| scoped_refptr<CryptohomeAuthenticator>(this), |
| false /*ephemeral*/, create_if_nonexistent); |
| break; |
| case NEED_OLD_PW: |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&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"; |
| FALLTHROUGH; |
| case UNLOCK: |
| VLOG(2) << "Unlock"; |
| FALLTHROUGH; |
| case ONLINE_LOGIN: |
| VLOG(2) << "Online login"; |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CryptohomeAuthenticator::OnAuthSuccess, this)); |
| break; |
| case GUEST_LOGIN: |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CryptohomeAuthenticator::OnOffTheRecordAuthSuccess, |
| this)); |
| break; |
| case KIOSK_ACCOUNT_LOGIN: |
| case PUBLIC_ACCOUNT_LOGIN: |
| current_state_->user_context.SetIsUsingOAuth(false); |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CryptohomeAuthenticator::OnAuthSuccess, this)); |
| break; |
| case SUPERVISED_USER_LOGIN: |
| current_state_->user_context.SetIsUsingOAuth(false); |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CryptohomeAuthenticator::OnAuthSuccess, this)); |
| break; |
| case LOGIN_FAILED: |
| current_state_->ResetCryptohomeStatus(); |
| task_runner_->PostTask( |
| FROM_HERE, base::BindOnce(&CryptohomeAuthenticator::OnAuthFailure, |
| this, current_state_->online_outcome())); |
| break; |
| case OWNER_REQUIRED: { |
| current_state_->ResetCryptohomeStatus(); |
| CryptohomeClient::Get()->UnmountEx( |
| cryptohome::UnmountRequest(), |
| base::BindOnce(&CryptohomeAuthenticator::OnUnmountEx, this)); |
| break; |
| } |
| case FAILED_OLD_ENCRYPTION: |
| case FAILED_PREVIOUS_MIGRATION_INCOMPLETE: |
| // In this case, we tried to create/mount cryptohome and failed |
| // because the file system is encrypted in old format. |
| // Chrome will show a screen which asks user to migrate the encryption. |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CryptohomeAuthenticator::OnOldEncryptionDetected, |
| this, state == FAILED_PREVIOUS_MIGRATION_INCOMPLETE)); |
| break; |
| case OFFLINE_NO_MOUNT: |
| task_runner_->PostTask( |
| FROM_HERE, |
| base::BindOnce(&CryptohomeAuthenticator::OnAuthFailure, this, |
| AuthFailure(AuthFailure::MISSING_CRYPTOHOME))); |
| break; |
| default: |
| NOTREACHED(); |
| break; |
| } |
| } |
| |
| CryptohomeAuthenticator::~CryptohomeAuthenticator() = default; |
| |
| CryptohomeAuthenticator::AuthState CryptohomeAuthenticator::ResolveState() { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| // 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_code() == cryptohome::MOUNT_ERROR_NONE && |
| current_state_->username_hash_valid()) { |
| state = ResolveCryptohomeSuccessState(); |
| } else { |
| state = ResolveCryptohomeFailureState(); |
| LOGIN_LOG(ERROR) << "Cryptohome failure: " |
| << "state(AuthState)=" << state |
| << ", code(cryptohome::MountError)=" |
| << current_state_->cryptohome_code(); |
| } |
| |
| 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_->RunsTasksInCurrentSequence()); |
| 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; |
| } |
| |
| if (current_state_->cryptohome_code() == |
| cryptohome::MOUNT_ERROR_OLD_ENCRYPTION) { |
| return FAILED_OLD_ENCRYPTION; |
| } |
| if (current_state_->cryptohome_code() == |
| cryptohome::MOUNT_ERROR_PREVIOUS_MIGRATION_INCOMPLETE) { |
| return FAILED_PREVIOUS_MIGRATION_INCOMPLETE; |
| } |
| |
| // 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 their 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; |
| } |
| } else if (current_state_->cryptohome_code() == |
| cryptohome::MOUNT_ERROR_USER_DOES_NOT_EXIST) { |
| // If we tried a mount but the user did not exist in the offline flow, |
| // surface this as an error. |
| return OFFLINE_NO_MOUNT; |
| } |
| |
| if (!current_state_->username_hash_valid()) |
| return FAILED_USERNAME_HASH; |
| |
| return FAILED_MOUNT; |
| } |
| |
| CryptohomeAuthenticator::AuthState |
| CryptohomeAuthenticator::ResolveCryptohomeSuccessState() { |
| DCHECK(task_runner_->RunsTasksInCurrentSequence()); |
| 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; |
| |
| const user_manager::UserType user_type = |
| current_state_->user_context.GetUserType(); |
| if (user_type == user_manager::USER_TYPE_GUEST) |
| return GUEST_LOGIN; |
| if (user_type == user_manager::USER_TYPE_PUBLIC_ACCOUNT) |
| return PUBLIC_ACCOUNT_LOGIN; |
| if (user_type == user_manager::USER_TYPE_KIOSK_APP) |
| return KIOSK_ACCOUNT_LOGIN; |
| if (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_->RunsTasksInCurrentSequence()); |
| 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 |