diff options
author | bartfab <bartfab@chromium.org> | 2014-09-03 16:59:47 -0700 |
---|---|---|
committer | Commit bot <commit-bot@chromium.org> | 2014-09-04 00:34:38 +0000 |
commit | 37e866f487685f176c1861ecc507a2f25e2c1a33 (patch) | |
tree | 49d4f747ac5bcaca061dbeb28660ec9ef74337e1 /chromeos/login | |
parent | 8d0540250a09769fa5f3a323302ceb4a0d498a51 (diff) | |
download | chromium_src-37e866f487685f176c1861ecc507a2f25e2c1a33.zip chromium_src-37e866f487685f176c1861ecc507a2f25e2c1a33.tar.gz chromium_src-37e866f487685f176c1861ecc507a2f25e2c1a33.tar.bz2 |
Make CryptohomeAuthenticator's Login*() methods work with pre-hashed keys
When a user's cryptohome directory is created with a pre-hashed key,
the credentials provided need to be hashed in the same way whenever the
crypthome is to be unlocked/mounted. This CL updates the Login*() methods
in CryptohomeAuthenticator to retrieve key metadata and apply the correct
hashing algorithm to the given credentials. Follow-up CLs will update
the other CryptohomeAuthenticator methods to also work with pre-hashed
keys.
BUG=367847
TEST=Extended unit tests
Review URL: https://codereview.chromium.org/517653002
Cr-Commit-Position: refs/heads/master@{#293227}
Diffstat (limited to 'chromeos/login')
-rw-r--r-- | chromeos/login/auth/cryptohome_authenticator.cc | 232 | ||||
-rw-r--r-- | chromeos/login/auth/cryptohome_authenticator.h | 2 |
2 files changed, 182 insertions, 52 deletions
diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc index fffa216..d00cf29 100644 --- a/chromeos/login/auth/cryptohome_authenticator.cc +++ b/chromeos/login/auth/cryptohome_authenticator.cc @@ -4,6 +4,7 @@ #include "chromeos/login/auth/cryptohome_authenticator.h" +#include "base/basictypes.h" #include "base/bind.h" #include "base/files/file_path.h" #include "base/location.h" @@ -30,6 +31,14 @@ namespace { // The label used for the key derived from the user's GAIA credentials. const char kCryptohomeGAIAKeyLabel[] = "gaia"; +// 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"; + // 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, @@ -73,14 +82,25 @@ void TriggerResolveWithLoginTimeMarker( TriggerResolve(attempt, resolver, success, return_code); } -void TriggerResolveWithHashAndLoginTimeMarker( - const std::string& marker_name, - AuthAttemptState* attempt, - scoped_refptr<CryptohomeAuthenticator> resolver, - bool success, - cryptohome::MountError return_code, - const std::string& mount_hash) { - chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker(marker_name, false); +// 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(AuthAttemptState* attempt, + scoped_refptr<CryptohomeAuthenticator> resolver) { + chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End", + false); + attempt->RecordCryptohomeStatus(false /* success */, + cryptohome::MOUNT_ERROR_KEY_FAILURE); + resolver->Resolve(); +} + +// Callback invoked when cryptohome's MountEx() method has finished. +void OnMount(AuthAttemptState* attempt, + scoped_refptr<CryptohomeAuthenticator> resolver, + bool success, + cryptohome::MountError return_code, + const std::string& mount_hash) { + chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker("CryptohomeMount-End", + false); attempt->RecordCryptohomeStatus(success, return_code); if (success) attempt->RecordUsernameHash(mount_hash); @@ -89,20 +109,23 @@ void TriggerResolveWithHashAndLoginTimeMarker( resolver->Resolve(); } -// Calls cryptohome's mount method. -void Mount(AuthAttemptState* attempt, - scoped_refptr<CryptohomeAuthenticator> resolver, - bool ephemeral, - bool create_if_nonexistent, - const std::string& system_salt) { - chromeos::LoginEventRecorder::Get()->AddLoginTimeMarker( - "CryptohomeMount-Start", false); +// 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(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(); - scoped_ptr<Key> key = - TransformKeyIfNeeded(*attempt->user_context.GetKey(), system_salt); // Set the authentication's key label 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 @@ -123,10 +146,127 @@ void Mount(AuthAttemptState* attempt, cryptohome::Identification(attempt->user_context.GetUserID()), cryptohome::Authorization(auth_key), mount, - base::Bind(&TriggerResolveWithHashAndLoginTimeMarker, - "CryptohomeMount-End", + base::Bind(&OnMount, attempt, resolver)); +} + +// 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(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); + + DoMount(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(AuthAttemptState* attempt, + scoped_refptr<CryptohomeAuthenticator> resolver, + bool ephemeral, + bool create_if_nonexistent, + bool success, + cryptohome::MountError return_code, + ScopedVector<cryptohome::RetrievedKeyData> key_data) { + if (success) { + if (key_data.size() == 1) { + cryptohome::RetrievedKeyData* key_data_entry = key_data.front(); + DCHECK_EQ(kCryptohomeGAIAKeyLabel, key_data_entry->label); + + // Extract the key type and salt from |key_data|, if present. + scoped_ptr<int64> type; + scoped_ptr<std::string> salt; + for (ScopedVector<cryptohome::RetrievedKeyData::ProviderData>:: + const_iterator it = key_data_entry->provider_data.begin(); + it != key_data_entry->provider_data.end(); ++it) { + if ((*it)->name == kKeyProviderDataTypeName) { + if ((*it)->number) + type.reset(new int64(*(*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) { + LOG(ERROR) << "Invalid key type: " << *type; + RecordKeyErrorAndResolve(attempt, resolver); + return; + } + + if (!salt) { + LOG(ERROR) << "Missing salt."; + RecordKeyErrorAndResolve(attempt, resolver); + return; + } + + attempt->user_context.GetKey()->Transform( + static_cast<Key::KeyType>(*type), + *salt); + DoMount(attempt, resolver, ephemeral, create_if_nonexistent); + return; + } + } else { + LOG(ERROR) << "GetKeyDataEx() returned " << key_data.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(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) { + DoMount(attempt, resolver, ephemeral, create_if_nonexistent); + return; + } + + cryptohome::HomedirMethods::GetInstance()->GetKeyDataEx( + cryptohome::Identification(attempt->user_context.GetUserID()), + kCryptohomeGAIAKeyLabel, + base::Bind(&OnGetKeyDataEx, attempt, - resolver)); + resolver, + ephemeral, + create_if_nonexistent)); } // Calls cryptohome's mount method for guest and also get the user hash from @@ -252,12 +392,10 @@ void CryptohomeAuthenticator::AuthenticateToLogin( // Reset the verified flag. owner_is_verified_ = false; - SystemSaltGetter::Get()->GetSystemSalt( - base::Bind(&Mount, - current_state_.get(), - scoped_refptr<CryptohomeAuthenticator>(this), - false /* ephemeral */, - false /* create_if_nonexistent */)); + StartMount(current_state_.get(), + scoped_refptr<CryptohomeAuthenticator>(this), + false /* ephemeral */, + false /* create_if_nonexistent */); } void CryptohomeAuthenticator::CompleteLogin(Profile* profile, @@ -272,12 +410,10 @@ void CryptohomeAuthenticator::CompleteLogin(Profile* profile, // Reset the verified flag. owner_is_verified_ = false; - SystemSaltGetter::Get()->GetSystemSalt( - base::Bind(&Mount, - current_state_.get(), - scoped_refptr<CryptohomeAuthenticator>(this), - false /* ephemeral */, - false /* create_if_nonexistent */)); + StartMount(current_state_.get(), + 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 @@ -312,12 +448,10 @@ void CryptohomeAuthenticator::LoginAsSupervisedUser( 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), - false /* ephemeral */, - false /* create_if_nonexistent */)); + StartMount(current_state_.get(), + scoped_refptr<CryptohomeAuthenticator>(this), + false /* ephemeral */, + false /* create_if_nonexistent */); } void CryptohomeAuthenticator::LoginRetailMode() { @@ -361,12 +495,10 @@ void CryptohomeAuthenticator::LoginAsPublicSession( 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), - true /* ephemeral */, - true /* create_if_nonexistent */)); + StartMount(current_state_.get(), + scoped_refptr<CryptohomeAuthenticator>(this), + true /* ephemeral */, + true /* create_if_nonexistent */); } void CryptohomeAuthenticator::LoginAsKioskAccount( @@ -569,12 +701,10 @@ void CryptohomeAuthenticator::Resolve() { create_if_nonexistent = true; case RECOVER_MOUNT: current_state_->ResetCryptohomeStatus(); - SystemSaltGetter::Get()->GetSystemSalt( - base::Bind(&Mount, - current_state_.get(), - scoped_refptr<CryptohomeAuthenticator>(this), - false /*ephemeral*/, - create_if_nonexistent)); + StartMount(current_state_.get(), + scoped_refptr<CryptohomeAuthenticator>(this), + false /*ephemeral*/, + create_if_nonexistent); break; case NEED_OLD_PW: task_runner_->PostTask( diff --git a/chromeos/login/auth/cryptohome_authenticator.h b/chromeos/login/auth/cryptohome_authenticator.h index d003b83..cd01ac4 100644 --- a/chromeos/login/auth/cryptohome_authenticator.h +++ b/chromeos/login/auth/cryptohome_authenticator.h @@ -7,9 +7,9 @@ #include <string> -#include "base/basictypes.h" #include "base/compiler_specific.h" #include "base/gtest_prod_util.h" +#include "base/macros.h" #include "base/memory/scoped_ptr.h" #include "base/synchronization/lock.h" #include "base/task_runner.h" |