Chromium Code Reviews| Index: chromeos/login/auth/cryptohome_authenticator.cc |
| diff --git a/chromeos/login/auth/cryptohome_authenticator.cc b/chromeos/login/auth/cryptohome_authenticator.cc |
| index fffa2164d6c9428e3849ec655205aabb57e0bf2b..050db15b186dde8da6a56004852300f00df5d5cf 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"; |
| + |
|
dzhioev (left Google)
2014/09/02 15:56:26
Where is the code, that saves this data into crypt
bartfab (slow)
2014/09/02 17:24:58
There is none yet. This CL adds the code path that
|
| // 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,122 @@ 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 && key_data.size() == 1) { |
|
dzhioev (left Google)
2014/09/02 15:56:26
When |key_data| with a size greater than 1 is retu
bartfab (slow)
2014/09/02 17:24:58
That would be an error. We ask for metadata for ex
|
| + 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; |
| + } |
| + } |
| + |
| + 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 +387,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 +405,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 +443,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 +490,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 +696,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( |