普通文本  |  383行  |  13.72 KB

// 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