| Index: chrome/browser/chromeos/login/parallel_authenticator.cc
|
| diff --git a/chrome/browser/chromeos/login/parallel_authenticator.cc b/chrome/browser/chromeos/login/parallel_authenticator.cc
|
| new file mode 100644
|
| index 0000000000000000000000000000000000000000..45192173049135644c2bbc1e48ec92835a42898d
|
| --- /dev/null
|
| +++ b/chrome/browser/chromeos/login/parallel_authenticator.cc
|
| @@ -0,0 +1,635 @@
|
| +// Copyright (c) 2010 The Chromium Authors. All rights reserved.
|
| +// Use of this source code is governed by a BSD-style license that can be
|
| +// found in the LICENSE file.
|
| +
|
| +#include "chrome/browser/chromeos/login/parallel_authenticator.h"
|
| +
|
| +#include <string>
|
| +#include <vector>
|
| +
|
| +#include "base/file_path.h"
|
| +#include "base/file_util.h"
|
| +#include "base/lock.h"
|
| +#include "base/logging.h"
|
| +#include "base/path_service.h"
|
| +#include "base/sha2.h"
|
| +#include "base/string_util.h"
|
| +#include "base/third_party/nss/blapi.h"
|
| +#include "base/third_party/nss/sha256.h"
|
| +#include "chrome/browser/browser_process.h"
|
| +#include "chrome/browser/chrome_thread.h"
|
| +#include "chrome/browser/chromeos/cros/cryptohome_library.h"
|
| +#include "chrome/browser/chromeos/login/auth_response_handler.h"
|
| +#include "chrome/browser/chromeos/login/authentication_notification_details.h"
|
| +#include "chrome/browser/chromeos/login/login_status_consumer.h"
|
| +#include "chrome/browser/chromeos/login/ownership_service.h"
|
| +#include "chrome/browser/profile.h"
|
| +#include "chrome/browser/profile_manager.h"
|
| +#include "chrome/common/chrome_paths.h"
|
| +#include "chrome/common/net/gaia/gaia_authenticator2.h"
|
| +#include "chrome/common/net/gaia/gaia_constants.h"
|
| +#include "chrome/common/notification_service.h"
|
| +#include "net/base/load_flags.h"
|
| +#include "net/base/net_errors.h"
|
| +#include "net/url_request/url_request_status.h"
|
| +#include "third_party/libjingle/source/talk/base/urlencode.h"
|
| +
|
| +using base::Time;
|
| +using base::TimeDelta;
|
| +using file_util::GetFileSize;
|
| +using file_util::PathExists;
|
| +using file_util::ReadFile;
|
| +using file_util::ReadFileToString;
|
| +
|
| +namespace chromeos {
|
| +
|
| +// static
|
| +const char ParallelAuthenticator::kLocalaccountFile[] = "localaccount";
|
| +
|
| +// static
|
| +const int ParallelAuthenticator::kClientLoginTimeoutMs = 10000;
|
| +// static
|
| +const int ParallelAuthenticator::kLocalaccountRetryIntervalMs = 20;
|
| +
|
| +const int kPassHashLen = 32;
|
| +
|
| +ParallelAuthenticator::ParallelAuthenticator(LoginStatusConsumer* consumer)
|
| + : Authenticator(consumer),
|
| + already_reported_success_(false),
|
| + checked_for_localaccount_(false) {
|
| + CHECK(chromeos::CrosLibrary::Get()->EnsureLoaded());
|
| + // If not already owned, this is a no-op. If it is, this loads the owner's
|
| + // public key off of disk.
|
| + OwnershipService::GetSharedInstance()->StartLoadOwnerKeyAttempt();
|
| +}
|
| +
|
| +ParallelAuthenticator::~ParallelAuthenticator() {}
|
| +
|
| +bool ParallelAuthenticator::AuthenticateToLogin(
|
| + Profile* profile,
|
| + const std::string& username,
|
| + const std::string& password,
|
| + const std::string& login_token,
|
| + const std::string& login_captcha) {
|
| + current_state_.reset(
|
| + new AuthAttemptState(Authenticator::Canonicalize(username),
|
| + password,
|
| + HashPassword(password),
|
| + login_token,
|
| + login_captcha));
|
| + mounter_ = CryptohomeOp::CreateMountAttempt(current_state_.get(),
|
| + this,
|
| + false /* don't create */);
|
| + current_online_ = new OnlineAttempt(current_state_.get(), this);
|
| + // Sadly, this MUST be on the UI thread due to sending DBus traffic :-/
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(mounter_.get(), &CryptohomeOp::Initiate));
|
| + ChromeThread::PostTask(
|
| + ChromeThread::IO, FROM_HERE,
|
| + NewRunnableMethod(current_online_.get(),
|
| + &OnlineAttempt::Initiate,
|
| + profile));
|
| + ChromeThread::PostTask(
|
| + ChromeThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &ParallelAuthenticator::LoadLocalaccount,
|
| + std::string(kLocalaccountFile)));
|
| + return true;
|
| +}
|
| +
|
| +bool ParallelAuthenticator::AuthenticateToUnlock(const std::string& username,
|
| + const std::string& password) {
|
| + current_state_.reset(
|
| + new AuthAttemptState(Authenticator::Canonicalize(username),
|
| + HashPassword(password)));
|
| + ChromeThread::PostTask(
|
| + ChromeThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &ParallelAuthenticator::LoadLocalaccount,
|
| + std::string(kLocalaccountFile)));
|
| + key_checker_ = CryptohomeOp::CreateCheckKeyAttempt(current_state_.get(),
|
| + this);
|
| + // Sadly, this MUST be on the UI thread due to sending DBus traffic :-/
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(key_checker_.get(), &CryptohomeOp::Initiate));
|
| + return true;
|
| +}
|
| +
|
| +void ParallelAuthenticator::LoginOffTheRecord() {
|
| + current_state_.reset(new AuthAttemptState("", "", "", "", ""));
|
| + guest_mounter_ =
|
| + CryptohomeOp::CreateMountGuestAttempt(current_state_.get(), this);
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + guest_mounter_->Initiate();
|
| +}
|
| +
|
| +void ParallelAuthenticator::OnLoginSuccess(
|
| + const GaiaAuthConsumer::ClientLoginResult& credentials,
|
| + bool request_pending) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + LOG(INFO) << "Online login success";
|
| + // Send notification of success
|
| + AuthenticationNotificationDetails details(true);
|
| + NotificationService::current()->Notify(
|
| + NotificationType::LOGIN_AUTHENTICATION,
|
| + NotificationService::AllSources(),
|
| + Details<AuthenticationNotificationDetails>(&details));
|
| + {
|
| + AutoLock for_this_block(success_lock_);
|
| + already_reported_success_ = true;
|
| + }
|
| + consumer_->OnLoginSuccess(current_state_->username,
|
| + credentials,
|
| + request_pending);
|
| +}
|
| +
|
| +void ParallelAuthenticator::OnOffTheRecordLoginSuccess() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + // Send notification of success
|
| + AuthenticationNotificationDetails details(true);
|
| + NotificationService::current()->Notify(
|
| + NotificationType::LOGIN_AUTHENTICATION,
|
| + NotificationService::AllSources(),
|
| + Details<AuthenticationNotificationDetails>(&details));
|
| + consumer_->OnOffTheRecordLoginSuccess();
|
| +}
|
| +
|
| +void ParallelAuthenticator::OnPasswordChangeDetected(
|
| + const GaiaAuthConsumer::ClientLoginResult& credentials) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + consumer_->OnPasswordChangeDetected(credentials);
|
| +}
|
| +
|
| +void ParallelAuthenticator::CheckLocalaccount(const LoginFailure& error) {
|
| + {
|
| + AutoLock for_this_block(localaccount_lock_);
|
| + LOG(INFO) << "Checking localaccount";
|
| + if (!checked_for_localaccount_) {
|
| + ChromeThread::PostDelayedTask(
|
| + ChromeThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &ParallelAuthenticator::CheckLocalaccount,
|
| + error),
|
| + kLocalaccountRetryIntervalMs);
|
| + return;
|
| + }
|
| + }
|
| +
|
| + if (!localaccount_.empty() && localaccount_ == current_state_->username) {
|
| + // Success. Go mount a tmpfs for the profile, if necessary.
|
| + if (!current_state_->unlock) {
|
| + guest_mounter_ =
|
| + CryptohomeOp::CreateMountGuestAttempt(current_state_.get(), this);
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(guest_mounter_.get(), &CryptohomeOp::Initiate));
|
| + } else {
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this, &ParallelAuthenticator::OnLoginSuccess,
|
| + GaiaAuthConsumer::ClientLoginResult(), false));
|
| + }
|
| + } else {
|
| + // Not the localaccount. Fail, passing along cached error info.
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure, error));
|
| + }
|
| +}
|
| +
|
| +void ParallelAuthenticator::OnLoginFailure(const LoginFailure& error) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::UI));
|
| + // Send notification of failure
|
| + AuthenticationNotificationDetails details(false);
|
| + NotificationService::current()->Notify(
|
| + NotificationType::LOGIN_AUTHENTICATION,
|
| + NotificationService::AllSources(),
|
| + Details<AuthenticationNotificationDetails>(&details));
|
| + LOG(WARNING) << "Login failed: " << error.GetErrorString();
|
| + consumer_->OnLoginFailure(error);
|
| +}
|
| +
|
| +void ParallelAuthenticator::RecoverEncryptedData(
|
| + const std::string& old_password,
|
| + const GaiaAuthConsumer::ClientLoginResult& credentials) {
|
| + std::string old_hash = HashPassword(old_password);
|
| + key_migrator_ = CryptohomeOp::CreateMigrateAttempt(current_state_.get(),
|
| + this,
|
| + true,
|
| + old_hash);
|
| + ChromeThread::PostTask(
|
| + ChromeThread::IO, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &ParallelAuthenticator::ResyncRecoverHelper,
|
| + key_migrator_.get()));
|
| +}
|
| +
|
| +void ParallelAuthenticator::ResyncEncryptedData(
|
| + const GaiaAuthConsumer::ClientLoginResult& credentials) {
|
| + data_remover_ =
|
| + CryptohomeOp::CreateRemoveAttempt(current_state_.get(), this);
|
| + ChromeThread::PostTask(
|
| + ChromeThread::IO, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &ParallelAuthenticator::ResyncRecoverHelper,
|
| + data_remover_.get()));
|
| +}
|
| +
|
| +void ParallelAuthenticator::ResyncRecoverHelper(CryptohomeOp* to_initiate) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
|
| + current_state_->ResetCryptohomeStatus();
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(to_initiate, &CryptohomeOp::Initiate));
|
| +}
|
| +
|
| +void ParallelAuthenticator::RetryAuth(Profile* profile,
|
| + const std::string& username,
|
| + const std::string& password,
|
| + const std::string& login_token,
|
| + const std::string& login_captcha) {
|
| + reauth_state_.reset(
|
| + new AuthAttemptState(Authenticator::Canonicalize(username),
|
| + password,
|
| + HashPassword(password),
|
| + login_token,
|
| + login_captcha));
|
| + current_online_ = new OnlineAttempt(reauth_state_.get(), this);
|
| + ChromeThread::PostTask(
|
| + ChromeThread::IO, FROM_HERE,
|
| + NewRunnableMethod(current_online_.get(),
|
| + &OnlineAttempt::Initiate,
|
| + profile));
|
| +}
|
| +
|
| +void ParallelAuthenticator::Resolve() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
|
| + bool request_pending = false;
|
| + bool create = false;
|
| + switch (ResolveState()) {
|
| + 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.
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(
|
| + this,
|
| + &ParallelAuthenticator::OnLoginFailure,
|
| + LoginFailure(LoginFailure::COULD_NOT_MOUNT_CRYPTOHOME)));
|
| + break;
|
| + case FAILED_REMOVE:
|
| + // In this case, we tried to remove the user's old cryptohome at her
|
| + // request, and the remove failed.
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure,
|
| + LoginFailure(LoginFailure::DATA_REMOVAL_FAILED)));
|
| + break;
|
| + case FAILED_TMPFS:
|
| + // In this case, we tried to mount a tmpfs for BWSI or the localaccount
|
| + // user and failed.
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure,
|
| + LoginFailure(LoginFailure::COULD_NOT_MOUNT_TMPFS)));
|
| + break;
|
| + case CREATE_NEW:
|
| + create = true;
|
| + case RECOVER_MOUNT:
|
| + current_state_->ResetCryptohomeStatus();
|
| + mounter_ = CryptohomeOp::CreateMountAttempt(current_state_.get(),
|
| + this,
|
| + create);
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(mounter_.get(), &CryptohomeOp::Initiate));
|
| + break;
|
| + case NEED_OLD_PW:
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this,
|
| + &ParallelAuthenticator::OnPasswordChangeDetected,
|
| + current_state_->credentials()));
|
| + break;
|
| + case ONLINE_FAILED:
|
| + // In this case, we know online login was rejected because the account
|
| + // is disabled or something similarly fatal. Sending the user through
|
| + // the same path they get when their password is rejected is cleaner
|
| + // for now.
|
| + // TODO(cmasone): optimize this so that we don't send the user through
|
| + // the 'changed password' path when we know doing so won't succeed.
|
| + case NEED_NEW_PW:
|
| + {
|
| + AutoLock for_this_block(success_lock_);
|
| + if (!already_reported_success_) {
|
| + // This allows us to present the same behavior for "online:
|
| + // fail, offline: ok", regardless of the order in which we
|
| + // receive the results. There will be cases in which we get
|
| + // the online failure some time after the offline success,
|
| + // so we just force all cases in this category to present like this:
|
| + // OnLoginSuccess(..., ..., true) -> OnLoginFailure().
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this, &ParallelAuthenticator::OnLoginSuccess,
|
| + current_state_->credentials(), true));
|
| + }
|
| + }
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this, &ParallelAuthenticator::OnLoginFailure,
|
| + current_state_->online_outcome()));
|
| + break;
|
| + case HAVE_NEW_PW:
|
| + key_migrator_ =
|
| + CryptohomeOp::CreateMigrateAttempt(reauth_state_.get(),
|
| + this,
|
| + false,
|
| + reauth_state_->ascii_hash);
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(key_migrator_.get(), &CryptohomeOp::Initiate));
|
| + break;
|
| + case OFFLINE_LOGIN:
|
| + request_pending = !current_state_->online_complete();
|
| + // Fall through.
|
| + case UNLOCK:
|
| + // Fall through.
|
| + case ONLINE_LOGIN:
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(this, &ParallelAuthenticator::OnLoginSuccess,
|
| + current_state_->credentials(), request_pending));
|
| + break;
|
| + case LOCAL_LOGIN:
|
| + ChromeThread::PostTask(
|
| + ChromeThread::UI, FROM_HERE,
|
| + NewRunnableMethod(
|
| + this,
|
| + &ParallelAuthenticator::OnOffTheRecordLoginSuccess));
|
| + break;
|
| + case LOGIN_FAILED:
|
| + current_state_->ResetCryptohomeStatus();
|
| + ChromeThread::PostTask(
|
| + ChromeThread::FILE, FROM_HERE,
|
| + NewRunnableMethod(this, &ParallelAuthenticator::CheckLocalaccount,
|
| + current_state_->online_outcome()));
|
| + break;
|
| + default:
|
| + NOTREACHED();
|
| + break;
|
| + }
|
| +}
|
| +
|
| +ParallelAuthenticator::AuthState ParallelAuthenticator::ResolveState() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
|
| + // If we haven't mounted the user's home dir yet, we can't be done.
|
| + // We never get past here if a cryptohome op is still pending.
|
| + // This is an important invariant.
|
| + if (!current_state_->cryptohome_complete())
|
| + return CONTINUE;
|
| +
|
| + AuthState state = (reauth_state_.get() ? ResolveReauthState() : CONTINUE);
|
| + if (state != CONTINUE)
|
| + return state;
|
| +
|
| + if (current_state_->cryptohome_outcome())
|
| + state = ResolveCryptohomeSuccessState();
|
| + else
|
| + state = ResolveCryptohomeFailureState();
|
| +
|
| + DCHECK(current_state_->cryptohome_complete()); // Ensure invariant holds.
|
| + key_migrator_ = NULL;
|
| + data_remover_ = NULL;
|
| + guest_mounter_ = NULL;
|
| + key_checker_ = NULL;
|
| +
|
| + if (state != POSSIBLE_PW_CHANGE &&
|
| + state != NO_MOUNT &&
|
| + state != OFFLINE_LOGIN)
|
| + return state;
|
| +
|
| + if (current_state_->online_complete()) {
|
| + if (current_state_->online_outcome().reason() == LoginFailure::NONE) {
|
| + // Online attempt succeeded as well, so combine the results.
|
| + return ResolveOnlineSuccessState(state);
|
| + } else {
|
| + // Online login attempt was rejected or failed to occur.
|
| + return ResolveOnlineFailureState(state);
|
| + }
|
| + }
|
| + // if online isn't complete yet, just return the offline result.
|
| + return state;
|
| +}
|
| +
|
| +ParallelAuthenticator::AuthState
|
| +ParallelAuthenticator::ResolveReauthState() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
|
| + if (reauth_state_->cryptohome_complete()) {
|
| + if (!reauth_state_->cryptohome_outcome()) {
|
| + // If we've tried to migrate and failed, log the error and just wait
|
| + // til next time the user logs in to migrate their cryptohome key.
|
| + LOG(ERROR) << "Failed to migrate cryptohome key: "
|
| + << reauth_state_->cryptohome_code();
|
| + }
|
| + reauth_state_.reset(NULL);
|
| + return ONLINE_LOGIN;
|
| + }
|
| + // Haven't tried the migrate yet, must be processing the online auth attempt.
|
| + if (!reauth_state_->online_complete()) {
|
| + NOTREACHED(); // Shouldn't be here at all, if online reauth isn't done!
|
| + return CONTINUE;
|
| + }
|
| + if (reauth_state_->online_outcome().reason() == LoginFailure::NONE)
|
| + return HAVE_NEW_PW;
|
| + else
|
| + return NEED_NEW_PW;
|
| +}
|
| +
|
| +ParallelAuthenticator::AuthState
|
| +ParallelAuthenticator::ResolveCryptohomeFailureState() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
|
| + if (data_remover_.get()) {
|
| + return FAILED_REMOVE;
|
| + } else if (guest_mounter_.get()) {
|
| + return FAILED_TMPFS;
|
| + } else if (key_migrator_.get()) {
|
| + return NEED_OLD_PW;
|
| + } else if (key_checker_.get()) {
|
| + return LOGIN_FAILED;
|
| + } else if (current_state_->cryptohome_code() ==
|
| + chromeos::kCryptohomeMountErrorKeyFailure) {
|
| + // 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;
|
| + } else if (current_state_->cryptohome_code() ==
|
| + chromeos::kCryptohomeMountErrorUserDoesNotExist) {
|
| + // 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 {
|
| + return FAILED_MOUNT;
|
| + }
|
| +}
|
| +
|
| +ParallelAuthenticator::AuthState
|
| +ParallelAuthenticator::ResolveCryptohomeSuccessState() {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
|
| + if (data_remover_.get()) {
|
| + return CREATE_NEW;
|
| + } else if (guest_mounter_.get()) {
|
| + return LOCAL_LOGIN;
|
| + } else if (key_migrator_.get()) {
|
| + return RECOVER_MOUNT;
|
| + } else if (key_checker_.get()) {
|
| + return UNLOCK;
|
| + } else {
|
| + return OFFLINE_LOGIN;
|
| + }
|
| +}
|
| +
|
| +ParallelAuthenticator::AuthState
|
| +ParallelAuthenticator::ResolveOnlineFailureState(
|
| + ParallelAuthenticator::AuthState offline_state) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
|
| + if (offline_state == OFFLINE_LOGIN) {
|
| + if (current_state_->online_outcome().error().state() ==
|
| + GoogleServiceAuthError::CONNECTION_FAILED) {
|
| + // Couldn't do an online check, so just go with the offline result.
|
| + return OFFLINE_LOGIN;
|
| + }
|
| + // Otherwise, online login was rejected!
|
| + if (current_state_->online_outcome().error().state() ==
|
| + GoogleServiceAuthError::INVALID_GAIA_CREDENTIALS) {
|
| + return NEED_NEW_PW;
|
| + } else {
|
| + return ONLINE_FAILED;
|
| + }
|
| + }
|
| + return LOGIN_FAILED;
|
| +}
|
| +
|
| +ParallelAuthenticator::AuthState
|
| +ParallelAuthenticator::ResolveOnlineSuccessState(
|
| + ParallelAuthenticator::AuthState offline_state) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::IO));
|
| + switch (offline_state) {
|
| + case POSSIBLE_PW_CHANGE:
|
| + return NEED_OLD_PW;
|
| + case NO_MOUNT:
|
| + return CREATE_NEW;
|
| + case OFFLINE_LOGIN:
|
| + return ONLINE_LOGIN;
|
| + default:
|
| + NOTREACHED();
|
| + return offline_state;
|
| + }
|
| +}
|
| +
|
| +void ParallelAuthenticator::LoadSystemSalt() {
|
| + if (!system_salt_.empty())
|
| + return;
|
| + system_salt_ = CrosLibrary::Get()->GetCryptohomeLibrary()->GetSystemSalt();
|
| + CHECK(!system_salt_.empty());
|
| + CHECK_EQ(system_salt_.size() % 2, 0U);
|
| +}
|
| +
|
| +void ParallelAuthenticator::LoadLocalaccount(const std::string& filename) {
|
| + DCHECK(ChromeThread::CurrentlyOn(ChromeThread::FILE));
|
| + {
|
| + AutoLock for_this_block(localaccount_lock_);
|
| + if (checked_for_localaccount_)
|
| + return;
|
| + }
|
| + FilePath localaccount_file;
|
| + std::string localaccount;
|
| + if (PathService::Get(base::DIR_EXE, &localaccount_file)) {
|
| + localaccount_file = localaccount_file.Append(filename);
|
| + LOG(INFO) << "looking for localaccount in " << localaccount_file.value();
|
| +
|
| + ReadFileToString(localaccount_file, &localaccount);
|
| + TrimWhitespaceASCII(localaccount, TRIM_TRAILING, &localaccount);
|
| + LOG(INFO) << "Loading localaccount: " << localaccount;
|
| + } else {
|
| + LOG(INFO) << "Assuming no localaccount";
|
| + }
|
| + SetLocalaccount(localaccount);
|
| +}
|
| +
|
| +void ParallelAuthenticator::SetLocalaccount(const std::string& new_name) {
|
| + localaccount_ = new_name;
|
| + { // extra braces for clarity about AutoLock scope.
|
| + AutoLock for_this_block(localaccount_lock_);
|
| + checked_for_localaccount_ = true;
|
| + }
|
| +}
|
| +
|
| +
|
| +std::string ParallelAuthenticator::HashPassword(const std::string& password) {
|
| + // Get salt, ascii encode, update sha with that, then update with ascii
|
| + // of password, then end.
|
| + std::string ascii_salt = SaltAsAscii();
|
| + unsigned char passhash_buf[kPassHashLen];
|
| + char ascii_buf[kPassHashLen + 1];
|
| +
|
| + // Hash salt and password
|
| + SHA256Context ctx;
|
| + SHA256_Begin(&ctx);
|
| + SHA256_Update(&ctx,
|
| + reinterpret_cast<const unsigned char*>(ascii_salt.data()),
|
| + static_cast<unsigned int>(ascii_salt.length()));
|
| + SHA256_Update(&ctx,
|
| + reinterpret_cast<const unsigned char*>(password.data()),
|
| + static_cast<unsigned int>(password.length()));
|
| + SHA256_End(&ctx,
|
| + passhash_buf,
|
| + NULL,
|
| + static_cast<unsigned int>(sizeof(passhash_buf)));
|
| +
|
| + std::vector<unsigned char> passhash(passhash_buf,
|
| + passhash_buf + sizeof(passhash_buf));
|
| + BinaryToHex(passhash,
|
| + passhash.size() / 2, // only want top half, at least for now.
|
| + ascii_buf,
|
| + sizeof(ascii_buf));
|
| + return std::string(ascii_buf, sizeof(ascii_buf) - 1);
|
| +}
|
| +
|
| +std::string ParallelAuthenticator::SaltAsAscii() {
|
| + LoadSystemSalt(); // no-op if it's already loaded.
|
| + unsigned int salt_len = system_salt_.size();
|
| + char ascii_salt[2 * salt_len + 1];
|
| + if (ParallelAuthenticator::BinaryToHex(system_salt_,
|
| + salt_len,
|
| + ascii_salt,
|
| + sizeof(ascii_salt))) {
|
| + return std::string(ascii_salt, sizeof(ascii_salt) - 1);
|
| + } else {
|
| + return std::string();
|
| + }
|
| +}
|
| +
|
| +// static
|
| +bool ParallelAuthenticator::BinaryToHex(
|
| + const std::vector<unsigned char>& binary,
|
| + const unsigned int binary_len,
|
| + char* hex_string,
|
| + const unsigned int len) {
|
| + if (len < 2*binary_len)
|
| + return false;
|
| + memset(hex_string, 0, len);
|
| + for (uint i = 0, j = 0; i < binary_len; i++, j+=2)
|
| + snprintf(hex_string + j, len - j, "%02x", binary[i]);
|
| + return true;
|
| +}
|
| +
|
| +} // namespace chromeos
|
|
|