// Copyright (c) 2012 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 "chromeos/cryptohome/async_method_caller.h"
#include "base/bind.h"
#include "base/containers/hash_tables.h"
#include "base/location.h"
#include "base/message_loop/message_loop_proxy.h"
#include "chromeos/dbus/cryptohome_client.h"
#include "chromeos/dbus/dbus_thread_manager.h"
using chromeos::DBusThreadManager;
namespace cryptohome {
namespace {
AsyncMethodCaller* g_async_method_caller = NULL;
// The implementation of AsyncMethodCaller
class AsyncMethodCallerImpl : public AsyncMethodCaller {
public:
AsyncMethodCallerImpl() : weak_ptr_factory_(this) {
DBusThreadManager::Get()->GetCryptohomeClient()->SetAsyncCallStatusHandlers(
base::Bind(&AsyncMethodCallerImpl::HandleAsyncResponse,
weak_ptr_factory_.GetWeakPtr()),
base::Bind(&AsyncMethodCallerImpl::HandleAsyncDataResponse,
weak_ptr_factory_.GetWeakPtr()));
}
virtual ~AsyncMethodCallerImpl() {
DBusThreadManager::Get()->GetCryptohomeClient()->
ResetAsyncCallStatusHandlers();
}
virtual void AsyncCheckKey(const std::string& user_email,
const std::string& passhash,
Callback callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncCheckKey(user_email, passhash, base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async check of user's key."));
}
virtual void AsyncMigrateKey(const std::string& user_email,
const std::string& old_hash,
const std::string& new_hash,
Callback callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncMigrateKey(user_email, old_hash, new_hash, base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate aync migration of user's key"));
}
virtual void AsyncMount(const std::string& user_email,
const std::string& passhash,
int flags,
Callback callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncMount(user_email, passhash, flags, base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async mount of cryptohome."));
}
virtual void AsyncAddKey(const std::string& user_email,
const std::string& passhash,
const std::string& new_passhash,
Callback callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncAddKey(user_email, passhash, new_passhash, base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async key addition."));
}
virtual void AsyncMountGuest(Callback callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncMountGuest(base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async mount of cryptohome."));
}
virtual void AsyncMountPublic(const std::string& public_mount_id,
int flags,
Callback callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncMountPublic(public_mount_id, flags, base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async mount public of cryptohome."));
}
virtual void AsyncRemove(const std::string& user_email,
Callback callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncRemove(user_email, base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async removal of cryptohome."));
}
virtual void AsyncTpmAttestationCreateEnrollRequest(
const DataCallback& callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncTpmAttestationCreateEnrollRequest(base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncDataCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async attestation enroll request."));
}
virtual void AsyncTpmAttestationEnroll(const std::string& pca_response,
const Callback& callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncTpmAttestationEnroll(pca_response, base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async attestation enroll."));
}
virtual void AsyncTpmAttestationCreateCertRequest(
chromeos::attestation::AttestationCertificateProfile certificate_profile,
const std::string& user_id,
const std::string& request_origin,
const DataCallback& callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncTpmAttestationCreateCertRequest(
certificate_profile,
user_id,
request_origin,
base::Bind(&AsyncMethodCallerImpl::RegisterAsyncDataCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async attestation cert request."));
}
virtual void AsyncTpmAttestationFinishCertRequest(
const std::string& pca_response,
chromeos::attestation::AttestationKeyType key_type,
const std::string& user_id,
const std::string& key_name,
const DataCallback& callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
AsyncTpmAttestationFinishCertRequest(
pca_response,
key_type,
user_id,
key_name,
base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncDataCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async attestation finish cert request."));
}
virtual void TpmAttestationRegisterKey(
chromeos::attestation::AttestationKeyType key_type,
const std::string& user_id,
const std::string& key_name,
const Callback& callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
TpmAttestationRegisterKey(
key_type,
user_id,
key_name,
base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async attestation register key."));
}
virtual void TpmAttestationSignEnterpriseChallenge(
chromeos::attestation::AttestationKeyType key_type,
const std::string& user_id,
const std::string& key_name,
const std::string& domain,
const std::string& device_id,
chromeos::attestation::AttestationChallengeOptions options,
const std::string& challenge,
const DataCallback& callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
TpmAttestationSignEnterpriseChallenge(
key_type,
user_id,
key_name,
domain,
device_id,
options,
challenge,
base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncDataCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async attestation enterprise challenge."));
}
virtual void TpmAttestationSignSimpleChallenge(
chromeos::attestation::AttestationKeyType key_type,
const std::string& user_id,
const std::string& key_name,
const std::string& challenge,
const DataCallback& callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
TpmAttestationSignSimpleChallenge(
key_type,
user_id,
key_name,
challenge,
base::Bind(
&AsyncMethodCallerImpl::RegisterAsyncDataCallback,
weak_ptr_factory_.GetWeakPtr(),
callback,
"Couldn't initiate async attestation simple challenge."));
}
virtual void AsyncGetSanitizedUsername(
const std::string& user,
const DataCallback& callback) OVERRIDE {
DBusThreadManager::Get()->GetCryptohomeClient()->
GetSanitizedUsername(user,
base::Bind(
&AsyncMethodCallerImpl::GetSanitizedUsernameCallback,
weak_ptr_factory_.GetWeakPtr(),
callback));
}
virtual void GetSanitizedUsernameCallback(
const DataCallback& callback,
const chromeos::DBusMethodCallStatus call_status,
const std::string& result) {
callback.Run(true, result);
}
private:
struct CallbackElement {
CallbackElement() {}
explicit CallbackElement(const AsyncMethodCaller::Callback& callback)
: callback(callback),
proxy(base::MessageLoopProxy::current()) {
}
AsyncMethodCaller::Callback callback;
scoped_refptr<base::MessageLoopProxy> proxy;
};
struct DataCallbackElement {
DataCallbackElement() {}
explicit DataCallbackElement(
const AsyncMethodCaller::DataCallback& callback)
: data_callback(callback),
proxy(base::MessageLoopProxy::current()) {
}
AsyncMethodCaller::DataCallback data_callback;
scoped_refptr<base::MessageLoopProxy> proxy;
};
typedef base::hash_map<int, CallbackElement> CallbackMap;
typedef base::hash_map<int, DataCallbackElement> DataCallbackMap;
// Handles the response for async calls.
// Below is described how async calls work.
// 1. CryptohomeClient::AsyncXXX returns "async ID".
// 2. RegisterAsyncCallback registers the "async ID" with the user-provided
// callback.
// 3. Cryptohome will return the result asynchronously as a signal with
// "async ID"
// 4. "HandleAsyncResponse" handles the result signal and call the registered
// callback associated with the "async ID".
void HandleAsyncResponse(int async_id, bool return_status, int return_code) {
const CallbackMap::iterator it = callback_map_.find(async_id);
if (it == callback_map_.end()) {
LOG(ERROR) << "Received signal for unknown async_id " << async_id;
return;
}
it->second.proxy->PostTask(FROM_HERE,
base::Bind(it->second.callback,
return_status,
static_cast<MountError>(return_code)));
callback_map_.erase(it);
}
// Similar to HandleAsyncResponse but for signals with a raw data payload.
void HandleAsyncDataResponse(int async_id,
bool return_status,
const std::string& return_data) {
const DataCallbackMap::iterator it = data_callback_map_.find(async_id);
if (it == data_callback_map_.end()) {
LOG(ERROR) << "Received signal for unknown async_id " << async_id;
return;
}
it->second.proxy->PostTask(FROM_HERE,
base::Bind(it->second.data_callback, return_status, return_data));
data_callback_map_.erase(it);
}
// Registers a callback which is called when the result for AsyncXXX is ready.
void RegisterAsyncCallback(
Callback callback, const char* error, int async_id) {
if (async_id == 0) {
LOG(ERROR) << error;
return;
}
VLOG(1) << "Adding handler for " << async_id;
DCHECK_EQ(callback_map_.count(async_id), 0U);
DCHECK_EQ(data_callback_map_.count(async_id), 0U);
callback_map_[async_id] = CallbackElement(callback);
}
// Registers a callback which is called when the result for AsyncXXX is ready.
void RegisterAsyncDataCallback(
DataCallback callback, const char* error, int async_id) {
if (async_id == 0) {
LOG(ERROR) << error;
return;
}
VLOG(1) << "Adding handler for " << async_id;
DCHECK_EQ(callback_map_.count(async_id), 0U);
DCHECK_EQ(data_callback_map_.count(async_id), 0U);
data_callback_map_[async_id] = DataCallbackElement(callback);
}
base::WeakPtrFactory<AsyncMethodCallerImpl> weak_ptr_factory_;
CallbackMap callback_map_;
DataCallbackMap data_callback_map_;
DISALLOW_COPY_AND_ASSIGN(AsyncMethodCallerImpl);
};
} // namespace
// static
void AsyncMethodCaller::Initialize() {
if (g_async_method_caller) {
LOG(WARNING) << "AsyncMethodCaller was already initialized";
return;
}
g_async_method_caller = new AsyncMethodCallerImpl();
VLOG(1) << "AsyncMethodCaller initialized";
}
// static
void AsyncMethodCaller::InitializeForTesting(
AsyncMethodCaller* async_method_caller) {
if (g_async_method_caller) {
LOG(WARNING) << "AsyncMethodCaller was already initialized";
return;
}
g_async_method_caller = async_method_caller;
VLOG(1) << "AsyncMethodCaller initialized";
}
// static
void AsyncMethodCaller::Shutdown() {
if (!g_async_method_caller) {
LOG(WARNING) << "AsyncMethodCaller::Shutdown() called with NULL manager";
return;
}
delete g_async_method_caller;
g_async_method_caller = NULL;
VLOG(1) << "AsyncMethodCaller Shutdown completed";
}
// static
AsyncMethodCaller* AsyncMethodCaller::GetInstance() {
return g_async_method_caller;
}
} // namespace cryptohome