blob: 5fae224d67b89b14706c3cabbfcf55078123aa20 [file] [log] [blame]
// Copyright (c) 2012 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/managed/managed_user_authenticator.h"
#include "base/bind.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "chrome/browser/chromeos/boot_times_loader.h"
#include "chrome/browser/chromeos/login/parallel_authenticator.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 "content/public/browser/browser_thread.h"
#include "crypto/sha2.h"
#include "google_apis/gaia/gaia_auth_util.h"
#include "third_party/cros_system_api/dbus/service_constants.h"
using content::BrowserThread;
namespace chromeos {
namespace {
// Records status and calls resolver->Resolve().
void TriggerResolve(ManagedUserAuthenticator::AuthAttempt* attempt,
scoped_refptr<ManagedUserAuthenticator> resolver,
bool success,
cryptohome::MountError return_code) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
attempt->RecordCryptohomeStatus(success, return_code);
resolver->Resolve();
}
// Records status and calls resolver->Resolve().
void TriggerResolveResult(ManagedUserAuthenticator::AuthAttempt* attempt,
scoped_refptr<ManagedUserAuthenticator> resolver,
bool success,
const std::string& result) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
attempt->RecordHash(result);
resolver->Resolve();
}
// Calls TriggerResolve while adding login time marker.
void TriggerResolveWithLoginTimeMarker(
const std::string& marker_name,
ManagedUserAuthenticator::AuthAttempt* attempt,
scoped_refptr<ManagedUserAuthenticator> resolver,
bool success,
cryptohome::MountError return_code) {
chromeos::BootTimesLoader::Get()->AddLoginTimeMarker(marker_name, false);
TriggerResolve(attempt, resolver, success, return_code);
}
// Calls cryptohome's mount method.
void Mount(ManagedUserAuthenticator::AuthAttempt* attempt,
scoped_refptr<ManagedUserAuthenticator> resolver,
int flags,
const std::string& system_salt) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
chromeos::BootTimesLoader::Get()->AddLoginTimeMarker(
"CryptohomeMount-LMU-Start", false);
cryptohome::AsyncMethodCaller::GetInstance()->AsyncMount(
attempt->username,
ParallelAuthenticator::HashPassword(attempt->password, system_salt),
flags,
base::Bind(&TriggerResolveWithLoginTimeMarker,
"CryptohomeMount-LMU-End",
attempt,
resolver));
cryptohome::AsyncMethodCaller::GetInstance()->AsyncGetSanitizedUsername(
attempt->username,
base::Bind(&TriggerResolveResult, attempt, resolver));
}
// Calls cryptohome's addKey method.
void AddKey(ManagedUserAuthenticator::AuthAttempt* attempt,
scoped_refptr<ManagedUserAuthenticator> resolver,
const std::string& master_key,
const std::string& system_salt) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
chromeos::BootTimesLoader::Get()->AddLoginTimeMarker(
"CryptohomeAddKey-LMU-Start", false);
cryptohome::AsyncMethodCaller::GetInstance()->AsyncAddKey(
attempt->username,
ParallelAuthenticator::HashPassword(attempt->password, system_salt),
ParallelAuthenticator::HashPassword(master_key, system_salt),
base::Bind(&TriggerResolveWithLoginTimeMarker,
"CryptohomeAddKey-LMU-End",
attempt,
resolver));
}
} // namespace
ManagedUserAuthenticator::ManagedUserAuthenticator(AuthStatusConsumer* consumer)
: consumer_(consumer) {}
void ManagedUserAuthenticator::AuthenticateToMount(
const std::string& username,
const std::string& password) {
std::string canonicalized = gaia::CanonicalizeEmail(username);
current_state_.reset(new ManagedUserAuthenticator::AuthAttempt(
canonicalized, password, false));
SystemSaltGetter::Get()->GetSystemSalt(
base::Bind(&Mount,
current_state_.get(),
scoped_refptr<ManagedUserAuthenticator>(this),
cryptohome::MOUNT_FLAGS_NONE));
}
void ManagedUserAuthenticator::AuthenticateToCreate(
const std::string& username,
const std::string& password) {
std::string canonicalized = gaia::CanonicalizeEmail(username);
current_state_.reset(new ManagedUserAuthenticator::AuthAttempt(
canonicalized, password, false));
SystemSaltGetter::Get()->GetSystemSalt(
base::Bind(&Mount,
current_state_.get(),
scoped_refptr<ManagedUserAuthenticator>(this),
cryptohome::CREATE_IF_MISSING));
}
void ManagedUserAuthenticator::AddMasterKey(
const std::string& username,
const std::string& password,
const std::string& master_key) {
std::string canonicalized = gaia::CanonicalizeEmail(username);
current_state_.reset(new ManagedUserAuthenticator::AuthAttempt(
canonicalized, password, true));
SystemSaltGetter::Get()->GetSystemSalt(
base::Bind(&AddKey,
current_state_.get(),
scoped_refptr<ManagedUserAuthenticator>(this),
master_key));
}
void ManagedUserAuthenticator::OnAuthenticationSuccess(
const std::string& mount_hash,
bool add_key) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
VLOG(1) << "Locally managed user authentication success";
if (consumer_) {
if (add_key)
consumer_->OnAddKeySuccess();
else
consumer_->OnMountSuccess(mount_hash);
}
}
void ManagedUserAuthenticator::OnAuthenticationFailure(
ManagedUserAuthenticator::AuthState state) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
LOG(WARNING) << "Locally managed user authentication failure";
if (consumer_)
consumer_->OnAuthenticationFailure(state);
}
void ManagedUserAuthenticator::Resolve() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
ManagedUserAuthenticator::AuthState state = ResolveState();
VLOG(1) << "Resolved state to: " << state;
switch (state) {
case CONTINUE:
// These are intermediate states; we need more info from a request that
// is still pending.
break;
case FAILED_MOUNT:
// In this case, whether login succeeded or not, we can't log
// the user in because their data is horked. So, override with
// the appropriate failure.
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(
&ManagedUserAuthenticator::OnAuthenticationFailure, this, state));
break;
case NO_MOUNT:
// In this case, whether login succeeded or not, we can't log
// the user in because no data exist. So, override with
// the appropriate failure.
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(
&ManagedUserAuthenticator::OnAuthenticationFailure, this, state));
break;
case FAILED_TPM:
// In this case, we tried to create/mount cryptohome and failed
// because of the critical TPM error.
// Chrome will notify user and request reboot.
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(
&ManagedUserAuthenticator::OnAuthenticationFailure, this, state));
break;
case SUCCESS:
VLOG(2) << "Locally managed user login";
BrowserThread::PostTask(
BrowserThread::UI,
FROM_HERE,
base::Bind(&ManagedUserAuthenticator::OnAuthenticationSuccess,
this,
current_state_->hash(),
current_state_->add_key));
break;
default:
NOTREACHED();
break;
}
}
ManagedUserAuthenticator::~ManagedUserAuthenticator() {}
ManagedUserAuthenticator::AuthState ManagedUserAuthenticator::ResolveState() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
// 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;
if (!current_state_->add_key && !current_state_->hash_obtained())
return CONTINUE;
AuthState state;
if (current_state_->cryptohome_outcome())
state = ResolveCryptohomeSuccessState();
else
state = ResolveCryptohomeFailureState();
DCHECK(current_state_->cryptohome_complete());
DCHECK(current_state_->hash_obtained() || current_state_->add_key);
return state;
}
ManagedUserAuthenticator::AuthState
ManagedUserAuthenticator::ResolveCryptohomeFailureState() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
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_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;
}
return FAILED_MOUNT;
}
ManagedUserAuthenticator::AuthState
ManagedUserAuthenticator::ResolveCryptohomeSuccessState() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return SUCCESS;
}
ManagedUserAuthenticator::AuthAttempt::AuthAttempt(const std::string& username,
const std::string& password,
bool add_key_attempt)
: username(username),
password(password),
add_key(add_key_attempt),
cryptohome_complete_(false),
cryptohome_outcome_(false),
hash_obtained_(false),
cryptohome_code_(cryptohome::MOUNT_ERROR_NONE) {}
ManagedUserAuthenticator::AuthAttempt::~AuthAttempt() {}
void ManagedUserAuthenticator::AuthAttempt::RecordCryptohomeStatus(
bool cryptohome_outcome,
cryptohome::MountError cryptohome_code) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
cryptohome_complete_ = true;
cryptohome_outcome_ = cryptohome_outcome;
cryptohome_code_ = cryptohome_code;
}
void ManagedUserAuthenticator::AuthAttempt::RecordHash(
const std::string& hash) {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
hash_obtained_ = true;
hash_ = hash;
}
bool ManagedUserAuthenticator::AuthAttempt::cryptohome_complete() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return cryptohome_complete_;
}
bool ManagedUserAuthenticator::AuthAttempt::cryptohome_outcome() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return cryptohome_outcome_;
}
cryptohome::MountError
ManagedUserAuthenticator::AuthAttempt::cryptohome_code() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return cryptohome_code_;
}
bool ManagedUserAuthenticator::AuthAttempt::hash_obtained() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return hash_obtained_;
}
std::string ManagedUserAuthenticator::AuthAttempt::hash() {
DCHECK(BrowserThread::CurrentlyOn(BrowserThread::UI));
return hash_;
}
} // namespace chromeos