summaryrefslogtreecommitdiffstats
path: root/chromeos/login
diff options
context:
space:
mode:
authorantrim@chromium.org <antrim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-21 17:29:56 +0000
committerantrim@chromium.org <antrim@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2014-08-21 17:31:24 +0000
commit2bb3ea82ec57a7e44cddb3b596d23aa934920df2 (patch)
tree9fbeacfeae5b3965d8b56413dd8f138bc045475e /chromeos/login
parentada965b23e1b32f3faef25d2bca5c509c12d8a97 (diff)
downloadchromium_src-2bb3ea82ec57a7e44cddb3b596d23aa934920df2.zip
chromium_src-2bb3ea82ec57a7e44cddb3b596d23aa934920df2.tar.gz
chromium_src-2bb3ea82ec57a7e44cddb3b596d23aa934920df2.tar.bz2
Move ParallelAuthnticator to chromeos and rename it to CryptohomeAuthenticator
BUG=387613 R=nkostylev@chromium.org TBR=stevenjb@chromium.org Review URL: https://codereview.chromium.org/495563003 Cr-Commit-Position: refs/heads/master@{#291125} git-svn-id: svn://svn.chromium.org/chrome/trunk/src@291125 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chromeos/login')
-rw-r--r--chromeos/login/auth/cryptohome_authenticator.cc757
-rw-r--r--chromeos/login/auth/cryptohome_authenticator.h269
2 files changed, 1026 insertions, 0 deletions
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc
new file mode 100644
index 0000000..91dc5f7
--- /dev/null
+++ b/chromeos/login/auth/cryptohome_authenticator.cc
@@ -0,0 +1,757 @@
+// 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 "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/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 {
+
+// 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);
+}
+
+// Calls cryptohome's mount method.
+void Mount(AuthAttemptState* attempt,
+ scoped_refptr<CryptohomeAuthenticator> resolver,
+ int flags,
+ const std::string& system_salt) {
+ chromeos::LoginEventRecorder::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 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;
+
+ SystemSaltGetter::Get()->GetSystemSalt(
+ base::Bind(&Mount,
+ current_state_.get(),
+ scoped_refptr<CryptohomeAuthenticator>(this),
+ cryptohome::MOUNT_FLAGS_NONE));
+}
+
+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;
+
+ SystemSaltGetter::Get()->GetSystemSalt(
+ base::Bind(&Mount,
+ current_state_.get(),
+ scoped_refptr<CryptohomeAuthenticator>(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.
+ 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;
+ SystemSaltGetter::Get()->GetSystemSalt(
+ base::Bind(&Mount,
+ current_state_.get(),
+ scoped_refptr<CryptohomeAuthenticator>(this),
+ cryptohome::MOUNT_FLAGS_NONE));
+}
+
+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;
+ SystemSaltGetter::Get()->GetSystemSalt(
+ base::Bind(&Mount,
+ current_state_.get(),
+ scoped_refptr<CryptohomeAuthenticator>(this),
+ cryptohome::CREATE_IF_MISSING | cryptohome::ENSURE_EPHEMERAL));
+}
+
+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());
+ int mount_flags = cryptohome::MOUNT_FLAGS_NONE;
+ 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:
+ mount_flags |= cryptohome::CREATE_IF_MISSING;
+ case RECOVER_MOUNT:
+ current_state_->ResetCryptohomeStatus();
+ SystemSaltGetter::Get()->GetSystemSalt(
+ base::Bind(&Mount,
+ current_state_.get(),
+ scoped_refptr<CryptohomeAuthenticator>(this),
+ mount_flags));
+ 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
diff --git a/chromeos/login/auth/cryptohome_authenticator.h b/chromeos/login/auth/cryptohome_authenticator.h
new file mode 100644
index 0000000..d003b83
--- /dev/null
+++ b/chromeos/login/auth/cryptohome_authenticator.h
@@ -0,0 +1,269 @@
+// 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.
+
+#ifndef CHROMEOS_LOGIN_AUTH_CRYPTOHOME_AUTHENTICATOR_H_
+#define CHROMEOS_LOGIN_AUTH_CRYPTOHOME_AUTHENTICATOR_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+#include "base/compiler_specific.h"
+#include "base/gtest_prod_util.h"
+#include "base/memory/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "base/task_runner.h"
+#include "chromeos/chromeos_export.h"
+#include "chromeos/login/auth/auth_attempt_state.h"
+#include "chromeos/login/auth/auth_attempt_state_resolver.h"
+#include "chromeos/login/auth/authenticator.h"
+#include "chromeos/login/auth/test_attempt_state.h"
+#include "google_apis/gaia/gaia_auth_consumer.h"
+
+class AuthFailure;
+class Profile;
+
+namespace chromeos {
+
+class AuthStatusConsumer;
+
+// Authenticates a Chromium OS user against cryptohome.
+// Relies on the fact that online authentications has been already performed
+// (i.e. using_oauth_ is true).
+//
+// At a high, level, here's what happens:
+// AuthenticateToLogin() calls a Cryptohome's method to perform offline login.
+// Resultes are stored in a AuthAttemptState owned by CryptohomeAuthenticator
+// and then call Resolve(). Resolve() will attempt to
+// determine which AuthState we're in, based on the info at hand.
+// It then triggers further action based on the calculated AuthState; this
+// further action might include calling back the passed-in AuthStatusConsumer
+// to signal that login succeeded or failed, waiting for more outstanding
+// operations to complete, or triggering some more Cryptohome method calls.
+//
+// Typical flows
+// -------------
+// Add new user: CONTINUE > CONTINUE > CREATE_NEW > CONTINUE > ONLINE_LOGIN
+// Login as existing user: CONTINUE > OFFLINE_LOGIN
+// Login as existing user (failure): CONTINUE > FAILED_MOUNT
+// Change password detected:
+// GAIA online ok: CONTINUE > CONTINUE > NEED_OLD_PW
+// Recreate: CREATE_NEW > CONTINUE > ONLINE_LOGIN
+// Old password failure: NEED_OLD_PW
+// Old password ok: RECOVER_MOUNT > CONTINUE > ONLINE_LOGIN
+//
+class CHROMEOS_EXPORT CryptohomeAuthenticator
+ : public Authenticator,
+ public AuthAttemptStateResolver {
+ public:
+ enum AuthState {
+ CONTINUE = 0, // State indeterminate; try again with more info.
+ NO_MOUNT = 1, // Cryptohome doesn't exist yet.
+ FAILED_MOUNT = 2, // Failed to mount existing cryptohome.
+ FAILED_REMOVE = 3, // Failed to remove existing cryptohome.
+ FAILED_TMPFS = 4, // Failed to mount tmpfs for guest user.
+ FAILED_TPM = 5, // Failed to mount/create cryptohome, TPM error.
+ CREATE_NEW = 6, // Need to create cryptohome for a new user.
+ RECOVER_MOUNT = 7, // After RecoverEncryptedData, mount cryptohome.
+ POSSIBLE_PW_CHANGE = 8, // Offline login failed, user may have changed pw.
+ NEED_NEW_PW = 9, // Obsolete (ClientLogin): user changed pw,
+ // we have the old one.
+ NEED_OLD_PW = 10, // User changed pw, and we have the new one
+ // (GAIA auth is OK).
+ HAVE_NEW_PW = 11, // Obsolete (ClientLogin): We have verified new pw,
+ // time to migrate key.
+ OFFLINE_LOGIN = 12, // Login succeeded offline.
+ DEMO_LOGIN = 13, // Logged in as the demo user.
+ ONLINE_LOGIN = 14, // Offline and online login succeeded.
+ UNLOCK = 15, // Screen unlock succeeded.
+ ONLINE_FAILED = 16, // Obsolete (ClientLogin): Online login disallowed,
+ // but offline succeeded.
+ GUEST_LOGIN = 17, // Logged in guest mode.
+ PUBLIC_ACCOUNT_LOGIN = 18, // Logged into a public account.
+ SUPERVISED_USER_LOGIN = 19, // Logged in as a supervised user.
+ LOGIN_FAILED = 20, // Login denied.
+ OWNER_REQUIRED = 21, // Login is restricted to the owner only.
+ FAILED_USERNAME_HASH = 22, // Failed GetSanitizedUsername request.
+ KIOSK_ACCOUNT_LOGIN = 23, // Logged into a kiosk account.
+ REMOVED_DATA_AFTER_FAILURE = 24, // Successfully removed the user's
+ // cryptohome after a login failure.
+ };
+
+ CryptohomeAuthenticator(scoped_refptr<base::TaskRunner> task_runner,
+ AuthStatusConsumer* consumer);
+
+ // Authenticator overrides.
+ virtual void CompleteLogin(Profile* profile,
+ const UserContext& user_context) OVERRIDE;
+
+ // Given |user_context|, this method attempts to authenticate to your
+ // Chrome OS device. As soon as we have successfully mounted the encrypted
+ // home directory for the user, we will call consumer_->OnAuthSuccess()
+ // with the username.
+ // Upon failure to login consumer_->OnAuthFailure() is called
+ // with an error message.
+ //
+ // Uses |profile| when doing URL fetches.
+ virtual void AuthenticateToLogin(Profile* profile,
+ const UserContext& user_context) OVERRIDE;
+
+ // Given |user_context|, this method attempts to authenticate to the cached
+ // user_context. This will never contact the server even if it's online.
+ // The auth result is sent to AuthStatusConsumer in a same way as
+ // AuthenticateToLogin does.
+ virtual void AuthenticateToUnlock(const UserContext& user_context) OVERRIDE;
+
+ // Initiates supervised user login.
+ // Creates cryptohome if missing or mounts existing one and
+ // notifies consumer on the success/failure.
+ virtual void LoginAsSupervisedUser(const UserContext& user_context) OVERRIDE;
+
+ // Initiates retail mode login.
+ // Mounts tmpfs and notifies consumer on the success/failure.
+ virtual void LoginRetailMode() OVERRIDE;
+
+ // Initiates incognito ("browse without signing in") login.
+ // Mounts tmpfs and notifies consumer on the success/failure.
+ virtual void LoginOffTheRecord() OVERRIDE;
+
+ // Initiates login into a public session.
+ // Mounts an ephemeral cryptohome and notifies consumer on the
+ // success/failure.
+ virtual void LoginAsPublicSession(const UserContext& user_context) OVERRIDE;
+
+ // Initiates login into the kiosk mode account identified by |app_user_id|.
+ // Mounts an ephemeral guest cryptohome if |use_guest_mount| is |true|.
+ // Otherwise, mounts a public cryptohome, which will be ephemeral if the
+ // |DeviceEphemeralUsersEnabled| policy is enabled and non-ephemeral
+ // otherwise.
+ virtual void LoginAsKioskAccount(const std::string& app_user_id,
+ bool use_guest_mount) OVERRIDE;
+
+ // These methods must be called on the UI thread, as they make DBus calls
+ // and also call back to the login UI.
+ virtual void OnRetailModeAuthSuccess() OVERRIDE;
+ virtual void OnAuthSuccess() OVERRIDE;
+ virtual void OnAuthFailure(const AuthFailure& error) OVERRIDE;
+ virtual void RecoverEncryptedData(const std::string& old_password) OVERRIDE;
+ virtual void ResyncEncryptedData() OVERRIDE;
+
+ // AuthAttemptStateResolver overrides.
+ // Attempts to make a decision and call back |consumer_| based on
+ // the state we have gathered at the time of call. If a decision
+ // can't be made, defers until the next time this is called.
+ // When a decision is made, will call back to |consumer_| on the UI thread.
+ //
+ // Must be called on the UI thread.
+ virtual void Resolve() OVERRIDE;
+
+ void OnOffTheRecordAuthSuccess();
+ void OnPasswordChangeDetected();
+
+ protected:
+ virtual ~CryptohomeAuthenticator();
+
+ typedef base::Callback<void(bool is_owner)> IsOwnerCallback;
+
+ // Method to be implemented in child. Return |true| if user specified in
+ // |context| exists on device.
+ virtual bool IsKnownUser(const UserContext& context) = 0;
+
+ // Method to be implemented in child. Return |true| if device is running
+ // in safe mode.
+ virtual bool IsSafeMode() = 0;
+
+ // Method to be implemented in child. Have to call |callback| with boolean
+ // parameter that indicates if user in |context| can act as an owner in
+ // safe mode.
+ virtual void CheckSafeModeOwnership(const UserContext& context,
+ const IsOwnerCallback& callback) = 0;
+
+ private:
+ friend class CryptohomeAuthenticatorTest;
+ FRIEND_TEST_ALL_PREFIXES(CryptohomeAuthenticatorTest,
+ ResolveOwnerNeededDirectFailedMount);
+ FRIEND_TEST_ALL_PREFIXES(CryptohomeAuthenticatorTest,
+ ResolveOwnerNeededMount);
+ FRIEND_TEST_ALL_PREFIXES(CryptohomeAuthenticatorTest,
+ ResolveOwnerNeededFailedMount);
+
+ // Removes the cryptohome of the user.
+ void RemoveEncryptedData();
+
+ // Returns the AuthState we're in, given the status info we have at
+ // the time of call.
+ // Must be called on the IO thread.
+ AuthState ResolveState();
+
+ // Helper for ResolveState().
+ // Given that some cryptohome operation has failed, determine which of the
+ // possible failure states we're in.
+ // Must be called on the IO thread.
+ AuthState ResolveCryptohomeFailureState();
+
+ // Helper for ResolveState().
+ // Given that some cryptohome operation has succeeded, determine which of
+ // the possible states we're in.
+ // Must be called on the IO thread.
+ AuthState ResolveCryptohomeSuccessState();
+
+ // Helper for ResolveState().
+ // Given that some online auth operation has succeeded, determine which of
+ // the possible success states we're in.
+ // Must be called on the IO thread.
+ AuthState ResolveOnlineSuccessState(AuthState offline_state);
+
+ // Used for testing.
+ void set_attempt_state(TestAttemptState* new_state) { // takes ownership.
+ current_state_.reset(new_state);
+ }
+
+ // Used for testing to set the expected state of an owner check.
+ void SetOwnerState(bool owner_check_finished, bool check_result);
+
+ // checks if the current mounted home contains the owner case and either
+ // continues or fails the log-in. Used for policy lost mitigation "safe-mode".
+ // Returns true if the owner check has been successful or if it is not needed.
+ bool VerifyOwner();
+
+ // Handles completion of the ownership check and continues login.
+ void OnOwnershipChecked(bool is_owner);
+
+ // Signal login completion status for cases when a new user is added via
+ // an external authentication provider (i.e. GAIA extension).
+ void ResolveLoginCompletionStatus();
+
+ scoped_refptr<base::TaskRunner> task_runner_;
+
+ scoped_ptr<AuthAttemptState> current_state_;
+ bool migrate_attempted_;
+ bool remove_attempted_;
+ bool resync_attempted_;
+ bool ephemeral_mount_attempted_;
+ bool check_key_attempted_;
+
+ // When the user has changed her password, but gives us the old one, we will
+ // be able to mount her cryptohome, but online authentication will fail.
+ // This allows us to present the same behavior to the caller, regardless
+ // of the order in which we receive these results.
+ bool already_reported_success_;
+ base::Lock success_lock_; // A lock around |already_reported_success_|.
+
+ // Flags signaling whether the owner verification has been done and the result
+ // of it.
+ bool owner_is_verified_;
+ bool user_can_login_;
+
+ // Flag indicating to delete the user's cryptohome the login fails.
+ bool remove_user_data_on_failure_;
+
+ // When |remove_user_data_on_failure_| is set, we delay calling
+ // consumer_->OnAuthFailure() until we removed the user cryptohome.
+ const AuthFailure* delayed_login_failure_;
+
+ DISALLOW_COPY_AND_ASSIGN(CryptohomeAuthenticator);
+};
+
+} // namespace chromeos
+
+#endif // CHROMEOS_LOGIN_AUTH_CRYPTOHOME_AUTHENTICATOR_H_