summaryrefslogtreecommitdiffstats
path: root/chromeos/login
diff options
context:
space:
mode:
authorbartfab <bartfab@chromium.org>2014-09-03 16:59:47 -0700
committerCommit bot <commit-bot@chromium.org>2014-09-04 00:34:38 +0000
commit37e866f487685f176c1861ecc507a2f25e2c1a33 (patch)
tree49d4f747ac5bcaca061dbeb28660ec9ef74337e1 /chromeos/login
parent8d0540250a09769fa5f3a323302ceb4a0d498a51 (diff)
downloadchromium_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.cc232
-rw-r--r--chromeos/login/auth/cryptohome_authenticator.h2
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"