blob: cfd798e31854b1021dcf9bc533b1a0422177c6fc [file] [log] [blame]
// Copyright 2014 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/supervised/supervised_user_creation_controller_new.h"
#include "base/base64.h"
#include "base/bind.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/strings/string_number_conversions.h"
#include "base/strings/string_util.h"
#include "base/sys_info.h"
#include "base/task_runner_util.h"
#include "base/threading/sequenced_worker_pool.h"
#include "base/values.h"
#include "chrome/browser/chromeos/login/supervised/supervised_user_authentication.h"
#include "chrome/browser/chromeos/login/supervised/supervised_user_constants.h"
#include "chrome/browser/chromeos/login/users/chrome_user_manager.h"
#include "chrome/browser/chromeos/login/users/supervised_user_manager.h"
#include "chrome/browser/chromeos/profiles/profile_helper.h"
#include "chrome/browser/lifetime/application_lifetime.h"
#include "chrome/browser/sync/profile_sync_service.h"
#include "chrome/browser/sync/profile_sync_service_factory.h"
#include "chromeos/cryptohome/cryptohome_parameters.h"
#include "chromeos/dbus/dbus_thread_manager.h"
#include "chromeos/dbus/session_manager_client.h"
#include "chromeos/login/auth/key.h"
#include "chromeos/login/auth/user_context.h"
#include "components/user_manager/user.h"
#include "components/user_manager/user_manager.h"
#include "content/public/browser/browser_thread.h"
#include "content/public/browser/user_metrics.h"
#include "crypto/random.h"
#include "google_apis/gaia/google_service_auth_error.h"
namespace chromeos {
namespace {
const int kUserCreationTimeoutSeconds = 30; // 30 seconds.
bool StoreSupervisedUserFiles(const std::string& token,
const base::FilePath& base_path) {
if (!base::SysInfo::IsRunningOnChromeOS()) {
// If running on desktop, cryptohome stub does not create home directory.
base::CreateDirectory(base_path);
}
base::FilePath token_file = base_path.Append(kSupervisedUserTokenFilename);
int bytes = base::WriteFile(token_file, token.c_str(), token.length());
return bytes >= 0;
}
} // namespace
SupervisedUserCreationControllerNew::SupervisedUserCreationControllerNew(
SupervisedUserCreationControllerNew::StatusConsumer* consumer,
const std::string& manager_id)
: SupervisedUserCreationController(consumer),
stage_(STAGE_INITIAL),
weak_factory_(this) {
creation_context_.reset(
new SupervisedUserCreationControllerNew::UserCreationContext());
creation_context_->manager_id = manager_id;
}
SupervisedUserCreationControllerNew::~SupervisedUserCreationControllerNew() {}
SupervisedUserCreationControllerNew::UserCreationContext::
UserCreationContext() {}
SupervisedUserCreationControllerNew::UserCreationContext::
~UserCreationContext() {}
void SupervisedUserCreationControllerNew::SetManagerProfile(
Profile* manager_profile) {
creation_context_->manager_profile = manager_profile;
}
Profile* SupervisedUserCreationControllerNew::GetManagerProfile() {
return creation_context_->manager_profile;
}
void SupervisedUserCreationControllerNew::StartCreation(
const base::string16& display_name,
const std::string& password,
int avatar_index) {
DCHECK(creation_context_);
creation_context_->creation_type = NEW_USER;
creation_context_->display_name = display_name;
creation_context_->password = password;
creation_context_->avatar_index = avatar_index;
StartCreationImpl();
}
void SupervisedUserCreationControllerNew::StartImport(
const base::string16& display_name,
const std::string& password,
int avatar_index,
const std::string& sync_id,
const std::string& master_key) {
DCHECK(creation_context_);
creation_context_->creation_type = USER_IMPORT_OLD;
creation_context_->display_name = display_name;
creation_context_->password = password;
creation_context_->avatar_index = avatar_index;
creation_context_->sync_user_id = sync_id;
creation_context_->master_key = master_key;
StartCreationImpl();
}
void SupervisedUserCreationControllerNew::StartImport(
const base::string16& display_name,
int avatar_index,
const std::string& sync_id,
const std::string& master_key,
const base::DictionaryValue* password_data,
const std::string& encryption_key,
const std::string& signature_key) {
DCHECK(creation_context_);
creation_context_->creation_type = USER_IMPORT_NEW;
creation_context_->display_name = display_name;
creation_context_->avatar_index = avatar_index;
creation_context_->sync_user_id = sync_id;
creation_context_->master_key = master_key;
password_data->GetStringWithoutPathExpansion(
kEncryptedPassword, &creation_context_->salted_password);
creation_context_->signature_key = signature_key;
creation_context_->encryption_key = encryption_key;
creation_context_->password_data.MergeDictionary(password_data);
StartCreationImpl();
}
void SupervisedUserCreationControllerNew::StartCreationImpl() {
DCHECK(creation_context_);
DCHECK_EQ(STAGE_INITIAL, stage_);
VLOG(1) << "Starting supervised user creation";
VLOG(1) << " Phase 1 : Prepare keys";
SupervisedUserManager* manager =
ChromeUserManager::Get()->GetSupervisedUserManager();
manager->StartCreationTransaction(creation_context_->display_name);
creation_context_->local_user_id = manager->GenerateUserId();
if (creation_context_->creation_type == NEW_USER) {
creation_context_->sync_user_id =
SupervisedUserRegistrationUtility::GenerateNewSupervisedUserId();
}
manager->SetCreationTransactionUserId(creation_context_->local_user_id);
stage_ = TRANSACTION_STARTED;
manager->CreateUserRecord(creation_context_->manager_id,
creation_context_->local_user_id,
creation_context_->sync_user_id,
creation_context_->display_name);
SupervisedUserAuthentication* authentication =
ChromeUserManager::Get()->GetSupervisedUserManager()->GetAuthentication();
// When importing M35+ users we need only to store data, for all other cases
// we need to create some keys.
if (creation_context_->creation_type != USER_IMPORT_NEW) {
// Of all required keys old imported users have only master key.
// Otherwise they are the same as newly created users in terms of keys.
if (creation_context_->creation_type == NEW_USER) {
creation_context_->master_key = authentication->GenerateMasterKey();
}
base::DictionaryValue extra;
authentication->FillDataForNewUser(creation_context_->local_user_id,
creation_context_->password,
&creation_context_->password_data,
&extra);
creation_context_->password_data.GetStringWithoutPathExpansion(
kEncryptedPassword, &creation_context_->salted_password);
extra.GetStringWithoutPathExpansion(kPasswordEncryptionKey,
&creation_context_->encryption_key);
extra.GetStringWithoutPathExpansion(kPasswordSignatureKey,
&creation_context_->signature_key);
}
authentication->StorePasswordData(creation_context_->local_user_id,
creation_context_->password_data);
stage_ = KEYS_GENERATED;
VLOG(1) << " Phase 2 : Create cryptohome";
timeout_timer_.Start(
FROM_HERE,
base::TimeDelta::FromSeconds(kUserCreationTimeoutSeconds),
this,
&SupervisedUserCreationControllerNew::CreationTimedOut);
authenticator_ = ExtendedAuthenticator::Create(this);
UserContext user_context;
user_context.SetKey(Key(creation_context_->master_key));
authenticator_->TransformKeyIfNeeded(
user_context,
base::Bind(&SupervisedUserCreationControllerNew::OnKeyTransformedIfNeeded,
weak_factory_.GetWeakPtr()));
}
void SupervisedUserCreationControllerNew::OnKeyTransformedIfNeeded(
const UserContext& user_context) {
VLOG(1) << " Phase 2.1 : Got hashed master key";
creation_context_->salted_master_key = user_context.GetKey()->GetSecret();
// Create home dir with two keys.
std::vector<cryptohome::KeyDefinition> keys;
// Main key is the master key. Just as keys for plain GAIA users, it is salted
// with system salt. It has all usual privileges.
cryptohome::KeyDefinition master_key(creation_context_->salted_master_key,
kCryptohomeMasterKeyLabel,
cryptohome::PRIV_DEFAULT);
keys.push_back(master_key);
authenticator_->CreateMount(
creation_context_->local_user_id,
keys,
base::Bind(&SupervisedUserCreationControllerNew::OnMountSuccess,
weak_factory_.GetWeakPtr()));
}
void SupervisedUserCreationControllerNew::OnAuthenticationFailure(
ExtendedAuthenticator::AuthState error) {
timeout_timer_.Stop();
ErrorCode code = NO_ERROR;
switch (error) {
case SupervisedUserAuthenticator::NO_MOUNT:
code = CRYPTOHOME_NO_MOUNT;
break;
case SupervisedUserAuthenticator::FAILED_MOUNT:
code = CRYPTOHOME_FAILED_MOUNT;
break;
case SupervisedUserAuthenticator::FAILED_TPM:
code = CRYPTOHOME_FAILED_TPM;
break;
default:
NOTREACHED();
}
stage_ = STAGE_ERROR;
if (consumer_)
consumer_->OnCreationError(code);
}
void SupervisedUserCreationControllerNew::OnMountSuccess(
const std::string& mount_hash) {
DCHECK(creation_context_);
DCHECK_EQ(KEYS_GENERATED, stage_);
VLOG(1) << " Phase 2.2 : Created home dir with master key";
creation_context_->mount_hash = mount_hash;
// Plain text password, hashed and salted with individual salt.
// It can be used for mounting homedir, and can be replaced only when signed.
cryptohome::KeyDefinition password_key(
creation_context_->salted_password,
kCryptohomeSupervisedUserKeyLabel,
kCryptohomeSupervisedUserKeyPrivileges);
std::string encryption_key;
base::Base64Decode(creation_context_->encryption_key, &encryption_key);
password_key.authorization_data.push_back(
cryptohome::KeyDefinition::AuthorizationData(true /* encrypt */,
false /* sign */,
encryption_key));
std::string signature_key;
base::Base64Decode(creation_context_->signature_key, &signature_key);
password_key.authorization_data.push_back(
cryptohome::KeyDefinition::AuthorizationData(false /* encrypt */,
true /* sign */,
signature_key));
Key key(Key::KEY_TYPE_SALTED_PBKDF2_AES256_1234,
std::string(), // The salt is stored elsewhere.
creation_context_->salted_master_key);
key.SetLabel(kCryptohomeMasterKeyLabel);
UserContext context(creation_context_->local_user_id);
context.SetKey(key);
context.SetIsUsingOAuth(false);
authenticator_->AddKey(
context,
password_key,
true,
base::Bind(&SupervisedUserCreationControllerNew::OnAddKeySuccess,
weak_factory_.GetWeakPtr()));
}
void SupervisedUserCreationControllerNew::OnAddKeySuccess() {
DCHECK(creation_context_);
DCHECK_EQ(KEYS_GENERATED, stage_);
stage_ = CRYPTOHOME_CREATED;
VLOG(1) << " Phase 3 : Create/update user on chrome.com/manage";
ProfileSyncService* sync_service =
ProfileSyncServiceFactory::GetInstance()->GetForProfile(
creation_context_->manager_profile);
ProfileSyncService::SyncStatusSummary status =
sync_service->QuerySyncStatusSummary();
if (status == ProfileSyncService::DATATYPES_NOT_INITIALIZED)
consumer_->OnLongCreationWarning();
creation_context_->registration_utility =
SupervisedUserRegistrationUtility::Create(
creation_context_->manager_profile);
SupervisedUserRegistrationInfo info(creation_context_->display_name,
creation_context_->avatar_index);
info.master_key = creation_context_->master_key;
info.password_signature_key = creation_context_->signature_key;
info.password_encryption_key = creation_context_->encryption_key;
info.password_data.MergeDictionary(&creation_context_->password_data);
// Registration utility will update user data if user already exist.
creation_context_->registration_utility->Register(
creation_context_->sync_user_id,
info,
base::Bind(&SupervisedUserCreationControllerNew::RegistrationCallback,
weak_factory_.GetWeakPtr()));
}
void SupervisedUserCreationControllerNew::RegistrationCallback(
const GoogleServiceAuthError& error,
const std::string& token) {
DCHECK(creation_context_);
DCHECK_EQ(CRYPTOHOME_CREATED, stage_);
stage_ = DASHBOARD_CREATED;
if (error.state() == GoogleServiceAuthError::NONE) {
creation_context_->token = token;
PostTaskAndReplyWithResult(
content::BrowserThread::GetBlockingPool(),
FROM_HERE,
base::Bind(&StoreSupervisedUserFiles,
creation_context_->token,
ProfileHelper::GetProfilePathByUserIdHash(
creation_context_->mount_hash)),
base::Bind(
&SupervisedUserCreationControllerNew::OnSupervisedUserFilesStored,
weak_factory_.GetWeakPtr()));
} else {
stage_ = STAGE_ERROR;
LOG(ERROR) << "Supervised user creation failed. Error code "
<< error.state();
if (consumer_)
consumer_->OnCreationError(CLOUD_SERVER_ERROR);
}
}
void SupervisedUserCreationControllerNew::OnSupervisedUserFilesStored(
bool success) {
DCHECK(creation_context_);
DCHECK_EQ(DASHBOARD_CREATED, stage_);
if (!success) {
stage_ = STAGE_ERROR;
if (consumer_)
consumer_->OnCreationError(TOKEN_WRITE_FAILED);
return;
}
// Assume that new token is valid. It will be automatically invalidated if
// sync service fails to use it.
user_manager::UserManager::Get()->SaveUserOAuthStatus(
creation_context_->local_user_id,
user_manager::User::OAUTH2_TOKEN_STATUS_VALID);
stage_ = TOKEN_WRITTEN;
timeout_timer_.Stop();
ChromeUserManager::Get()
->GetSupervisedUserManager()
->CommitCreationTransaction();
content::RecordAction(
base::UserMetricsAction("ManagedMode_LocallyManagedUserCreated"));
stage_ = TRANSACTION_COMMITTED;
if (consumer_)
consumer_->OnCreationSuccess();
}
void SupervisedUserCreationControllerNew::CreationTimedOut() {
LOG(ERROR) << "Supervised user creation timed out. stage = " << stage_;
if (consumer_)
consumer_->OnCreationTimeout();
}
void SupervisedUserCreationControllerNew::FinishCreation() {
chrome::AttemptUserExit();
}
void SupervisedUserCreationControllerNew::CancelCreation() {
creation_context_->registration_utility.reset();
chrome::AttemptUserExit();
}
std::string SupervisedUserCreationControllerNew::GetSupervisedUserId() {
DCHECK(creation_context_);
return creation_context_->local_user_id;
}
} // namespace chromeos