blob: 3cd99b3d1141d5b848185df0178d3f58da35d6fb [file] [log] [blame]
// Copyright 2016 The Chromium OS 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 <string>
#include <base/command_line.h>
#include <base/strings/string_number_conversions.h>
#include "cryptohome/service_monolithic.h"
#include "cryptohome/attestation_task.h"
namespace cryptohome {
const char kRetainEndorsementDataSwitch[] = "retain_endorsement_data";
// A helper function which maps an integer to a valid CertificateProfile.
CertificateProfile GetProfile(int profile_value) {
// The protobuf compiler generates the _IsValid function.
if (!CertificateProfile_IsValid(profile_value))
return ENTERPRISE_USER_CERTIFICATE;
return static_cast<CertificateProfile>(profile_value);
}
// A helper function which maps an integer to a valid Attestation::PCAType.
Attestation::PCAType GetPCAType(int value) {
if (value < 0 || value > Attestation::kMaxPCAType)
return Attestation::kDefaultPCA;
return static_cast<Attestation::PCAType>(value);
}
ServiceMonolithic::ServiceMonolithic(const std::string& abe_data)
: default_attestation_(new Attestation()),
attestation_(default_attestation_.get()) {
if (!abe_data.empty()) {
if (!GetAttestationBasedEnterpriseEnrollmentData(abe_data, &abe_data_)) {
LOG(FATAL) << "Invalid attestation-based enterprise enrollment data.";
}
}
}
ServiceMonolithic::~ServiceMonolithic() {
// Must be called here. Otherwise, after this destructor,
// all pure virtual functions from Service overloaded here
// and all members defined for this class will be gone, while
// mount_thread_ will continue running tasks until stopped in
// ~Service.
StopTasks();
}
bool ServiceMonolithic::GetAttestationBasedEnterpriseEnrollmentData(
const std::string& data, brillo::SecureBlob* abe_data) {
// The data must be a valid 32 bytes (256 bites) hexadecimal string.
if (!base::HexStringToBytes(data, abe_data) || abe_data->size() != 32) {
abe_data->clear();
return false;
}
return true;
}
void ServiceMonolithic::AttestationInitialize() {
// Get data for attestation-based enterprise enrollment.
if (abe_data_.empty())
LOG(WARNING) << "Attestation-based enterprise enrollment"
<< " will not be available.";
// Pass in all the shared dependencies here rather than
// needing to always get the Attestation object to set them
// during testing.
attestation_->Initialize(tpm_, tpm_init_, platform_, crypto_, install_attrs_,
abe_data_, base::CommandLine::ForCurrentProcess()->HasSwitch(
kRetainEndorsementDataSwitch));
}
void ServiceMonolithic::AttestationInitializeTpm() {
attestation_->CacheEndorsementData();
brillo::SecureBlob password;
if (tpm_init_->IsTpmReady() && tpm_init_->GetTpmPassword(&password)) {
attestation_->PrepareForEnrollmentAsync();
}
}
void ServiceMonolithic::AttestationInitializeTpmComplete() {
attestation_->PrepareForEnrollment();
}
void ServiceMonolithic::AttestationGetTpmStatus(GetTpmStatusReply* reply) {
reply->set_attestation_prepared(attestation_->IsPreparedForEnrollment());
reply->set_attestation_enrolled(attestation_->IsEnrolled());
reply->set_verified_boot_measured(attestation_->IsPCR0VerifiedMode());
}
bool ServiceMonolithic::AttestationGetDelegateCredentials(
brillo::SecureBlob* blob,
brillo::SecureBlob* secret,
bool* has_reset_lock_permissions) {
return attestation_->GetDelegateCredentials(blob, secret,
has_reset_lock_permissions);
}
gboolean ServiceMonolithic::TpmIsAttestationPrepared(
gboolean* OUT_prepared,
GError** error) {
*OUT_prepared = attestation_->IsPreparedForEnrollment();
return TRUE;
}
gboolean ServiceMonolithic::TpmVerifyAttestationData(
gboolean is_cros_core,
gboolean* OUT_verified,
GError** error) {
*OUT_verified = attestation_->Verify(is_cros_core);
return TRUE;
}
gboolean ServiceMonolithic::TpmVerifyEK(
gboolean is_cros_core,
gboolean* OUT_verified,
GError** error) {
*OUT_verified = attestation_->VerifyEK(is_cros_core);
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationCreateEnrollRequest(
gint pca_type,
GArray** OUT_pca_request,
GError** error) {
// We must set the GArray now because if we return without setting it,
// dbus-glib loops forever.
*OUT_pca_request = g_array_new(false, false, sizeof(SecureBlob::value_type));
brillo::SecureBlob blob;
if (attestation_->CreateEnrollRequest(GetPCAType(pca_type), &blob))
g_array_append_vals(*OUT_pca_request, blob.data(), blob.size());
return TRUE;
}
gboolean ServiceMonolithic::AsyncTpmAttestationCreateEnrollRequest(
gint pca_type,
gint* OUT_async_id,
GError** error) {
AttestationTaskObserver* observer =
new MountTaskObserverBridge(NULL, &event_source_);
scoped_refptr<CreateEnrollRequestTask> task =
new CreateEnrollRequestTask(observer, attestation_,
GetPCAType(pca_type));
*OUT_async_id = task->sequence_id();
mount_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&CreateEnrollRequestTask::Run, task.get()));
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationEnroll(
gint pca_type,
GArray* pca_response,
gboolean* OUT_success,
GError** error) {
brillo::SecureBlob blob(pca_response->data,
pca_response->data + pca_response->len);
*OUT_success = attestation_->Enroll(GetPCAType(pca_type), blob);
return TRUE;
}
gboolean ServiceMonolithic::AsyncTpmAttestationEnroll(
gint pca_type,
GArray* pca_response,
gint* OUT_async_id,
GError** error) {
brillo::SecureBlob blob(pca_response->data,
pca_response->data + pca_response->len);
AttestationTaskObserver* observer =
new MountTaskObserverBridge(NULL, &event_source_);
scoped_refptr<EnrollTask> task =
new EnrollTask(observer, attestation_, GetPCAType(pca_type), blob);
*OUT_async_id = task->sequence_id();
mount_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&EnrollTask::Run, task.get()));
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationCreateCertRequest(
gint pca_type,
gint certificate_profile,
gchar* username,
gchar* request_origin,
GArray** OUT_pca_request,
GError** error) {
// We must set the GArray now because if we return without setting it,
// dbus-glib loops forever.
*OUT_pca_request = g_array_new(false, false, sizeof(SecureBlob::value_type));
brillo::SecureBlob blob;
if (attestation_->CreateCertRequest(GetPCAType(pca_type),
GetProfile(certificate_profile),
username,
request_origin,
&blob))
g_array_append_vals(*OUT_pca_request, blob.data(), blob.size());
return TRUE;
}
gboolean ServiceMonolithic::AsyncTpmAttestationCreateCertRequest(
gint pca_type,
gint certificate_profile,
gchar* username,
gchar* request_origin,
gint* OUT_async_id,
GError** error) {
AttestationTaskObserver* observer =
new MountTaskObserverBridge(NULL, &event_source_);
scoped_refptr<CreateCertRequestTask> task =
new CreateCertRequestTask(observer,
attestation_,
GetPCAType(pca_type),
GetProfile(certificate_profile),
username,
request_origin);
*OUT_async_id = task->sequence_id();
mount_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&CreateCertRequestTask::Run, task.get()));
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationFinishCertRequest(
GArray* pca_response,
gboolean is_user_specific,
gchar* username,
gchar* key_name,
GArray** OUT_cert,
gboolean* OUT_success,
GError** error) {
// We must set the GArray now because if we return without setting it,
// dbus-glib loops forever.
*OUT_cert = g_array_new(false, false, sizeof(SecureBlob::value_type));
brillo::SecureBlob response_blob(pca_response->data,
pca_response->data + pca_response->len);
brillo::SecureBlob cert_blob;
*OUT_success = attestation_->FinishCertRequest(response_blob,
is_user_specific,
username,
key_name,
&cert_blob);
if (*OUT_success)
g_array_append_vals(*OUT_cert, cert_blob.data(), cert_blob.size());
return TRUE;
}
gboolean ServiceMonolithic::AsyncTpmAttestationFinishCertRequest(
GArray* pca_response,
gboolean is_user_specific,
gchar* username,
gchar* key_name,
gint* OUT_async_id,
GError** error) {
brillo::SecureBlob blob(pca_response->data,
pca_response->data + pca_response->len);
AttestationTaskObserver* observer =
new MountTaskObserverBridge(NULL, &event_source_);
scoped_refptr<FinishCertRequestTask> task =
new FinishCertRequestTask(observer,
attestation_,
blob,
is_user_specific,
username,
key_name);
*OUT_async_id = task->sequence_id();
mount_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&FinishCertRequestTask::Run, task.get()));
return TRUE;
}
gboolean ServiceMonolithic::TpmIsAttestationEnrolled(
gboolean* OUT_is_enrolled,
GError** error) {
*OUT_is_enrolled = attestation_->IsEnrolled();
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationDoesKeyExist(
gboolean is_user_specific,
gchar* username,
gchar* key_name,
gboolean *OUT_exists,
GError** error) {
*OUT_exists = attestation_->DoesKeyExist(is_user_specific,
username,
key_name);
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationGetCertificate(
gboolean is_user_specific,
gchar* username,
gchar* key_name,
GArray **OUT_certificate,
gboolean* OUT_success,
GError** error) {
*OUT_certificate = g_array_new(false, false, sizeof(SecureBlob::value_type));
brillo::SecureBlob blob;
*OUT_success = attestation_->GetCertificateChain(is_user_specific,
username,
key_name,
&blob);
if (*OUT_success)
g_array_append_vals(*OUT_certificate, blob.data(), blob.size());
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationGetPublicKey(
gboolean is_user_specific,
gchar* username,
gchar* key_name,
GArray **OUT_public_key,
gboolean* OUT_success,
GError** error) {
*OUT_public_key = g_array_new(false, false, sizeof(SecureBlob::value_type));
brillo::SecureBlob blob;
*OUT_success = attestation_->GetPublicKey(is_user_specific,
username,
key_name,
&blob);
if (*OUT_success)
g_array_append_vals(*OUT_public_key, blob.data(), blob.size());
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationRegisterKey(
gboolean is_user_specific,
gchar* username,
gchar* key_name,
gint *OUT_async_id,
GError** error) {
AttestationTaskObserver* observer =
new MountTaskObserverBridge(NULL, &event_source_);
scoped_refptr<RegisterKeyTask> task =
new RegisterKeyTask(observer,
attestation_,
is_user_specific,
username,
key_name);
*OUT_async_id = task->sequence_id();
mount_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&RegisterKeyTask::Run, task.get()));
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationSignEnterpriseChallenge(
gboolean is_user_specific,
gchar* username,
gchar* key_name,
gchar* domain,
GArray* device_id,
gboolean include_signed_public_key,
GArray* challenge,
gint *OUT_async_id,
GError** error) {
brillo::SecureBlob device_id_blob(device_id->data,
device_id->data + device_id->len);
brillo::SecureBlob challenge_blob(challenge->data,
challenge->data + challenge->len);
AttestationTaskObserver* observer =
new MountTaskObserverBridge(NULL, &event_source_);
scoped_refptr<SignChallengeTask> task =
new SignChallengeTask(observer,
attestation_,
is_user_specific,
username,
key_name,
domain,
device_id_blob,
include_signed_public_key,
challenge_blob);
*OUT_async_id = task->sequence_id();
mount_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&SignChallengeTask::Run, task.get()));
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationSignSimpleChallenge(
gboolean is_user_specific,
gchar* username,
gchar* key_name,
GArray* challenge,
gint *OUT_async_id,
GError** error) {
brillo::SecureBlob challenge_blob(challenge->data,
challenge->data + challenge->len);
AttestationTaskObserver* observer =
new MountTaskObserverBridge(NULL, &event_source_);
scoped_refptr<SignChallengeTask> task =
new SignChallengeTask(observer,
attestation_,
is_user_specific,
username,
key_name,
challenge_blob);
*OUT_async_id = task->sequence_id();
mount_thread_.message_loop()->PostTask(
FROM_HERE,
base::Bind(&SignChallengeTask::Run, task.get()));
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationGetKeyPayload(
gboolean is_user_specific,
gchar* username,
gchar* key_name,
GArray** OUT_payload,
gboolean* OUT_success,
GError** error) {
// We must set the GArray now because if we return without setting it,
// dbus-glib loops forever.
*OUT_payload = g_array_new(false, false, sizeof(SecureBlob::value_type));
brillo::SecureBlob blob;
*OUT_success = attestation_->GetKeyPayload(is_user_specific,
username,
key_name,
&blob);
if (*OUT_success)
g_array_append_vals(*OUT_payload, blob.data(), blob.size());
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationSetKeyPayload(
gboolean is_user_specific,
gchar* username,
gchar* key_name,
GArray* payload,
gboolean* OUT_success,
GError** error) {
brillo::SecureBlob blob(payload->data, payload->data + payload->len);
*OUT_success = attestation_->SetKeyPayload(is_user_specific,
username,
key_name,
blob);
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationDeleteKeys(
gboolean is_user_specific,
gchar* username,
gchar* key_prefix,
gboolean* OUT_success,
GError** error) {
*OUT_success = attestation_->DeleteKeysByPrefix(is_user_specific,
username,
key_prefix);
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationGetEK(
gchar** OUT_ek_info,
gboolean* OUT_success,
GError** error) {
std::string ek_info;
*OUT_success = attestation_->GetEKInfo(&ek_info);
*OUT_ek_info = g_strndup(ek_info.data(), ek_info.size());
return TRUE;
}
gboolean ServiceMonolithic::TpmAttestationResetIdentity(
gchar* reset_token,
GArray** OUT_reset_request,
gboolean* OUT_success,
GError** error) {
// We must set the GArray now because if we return without setting it,
// dbus-glib loops forever.
*OUT_reset_request = g_array_new(false, false,
sizeof(SecureBlob::value_type));
brillo::SecureBlob reset_request;
*OUT_success = attestation_->GetIdentityResetRequest(
std::string(reinterpret_cast<char*>(reset_token)),
&reset_request);
if (*OUT_success)
g_array_append_vals(*OUT_reset_request,
reset_request.data(),
reset_request.size());
return TRUE;
}
void ServiceMonolithic::DoGetEndorsementInfo(const brillo::SecureBlob& request,
DBusGMethodInvocation* context) {
GetEndorsementInfoRequest request_pb;
if (!request_pb.ParseFromArray(request.data(), request.size())) {
SendInvalidArgsReply(context, "Bad GetEndorsementInfoRequest");
return;
}
BaseReply reply;
SecureBlob public_key;
SecureBlob certificate;
if (attestation_->GetCachedEndorsementData(&public_key, &certificate) ||
(tpm_->GetEndorsementPublicKey(&public_key) &&
tpm_->GetEndorsementCredential(&certificate))) {
GetEndorsementInfoReply* extension = reply.MutableExtension(
GetEndorsementInfoReply::reply);
extension->set_ek_public_key(public_key.to_string());
if (!certificate.empty()) {
extension->set_ek_certificate(certificate.to_string());
}
} else {
reply.set_error(CRYPTOHOME_ERROR_TPM_EK_NOT_AVAILABLE);
}
SendReply(context, reply);
}
gboolean ServiceMonolithic::GetEndorsementInfo(const GArray* request,
DBusGMethodInvocation* context) {
mount_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&ServiceMonolithic::DoGetEndorsementInfo,
base::Unretained(this),
SecureBlob(request->data, request->data + request->len),
base::Unretained(context)));
return TRUE;
}
void ServiceMonolithic::DoInitializeCastKey(const brillo::SecureBlob& request,
DBusGMethodInvocation* context) {
const char kCastCertificateOrigin[] = "CAST";
const char kCastKeyLabel[] = "CERTIFIED_CAST_KEY";
LOG(INFO) << "Initializing Cast Key";
InitializeCastKeyRequest request_pb;
if (!request_pb.ParseFromArray(request.data(), request.size())) {
SendInvalidArgsReply(context, "Bad InitializeCastKeyRequest");
return;
}
BaseReply reply;
if (!attestation_->IsPreparedForEnrollment() ||
!pkcs11_init_->IsSystemTokenOK()) {
reply.set_error(CRYPTOHOME_ERROR_ATTESTATION_NOT_READY);
SendReply(context, reply);
return;
}
if (!attestation_->IsEnrolled()) {
brillo::SecureBlob enroll_request;
if (!attestation_->CreateEnrollRequest(Attestation::kDefaultPCA,
&enroll_request)) {
reply.set_error(CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR);
SendReply(context, reply);
return;
}
brillo::SecureBlob enroll_reply;
if (!attestation_->SendPCARequestAndBlock(Attestation::kDefaultPCA,
Attestation::kEnroll,
enroll_request,
&enroll_reply)) {
reply.set_error(CRYPTOHOME_ERROR_CANNOT_CONNECT_TO_CA);
SendReply(context, reply);
return;
}
if (!attestation_->Enroll(Attestation::kDefaultPCA, enroll_reply)) {
reply.set_error(CRYPTOHOME_ERROR_CA_REFUSED_ENROLLMENT);
SendReply(context, reply);
return;
}
}
if (!attestation_->DoesKeyExist(false, // is_user_specific
"", // username
kCastKeyLabel)) {
brillo::SecureBlob certificate_request;
if (!attestation_->CreateCertRequest(Attestation::kDefaultPCA,
CAST_CERTIFICATE,
"", // username
kCastCertificateOrigin,
&certificate_request)) {
reply.set_error(CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR);
SendReply(context, reply);
return;
}
brillo::SecureBlob certificate_reply;
if (!attestation_->SendPCARequestAndBlock(Attestation::kDefaultPCA,
Attestation::kGetCertificate,
certificate_request,
&certificate_reply)) {
reply.set_error(CRYPTOHOME_ERROR_CANNOT_CONNECT_TO_CA);
SendReply(context, reply);
return;
}
brillo::SecureBlob certificate_chain;
if (!attestation_->FinishCertRequest(certificate_reply,
false, // is_user_specific
"", // username
kCastKeyLabel,
&certificate_chain)) {
reply.set_error(CRYPTOHOME_ERROR_CA_REFUSED_CERTIFICATE);
SendReply(context, reply);
return;
}
}
if (!attestation_->RegisterKey(false, // is_user_specific
"", // username
kCastKeyLabel,
true)) { // include_certificates
reply.set_error(CRYPTOHOME_ERROR_INTERNAL_ATTESTATION_ERROR);
SendReply(context, reply);
return;
}
SendReply(context, reply);
}
gboolean ServiceMonolithic::InitializeCastKey(const GArray* request,
DBusGMethodInvocation* context) {
mount_thread_.message_loop()->PostTask(FROM_HERE,
base::Bind(&ServiceMonolithic::DoInitializeCastKey,
base::Unretained(this),
SecureBlob(request->data, request->data + request->len),
base::Unretained(context)));
return TRUE;
}
} // namespace cryptohome