普通文本  |  304行  |  9.93 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/attestation/attestation_flow.h"

#include "base/bind.h"
#include "chromeos/cryptohome/async_method_caller.h"
#include "chromeos/dbus/cryptohome_client.h"

namespace chromeos {
namespace attestation {

namespace {

// Redirects to one of three callbacks based on a boolean value and dbus call
// status.
//
// Parameters
//   on_true - Called when status=succes and value=true.
//   on_false - Called when status=success and value=false.
//   on_fail - Called when status=failure.
//   status - The D-Bus operation status.
//   value - The value returned by the D-Bus operation.
void DBusBoolRedirectCallback(const base::Closure& on_true,
                              const base::Closure& on_false,
                              const base::Closure& on_fail,
                              DBusMethodCallStatus status,
                              bool value) {
  if (status != DBUS_METHOD_CALL_SUCCESS) {
    LOG(ERROR) << "Attestation: Failed to query enrollment state.";
    if (!on_fail.is_null())
      on_fail.Run();
    return;
  }
  const base::Closure& task = value ? on_true : on_false;
  if (!task.is_null())
    task.Run();
}

void DBusDataMethodCallback(
    const AttestationFlow::CertificateCallback& callback,
    DBusMethodCallStatus status,
    bool result,
    const std::string& data) {
  if (status != DBUS_METHOD_CALL_SUCCESS) {
    LOG(ERROR) << "Attestation: DBus data operation failed.";
    if (!callback.is_null())
      callback.Run(false, "");
    return;
  }
  if (!callback.is_null())
    callback.Run(result, data);
}

AttestationKeyType GetKeyTypeForProfile(
    AttestationCertificateProfile profile) {
  switch (profile) {
    case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE:
      return KEY_DEVICE;
    case PROFILE_ENTERPRISE_USER_CERTIFICATE:
    case PROFILE_CONTENT_PROTECTION_CERTIFICATE:
      return KEY_USER;
  }
  NOTREACHED();
  return KEY_USER;
}

std::string GetKeyNameForProfile(AttestationCertificateProfile profile,
                                 const std::string& origin) {
  switch (profile) {
    case PROFILE_ENTERPRISE_MACHINE_CERTIFICATE:
      return kEnterpriseMachineKey;
    case PROFILE_ENTERPRISE_USER_CERTIFICATE:
      return kEnterpriseUserKey;
    case PROFILE_CONTENT_PROTECTION_CERTIFICATE:
      return std::string(kContentProtectionKeyPrefix) + origin;
  }
  NOTREACHED();
  return "";
}

}  // namespace

AttestationFlow::AttestationFlow(cryptohome::AsyncMethodCaller* async_caller,
                                 CryptohomeClient* cryptohome_client,
                                 scoped_ptr<ServerProxy> server_proxy)
    : async_caller_(async_caller),
      cryptohome_client_(cryptohome_client),
      server_proxy_(server_proxy.Pass()),
      weak_factory_(this) {
}

AttestationFlow::~AttestationFlow() {
}

void AttestationFlow::GetCertificate(
    AttestationCertificateProfile certificate_profile,
    const std::string& user_id,
    const std::string& request_origin,
    bool force_new_key,
    const CertificateCallback& callback) {
  // If this device has not enrolled with the Privacy CA, we need to do that
  // first.  Once enrolled we can proceed with the certificate request.
  base::Closure do_cert_request = base::Bind(
      &AttestationFlow::StartCertificateRequest,
      weak_factory_.GetWeakPtr(),
      certificate_profile,
      user_id,
      request_origin,
      force_new_key,
      callback);
  base::Closure on_enroll_failure = base::Bind(callback, false, "");
  base::Closure do_enroll = base::Bind(&AttestationFlow::StartEnroll,
                                       weak_factory_.GetWeakPtr(),
                                       on_enroll_failure,
                                       do_cert_request);
  cryptohome_client_->TpmAttestationIsEnrolled(base::Bind(
      &DBusBoolRedirectCallback,
      do_cert_request,  // If enrolled, proceed with cert request.
      do_enroll,        // If not enrolled, initiate enrollment.
      on_enroll_failure));
}

void AttestationFlow::StartEnroll(const base::Closure& on_failure,
                                  const base::Closure& next_task) {
  // Get the attestation service to create a Privacy CA enrollment request.
  async_caller_->AsyncTpmAttestationCreateEnrollRequest(base::Bind(
      &AttestationFlow::SendEnrollRequestToPCA,
      weak_factory_.GetWeakPtr(),
      on_failure,
      next_task));
}

void AttestationFlow::SendEnrollRequestToPCA(const base::Closure& on_failure,
                                             const base::Closure& next_task,
                                             bool success,
                                             const std::string& data) {
  if (!success) {
    LOG(ERROR) << "Attestation: Failed to create enroll request.";
    if (!on_failure.is_null())
      on_failure.Run();
    return;
  }

  // Send the request to the Privacy CA.
  server_proxy_->SendEnrollRequest(
      data,
      base::Bind(&AttestationFlow::SendEnrollResponseToDaemon,
                 weak_factory_.GetWeakPtr(),
                 on_failure,
                 next_task));
}

void AttestationFlow::SendEnrollResponseToDaemon(
    const base::Closure& on_failure,
    const base::Closure& next_task,
    bool success,
    const std::string& data) {
  if (!success) {
    LOG(ERROR) << "Attestation: Enroll request failed.";
    if (!on_failure.is_null())
      on_failure.Run();
    return;
  }

  // Forward the response to the attestation service to complete enrollment.
  async_caller_->AsyncTpmAttestationEnroll(
      data,
      base::Bind(&AttestationFlow::OnEnrollComplete,
                 weak_factory_.GetWeakPtr(),
                 on_failure,
                 next_task));
}

void AttestationFlow::OnEnrollComplete(const base::Closure& on_failure,
                                       const base::Closure& next_task,
                                       bool success,
                                       cryptohome::MountError /*not_used*/) {
  if (!success) {
    LOG(ERROR) << "Attestation: Failed to complete enrollment.";
    if (!on_failure.is_null())
      on_failure.Run();
    return;
  }

  // Enrollment has successfully completed, we can move on to whatever is next.
  if (!next_task.is_null())
    next_task.Run();
}

void AttestationFlow::StartCertificateRequest(
    AttestationCertificateProfile certificate_profile,
    const std::string& user_id,
    const std::string& request_origin,
    bool generate_new_key,
    const CertificateCallback& callback) {
  AttestationKeyType key_type = GetKeyTypeForProfile(certificate_profile);
  std::string key_name = GetKeyNameForProfile(certificate_profile,
                                              request_origin);
  if (generate_new_key) {
    // Get the attestation service to create a Privacy CA certificate request.
    async_caller_->AsyncTpmAttestationCreateCertRequest(
        certificate_profile,
        user_id,
        request_origin,
        base::Bind(&AttestationFlow::SendCertificateRequestToPCA,
                   weak_factory_.GetWeakPtr(),
                   key_type,
                   user_id,
                   key_name,
                   callback));
  } else {
    // If the key already exists, query the existing certificate.
    base::Closure on_key_exists = base::Bind(
        &AttestationFlow::GetExistingCertificate,
        weak_factory_.GetWeakPtr(),
        key_type,
        user_id,
        key_name,
        callback);
    // If the key does not exist, call this method back with |generate_new_key|
    // set to true.
    base::Closure on_key_not_exists = base::Bind(
        &AttestationFlow::StartCertificateRequest,
        weak_factory_.GetWeakPtr(),
        certificate_profile,
        user_id,
        request_origin,
        true,
        callback);
    cryptohome_client_->TpmAttestationDoesKeyExist(
        key_type,
        user_id,
        key_name,
        base::Bind(&DBusBoolRedirectCallback,
            on_key_exists,
            on_key_not_exists,
            base::Bind(callback, false, "")));
  }
}

void AttestationFlow::SendCertificateRequestToPCA(
    AttestationKeyType key_type,
    const std::string& user_id,
    const std::string& key_name,
    const CertificateCallback& callback,
    bool success,
    const std::string& data) {
  if (!success) {
    LOG(ERROR) << "Attestation: Failed to create certificate request.";
    if (!callback.is_null())
      callback.Run(false, "");
    return;
  }

  // Send the request to the Privacy CA.
  server_proxy_->SendCertificateRequest(
      data,
      base::Bind(&AttestationFlow::SendCertificateResponseToDaemon,
                 weak_factory_.GetWeakPtr(),
                 key_type,
                 user_id,
                 key_name,
                 callback));
}

void AttestationFlow::SendCertificateResponseToDaemon(
    AttestationKeyType key_type,
    const std::string& user_id,
    const std::string& key_name,
    const CertificateCallback& callback,
    bool success,
    const std::string& data) {
  if (!success) {
    LOG(ERROR) << "Attestation: Certificate request failed.";
    if (!callback.is_null())
      callback.Run(false, "");
    return;
  }

  // Forward the response to the attestation service to complete the operation.
  async_caller_->AsyncTpmAttestationFinishCertRequest(data,
                                                      key_type,
                                                      user_id,
                                                      key_name,
                                                      base::Bind(callback));
}

void AttestationFlow::GetExistingCertificate(
    AttestationKeyType key_type,
    const std::string& user_id,
    const std::string& key_name,
    const CertificateCallback& callback) {
  cryptohome_client_->TpmAttestationGetCertificate(
      key_type,
      user_id,
      key_name,
      base::Bind(&DBusDataMethodCallback, callback));
}

}  // namespace attestation
}  // namespace chromeos