// 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 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 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 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 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 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(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(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(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