blob: b9e057316e8b0e8054d9500147d518dd4e2affb3 [file] [log] [blame]
// Copyright 2016 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/policy/dm_token_storage.h"
#include "base/bind.h"
#include "base/task_scheduler/post_task.h"
#include "chrome/browser/chromeos/settings/token_encryptor.h"
#include "chrome/common/pref_names.h"
#include "chromeos/cryptohome/system_salt_getter.h"
#include "components/prefs/pref_registry_simple.h"
#include "components/prefs/pref_service.h"
#include "content/public/browser/browser_thread.h"
namespace {
std::string EncryptToken(const std::string& system_salt,
const std::string& dm_token) {
chromeos::CryptohomeTokenEncryptor encryptor(system_salt);
return encryptor.EncryptWithSystemSalt(dm_token);
}
std::string DecryptToken(const std::string& system_salt,
const std::string encrypted_dm_token) {
chromeos::CryptohomeTokenEncryptor encryptor(system_salt);
return encryptor.DecryptWithSystemSalt(encrypted_dm_token);
}
} // namespace
namespace policy {
DMTokenStorage::DMTokenStorage(PrefService* local_state)
: local_state_(local_state), weak_ptr_factory_(this) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
chromeos::SystemSaltGetter::Get()->GetSystemSalt(base::Bind(
&DMTokenStorage::OnSystemSaltRecevied, weak_ptr_factory_.GetWeakPtr()));
}
DMTokenStorage::~DMTokenStorage() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
FlushStoreTokenCallback(false);
FlushRetrieveTokenCallback(std::string());
}
// static
void DMTokenStorage::RegisterPrefs(PrefRegistrySimple* registry) {
registry->RegisterStringPref(prefs::kDeviceDMToken, std::string());
}
void DMTokenStorage::StoreDMToken(const std::string& dm_token,
StoreCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!store_callback_.is_null()) {
DLOG(ERROR)
<< "Failed to store DM token: Previous store operation is not finished";
std::move(callback).Run(false);
return;
}
if (!retrieve_callbacks_.empty()) {
DLOG(ERROR)
<< "Failed to store DM token: Retrieve operation is not finished";
std::move(callback).Run(false);
return;
}
store_callback_ = std::move(callback);
dm_token_ = dm_token;
switch (state_) {
case SaltState::LOADING:
// Do nothing. Waiting for system salt.
break;
case SaltState::LOADED:
EncryptAndStoreToken();
break;
case SaltState::ERROR:
FlushStoreTokenCallback(false);
break;
}
}
void DMTokenStorage::RetrieveDMToken(RetrieveCallback callback) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!store_callback_.is_null()) {
DCHECK(retrieve_callbacks_.empty());
DLOG(ERROR)
<< "Failed to retrieve DM token: Store operation is not finished";
std::move(callback).Run(std::string());
return;
}
retrieve_callbacks_.push_back(std::move(callback));
switch (state_) {
case SaltState::LOADING:
// Do nothing. Waiting for system salt.
break;
case SaltState::LOADED:
if (retrieve_callbacks_.size() == 1) { // First consumer.
LoadAndDecryptToken();
}
break;
case SaltState::ERROR:
FlushRetrieveTokenCallback(std::string());
break;
}
}
void DMTokenStorage::OnSystemSaltRecevied(const std::string& system_salt) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
system_salt_ = system_salt;
if (system_salt_.empty()) {
state_ = SaltState::ERROR;
DLOG(ERROR) << "Failed to get system salt.";
FlushStoreTokenCallback(false);
FlushRetrieveTokenCallback(std::string());
return;
}
// Should not be concurrent store and get operations.
DCHECK(store_callback_.is_null() || retrieve_callbacks_.empty());
state_ = SaltState::LOADED;
if (!store_callback_.is_null())
EncryptAndStoreToken();
else if (!retrieve_callbacks_.empty())
LoadAndDecryptToken();
}
void DMTokenStorage::EncryptAndStoreToken() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK(!system_salt_.empty());
DCHECK(!dm_token_.empty());
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::Bind(&EncryptToken, system_salt_, dm_token_),
base::Bind(&DMTokenStorage::OnTokenEncrypted,
weak_ptr_factory_.GetWeakPtr()));
}
void DMTokenStorage::OnTokenEncrypted(const std::string& encrypted_dm_token) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (encrypted_dm_token.empty()) {
DLOG(ERROR) << "Failed to encrypt DM token.";
} else {
local_state_->SetString(prefs::kDeviceDMToken, encrypted_dm_token);
}
FlushStoreTokenCallback(!encrypted_dm_token.empty());
}
void DMTokenStorage::LoadAndDecryptToken() {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
DCHECK_EQ(SaltState::LOADED, state_);
std::string encrypted_dm_token =
local_state_->GetString(prefs::kDeviceDMToken);
if (!encrypted_dm_token.empty()) {
base::PostTaskWithTraitsAndReplyWithResult(
FROM_HERE, {base::MayBlock(), base::TaskPriority::BEST_EFFORT},
base::Bind(&DecryptToken, system_salt_, encrypted_dm_token),
base::Bind(&DMTokenStorage::FlushRetrieveTokenCallback,
weak_ptr_factory_.GetWeakPtr()));
} else {
DLOG(ERROR) << "No DM token in the local state.";
FlushRetrieveTokenCallback(std::string());
}
}
void DMTokenStorage::FlushStoreTokenCallback(bool status) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (!store_callback_.is_null()) {
std::move(store_callback_).Run(status);
}
}
void DMTokenStorage::FlushRetrieveTokenCallback(const std::string& dm_token) {
DCHECK_CURRENTLY_ON(content::BrowserThread::UI);
if (retrieve_callbacks_.empty())
return;
if (dm_token.empty())
DLOG(ERROR) << "Failed to retrieve DM token.";
std::vector<RetrieveCallback> callbacks;
callbacks.swap(retrieve_callbacks_);
for (RetrieveCallback& callback : callbacks)
std::move(callback).Run(dm_token);
}
} // namespace policy