| // Copyright 2018 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 <utility> |
| |
| #include <base/bind.h> |
| #include <base/check.h> |
| #include <base/logging.h> |
| #include <base/message_loop/message_pump_type.h> |
| #include <libhwsec-foundation/tpm/tpm_version.h> |
| #include <tpm_manager-client/tpm_manager/dbus-constants.h> |
| |
| #include "cryptohome/bootlockbox/tpm_nvspace.h" |
| #include "cryptohome/bootlockbox/tpm_nvspace_impl.h" |
| |
| namespace { |
| constexpr base::TimeDelta kDefaultTimeout = base::Minutes(2); |
| |
| // The index of the nv space for bootlockboxd. Refer to README.lockbox |
| // for how the index is selected. |
| uint32_t GetBootLockboxNVRamIndex() { |
| TPM_SELECT_BEGIN; |
| TPM1_SECTION({ return 0x20000006; }); |
| TPM2_SECTION({ return 0x800006; }); |
| OTHER_TPM_SECTION({ |
| LOG(ERROR) << "Failed to get the bootlockbox index on none supported TPM."; |
| return 0; |
| }); |
| TPM_SELECT_END; |
| } |
| |
| } // namespace |
| |
| using tpm_manager::NvramResult; |
| |
| namespace cryptohome { |
| |
| NVSpaceState MapReadNvramError(NvramResult r) { |
| switch (r) { |
| case NvramResult::NVRAM_RESULT_SUCCESS: |
| return NVSpaceState::kNVSpaceNormal; |
| case NvramResult::NVRAM_RESULT_SPACE_DOES_NOT_EXIST: |
| return NVSpaceState::kNVSpaceUndefined; |
| // Operation disable includes uninitialized and locked, but we shouldn't see |
| // read lock for bootlockboxd. |
| case NvramResult::NVRAM_RESULT_OPERATION_DISABLED: |
| return NVSpaceState::kNVSpaceUninitialized; |
| // There is nothing to do for these errors. |
| case NvramResult::NVRAM_RESULT_DEVICE_ERROR: |
| case NvramResult::NVRAM_RESULT_ACCESS_DENIED: |
| case NvramResult::NVRAM_RESULT_INVALID_PARAMETER: |
| case NvramResult::NVRAM_RESULT_SPACE_ALREADY_EXISTS: |
| case NvramResult::NVRAM_RESULT_INSUFFICIENT_SPACE: |
| case NvramResult::NVRAM_RESULT_IPC_ERROR: |
| return NVSpaceState::kNVSpaceError; |
| } |
| } |
| |
| std::string NvramResult2Str(NvramResult r) { |
| switch (r) { |
| case NvramResult::NVRAM_RESULT_SUCCESS: |
| return "NVRAM_RESULT_SUCCESS"; |
| case NvramResult::NVRAM_RESULT_DEVICE_ERROR: |
| return "NVRAM_RESULT_DEVICE_ERROR"; |
| case NvramResult::NVRAM_RESULT_ACCESS_DENIED: |
| return "NVRAM_RESULT_ACCESS_DENIED"; |
| case NvramResult::NVRAM_RESULT_INVALID_PARAMETER: |
| return "NVRAM_RESULT_INVALID_PARAMETER"; |
| case NvramResult::NVRAM_RESULT_SPACE_DOES_NOT_EXIST: |
| return "NVRAM_RESULT_SPACE_DOES_NOT_EXIST"; |
| case NvramResult::NVRAM_RESULT_SPACE_ALREADY_EXISTS: |
| return "NVRAM_RESULT_SPACE_ALREADY_EXISTS"; |
| case NvramResult::NVRAM_RESULT_OPERATION_DISABLED: |
| return "NVRAM_RESULT_OPERATION_DISABLED"; |
| case NvramResult::NVRAM_RESULT_INSUFFICIENT_SPACE: |
| return "NVRAM_RESULT_INSUFFICIENT_SPACE"; |
| case NvramResult::NVRAM_RESULT_IPC_ERROR: |
| return "NVRAM_RESULT_IPC_ERROR"; |
| } |
| } |
| |
| TPMNVSpaceImpl::TPMNVSpaceImpl( |
| org::chromium::TpmNvramProxyInterface* tpm_nvram, |
| org::chromium::TpmManagerProxyInterface* tpm_owner) |
| : tpm_nvram_(tpm_nvram), tpm_owner_(tpm_owner) {} |
| |
| bool TPMNVSpaceImpl::Initialize() { |
| if (!tpm_nvram_ && !tpm_owner_) { |
| scoped_refptr<dbus::Bus> bus = connection_.Connect(); |
| CHECK(bus) << "Failed to connect to system D-Bus"; |
| default_tpm_nvram_ = std::make_unique<org::chromium::TpmNvramProxy>(bus); |
| tpm_nvram_ = default_tpm_nvram_.get(); |
| default_tpm_owner_ = std::make_unique<org::chromium::TpmManagerProxy>(bus); |
| tpm_owner_ = default_tpm_owner_.get(); |
| } |
| return true; |
| } |
| |
| NVSpaceState TPMNVSpaceImpl::DefineNVSpace() { |
| bool owned = false; |
| bool owner_password_present = false; |
| if (!GetTPMStatus(&owned, &owner_password_present)) { |
| LOG(INFO) << "Failed to get TPM status."; |
| return NVSpaceState::kNVSpaceUndefined; |
| } |
| if (!owned) { |
| LOG(INFO) << "Try to define nvram before TPM is owned."; |
| return NVSpaceState::kNVSpaceUndefined; |
| } |
| if (!owner_password_present) { |
| LOG(INFO) << "Try to define nvram without owner password present."; |
| return NVSpaceState::kNVSpaceNeedPowerwash; |
| } |
| |
| tpm_manager::DefineSpaceRequest request; |
| request.set_index(GetBootLockboxNVRamIndex()); |
| request.set_size(kNVSpaceSize); |
| request.add_attributes(tpm_manager::NVRAM_READ_AUTHORIZATION); |
| request.add_attributes(tpm_manager::NVRAM_BOOT_WRITE_LOCK); |
| request.add_attributes(tpm_manager::NVRAM_WRITE_AUTHORIZATION); |
| request.set_authorization_value(kWellKnownPassword); |
| |
| tpm_manager::DefineSpaceReply reply; |
| brillo::ErrorPtr error; |
| if (!tpm_nvram_->DefineSpace(request, &reply, &error, |
| kDefaultTimeout.InMilliseconds())) { |
| LOG(ERROR) << "Failed to call DefineSpace: " << error->GetMessage(); |
| return NVSpaceState::kNVSpaceUndefined; |
| } |
| if (reply.result() != tpm_manager::NVRAM_RESULT_SUCCESS) { |
| LOG(ERROR) << "Failed to define nvram space: " |
| << NvramResult2Str(reply.result()); |
| return NVSpaceState::kNVSpaceUndefined; |
| } |
| if (!RemoveNVSpaceOwnerDependency()) { |
| LOG(ERROR) << "Failed to remove the owner dependency."; |
| } |
| |
| return NVSpaceState::kNVSpaceUninitialized; |
| } |
| |
| bool TPMNVSpaceImpl::RemoveNVSpaceOwnerDependency() { |
| tpm_manager::RemoveOwnerDependencyRequest request; |
| tpm_manager::RemoveOwnerDependencyReply reply; |
| brillo::ErrorPtr error; |
| request.set_owner_dependency(tpm_manager::kTpmOwnerDependency_Bootlockbox); |
| if (!tpm_owner_->RemoveOwnerDependency(request, &reply, &error, |
| kDefaultTimeout.InMilliseconds())) { |
| LOG(ERROR) << "Failed to call RemoveOwnerDependency: " |
| << error->GetMessage(); |
| return false; |
| } |
| return true; |
| } |
| |
| bool TPMNVSpaceImpl::WriteNVSpace(const std::string& digest) { |
| if (digest.size() != SHA256_DIGEST_LENGTH) { |
| LOG(ERROR) << "Wrong digest size, expected: " << SHA256_DIGEST_LENGTH |
| << " got: " << digest.size(); |
| return false; |
| } |
| |
| BootLockboxNVSpace BootLockboxNVSpace; |
| BootLockboxNVSpace.version = kNVSpaceVersion; |
| BootLockboxNVSpace.flags = 0; |
| memcpy(BootLockboxNVSpace.digest, digest.data(), SHA256_DIGEST_LENGTH); |
| std::string nvram_data(reinterpret_cast<const char*>(&BootLockboxNVSpace), |
| kNVSpaceSize); |
| |
| tpm_manager::WriteSpaceRequest request; |
| request.set_index(GetBootLockboxNVRamIndex()); |
| request.set_data(nvram_data); |
| request.set_authorization_value(kWellKnownPassword); |
| request.set_use_owner_authorization(false); |
| tpm_manager::WriteSpaceReply reply; |
| brillo::ErrorPtr error; |
| if (!tpm_nvram_->WriteSpace(request, &reply, &error, |
| kDefaultTimeout.InMilliseconds())) { |
| LOG(ERROR) << "Failed to call WriteSpace: " << error->GetMessage(); |
| return false; |
| } |
| if (reply.result() != tpm_manager::NVRAM_RESULT_SUCCESS) { |
| LOG(ERROR) << "Failed to write nvram space: " |
| << NvramResult2Str(reply.result()); |
| return false; |
| } |
| return true; |
| } |
| |
| bool TPMNVSpaceImpl::ReadNVSpace(std::string* digest, NVSpaceState* result) { |
| *result = NVSpaceState::kNVSpaceError; |
| |
| tpm_manager::ReadSpaceRequest request; |
| request.set_index(GetBootLockboxNVRamIndex()); |
| request.set_authorization_value(kWellKnownPassword); |
| request.set_use_owner_authorization(false); |
| tpm_manager::ReadSpaceReply reply; |
| |
| brillo::ErrorPtr error; |
| if (!tpm_nvram_->ReadSpace(request, &reply, &error, |
| kDefaultTimeout.InMilliseconds())) { |
| LOG(ERROR) << "Failed to call ReadSpace: " << error->GetMessage(); |
| return false; |
| } |
| if (reply.result() != tpm_manager::NVRAM_RESULT_SUCCESS) { |
| LOG(ERROR) << "Failed to read nvram space: " |
| << NvramResult2Str(reply.result()); |
| *result = MapReadNvramError(reply.result()); |
| return false; |
| } |
| |
| const std::string& nvram_data = reply.data(); |
| |
| if (nvram_data.size() != kNVSpaceSize) { |
| LOG(ERROR) << "Error reading nvram space, invalid data length, expected:" |
| << kNVSpaceSize << ", got " << nvram_data.size(); |
| return false; |
| } |
| |
| bool owned = false; |
| bool owner_password_present = false; |
| if (!GetTPMStatus(&owned, &owner_password_present)) { |
| LOG(INFO) << "Failed to get TPM status."; |
| return false; |
| } |
| if (!owned) { |
| // Remove the owner dependency after the TPM ownership has been taken. |
| base::RepeatingClosure callback = |
| base::BindRepeating(&TPMNVSpaceImpl::RemoveNVSpaceOwnerDependency, |
| base::Unretained(this)) |
| .Then(base::BindRepeating([](bool result) { |
| if (!result) { |
| LOG(ERROR) << "Failed to remove the owner dependency."; |
| } |
| })); |
| RegisterOwnershipTakenCallback(callback); |
| } else if (owner_password_present) { |
| // Remove the owner dependency if the owner presented and the space defined |
| // correctly. |
| if (!RemoveNVSpaceOwnerDependency()) { |
| LOG(ERROR) << "Failed to remove the owner dependency."; |
| } |
| } |
| |
| if (nvram_data == std::string(kNVSpaceSize, '\0') || |
| nvram_data == std::string(kNVSpaceSize, 0xff)) { |
| LOG(ERROR) << "Empty nvram data."; |
| *result = NVSpaceState::kNVSpaceUninitialized; |
| return false; |
| } |
| |
| BootLockboxNVSpace BootLockboxNVSpace; |
| memcpy(&BootLockboxNVSpace, nvram_data.data(), kNVSpaceSize); |
| if (BootLockboxNVSpace.version != kNVSpaceVersion) { |
| LOG(ERROR) << "Error reading nvram space, invalid version"; |
| return false; |
| } |
| digest->assign(reinterpret_cast<const char*>(BootLockboxNVSpace.digest), |
| SHA256_DIGEST_LENGTH); |
| *result = NVSpaceState::kNVSpaceNormal; |
| return true; |
| } |
| |
| bool TPMNVSpaceImpl::LockNVSpace() { |
| tpm_manager::LockSpaceRequest request; |
| request.set_index(GetBootLockboxNVRamIndex()); |
| request.set_lock_read(false); |
| request.set_lock_write(true); |
| request.set_authorization_value(kWellKnownPassword); |
| request.set_use_owner_authorization(false); |
| tpm_manager::LockSpaceReply reply; |
| |
| brillo::ErrorPtr error; |
| if (!tpm_nvram_->LockSpace(request, &reply, &error, |
| kDefaultTimeout.InMilliseconds())) { |
| LOG(ERROR) << "Failed to call LockSpace: " << error->GetMessage(); |
| return false; |
| } |
| if (reply.result() == tpm_manager::NVRAM_RESULT_OPERATION_DISABLED) { |
| // This error may happen when we lock the space second time in a boot cycle. |
| return true; |
| } |
| if (reply.result() != tpm_manager::NVRAM_RESULT_SUCCESS) { |
| LOG(ERROR) << "Failed to lock nvram space: " |
| << NvramResult2Str(reply.result()); |
| return false; |
| } |
| return true; |
| } |
| |
| bool TPMNVSpaceImpl::GetTPMStatus(bool* owned, bool* owner_password_present) { |
| tpm_manager::GetTpmNonsensitiveStatusRequest request; |
| tpm_manager::GetTpmNonsensitiveStatusReply reply; |
| brillo::ErrorPtr error; |
| if (!tpm_owner_->GetTpmNonsensitiveStatus(request, &reply, &error, |
| kDefaultTimeout.InMilliseconds())) { |
| LOG(ERROR) << "Failed to call GetTpmNonsensitiveStatus: " |
| << error->GetMessage(); |
| return false; |
| } |
| *owned = reply.is_owned(); |
| *owner_password_present = reply.is_owner_password_present(); |
| return true; |
| } |
| |
| void TPMNVSpaceImpl::RegisterOwnershipTakenCallback( |
| const base::RepeatingClosure& callback) { |
| tpm_owner_->RegisterSignalOwnershipTakenSignalHandler( |
| base::BindRepeating(&TPMNVSpaceImpl::OnOwnershipTaken, |
| base::Unretained(this), callback), |
| base::DoNothing()); |
| } |
| |
| void TPMNVSpaceImpl::OnOwnershipTaken( |
| const base::RepeatingClosure& callback, |
| const tpm_manager::OwnershipTakenSignal& signal) { |
| LOG(INFO) << __func__ << ": Received |OwnershipTakenSignal|."; |
| callback.Run(); |
| } |
| |
| } // namespace cryptohome |