blob: b8e5df8448a76444eae936fa855da4a009e848c2 [file] [log] [blame]
// Copyright 2021 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 "tpm_manager/server/tpm_allowlist_impl.h"
#include <cstring>
#include <string>
#include <vector>
#include <base/check.h>
#include <base/files/file_path.h>
#include <base/files/file_util.h>
#include <base/optional.h>
#include <base/strings/string_number_conversions.h>
#include <base/strings/string_util.h>
#include <libhwsec-foundation/tpm/tpm_version.h>
namespace {
#if USE_TPM_DYNAMIC
const char kTpmForceAllowTpmFile[] = "/var/lib/tpm_manager/force_allow_tpm";
// Simulator Vendor ID ("SIMU").
constexpr uint32_t kVendorIdSimulator = 0x53494d55;
// STMicroelectronics Vendor ID ("STM ").
constexpr uint32_t kVendorIdStm = 0x53544D20;
// The location of TPM DID & VID information.
constexpr char kTpmDidVidPath[] = "/sys/class/tpm/tpm0/did_vid";
// The location of system vendor information.
constexpr char kSysVendorPath[] = "/sys/class/dmi/id/sys_vendor";
// The location of product name information.
constexpr char kProductNamePath[] = "/sys/class/dmi/id/product_name";
// The location of product family information.
constexpr char kProductFamilyPath[] = "/sys/class/dmi/id/product_family";
struct TpmVidDid {
uint16_t vendor_id;
uint16_t device_id;
};
constexpr uint16_t kTpmVidAtmel = 0x1114;
constexpr uint16_t kTpmVidIbm = 0x1014;
constexpr uint16_t kTpmVidWinbond = 0x1050;
constexpr uint16_t kTpmVidIfx = 0x15D1;
constexpr TpmVidDid kTpm1DidVidAllowlist[] = {
// Atmel TPM used in some Dell Latitudes.
TpmVidDid{kTpmVidAtmel, 0x3204},
// Emulated TPM provided by the swtpm program, used with QEMU.
TpmVidDid{kTpmVidIbm, 0x1},
// Enable TPM chip in Toshiba TCXWave 6140 tablet kiosk.
TpmVidDid{kTpmVidWinbond, 0xFE},
// The vendor is INFINEON, HP Elitebook 840 G1.
TpmVidDid{kTpmVidIfx, 0xB},
// The vendor is INFINEON, HP Elitebook 840 G2.
TpmVidDid{kTpmVidIfx, 0x1A},
// The vendor is INFINEON, HP Elitebook 840 G3.
TpmVidDid{kTpmVidIfx, 0x1B},
};
constexpr TpmVidDid kTpm2DidVidAllowlist[] = {
// Emulated TPM provided by the swtpm program, used with QEMU.
TpmVidDid{kTpmVidIbm, 0x1},
};
struct DeviceModel {
const char* sys_vendor;
const char* product_name;
TpmVidDid vid_did;
};
struct DeviceFamily {
const char* sys_vendor;
const char* product_family;
uint32_t tpm_vendor_id;
};
constexpr DeviceModel kTpm2ModelsAllowlist[] = {
DeviceModel{"Dell Inc.", "Latitude 7490", TpmVidDid{kTpmVidWinbond, 0xFC}},
};
constexpr DeviceFamily kTpm2FamiliesAllowlist[] = {
DeviceFamily{"LENOVO", "ThinkPad X1 Carbon Gen 8", kVendorIdStm},
DeviceFamily{"LENOVO", "ThinkPad X1 Carbon Gen 9", kVendorIdStm},
};
bool GetDidVid(uint16_t* did, uint16_t* vid) {
base::FilePath file_path(kTpmDidVidPath);
std::string did_vid_s;
if (!base::ReadFileToString(file_path, &did_vid_s)) {
return false;
}
uint32_t did_vid = 0;
if (sscanf(did_vid_s.c_str(), "0x%X", &did_vid) != 1) {
LOG(ERROR) << __func__ << ": Failed to parse TPM DID & VID: " << did_vid_s;
return false;
}
*vid = did_vid & 0xFFFF;
*did = did_vid >> 16;
return true;
}
bool GetSysVendor(std::string* sys_vendor) {
base::FilePath file_path(kSysVendorPath);
std::string file_content;
if (!base::ReadFileToString(file_path, &file_content)) {
return false;
}
base::TrimWhitespaceASCII(file_content, base::TRIM_ALL, sys_vendor);
return true;
}
bool GetProductName(std::string* product_name) {
base::FilePath file_path(kProductNamePath);
std::string file_content;
if (!base::ReadFileToString(file_path, &file_content)) {
return false;
}
base::TrimWhitespaceASCII(file_content, base::TRIM_ALL, product_name);
return true;
}
bool GetProductFamily(std::string* product_family) {
base::FilePath file_path(kProductFamilyPath);
std::string file_content;
if (!base::ReadFileToString(file_path, &file_content)) {
return false;
}
base::TrimWhitespaceASCII(file_content, base::TRIM_ALL, product_family);
return true;
}
base::Optional<bool> IsForceAllow() {
base::FilePath file_path(kTpmForceAllowTpmFile);
std::string file_content;
if (!base::ReadFileToString(file_path, &file_content)) {
return {};
}
std::string force_allow_str;
base::TrimWhitespaceASCII(file_content, base::TRIM_ALL, &force_allow_str);
int force_allow = 0;
if (!base::StringToInt(force_allow_str, &force_allow)) {
LOG(ERROR) << "force_allow is not a number";
return {};
}
return static_cast<bool>(force_allow);
}
#endif
} // namespace
namespace tpm_manager {
TpmAllowlistImpl::TpmAllowlistImpl(TpmStatus* tpm_status)
: tpm_status_(tpm_status) {
CHECK(tpm_status_);
}
bool TpmAllowlistImpl::IsAllowed() {
#if !USE_TPM_DYNAMIC
// Allow all kinds of TPM if we are not using runtime TPM selection.
return true;
#else
base::Optional<bool> force_allow = IsForceAllow();
if (force_allow.has_value()) {
return force_allow.value();
}
TPM_SELECT_BEGIN;
TPM2_SECTION({
uint32_t family;
uint64_t spec_level;
uint32_t manufacturer;
uint32_t tpm_model;
uint64_t firmware_version;
std::vector<uint8_t> vendor_specific;
if (!tpm_status_->GetVersionInfo(&family, &spec_level, &manufacturer,
&tpm_model, &firmware_version,
&vendor_specific)) {
LOG(ERROR) << __func__ << ": failed to get version info from tpm status.";
return false;
}
// Allow the tpm2-simulator.
if (manufacturer == kVendorIdSimulator) {
return true;
}
std::string sys_vendor;
std::string product_name;
std::string product_family;
if (!GetSysVendor(&sys_vendor)) {
LOG(ERROR) << __func__ << ": Failed to get the system vendor.";
return false;
}
if (!GetProductName(&product_name)) {
LOG(ERROR) << __func__ << ": Failed to get the product name.";
return false;
}
if (!GetProductFamily(&product_family)) {
LOG(ERROR) << __func__ << ": Failed to get the product family.";
return false;
}
for (const DeviceFamily& match : kTpm2FamiliesAllowlist) {
if (sys_vendor == match.sys_vendor &&
product_family == match.product_family &&
manufacturer == match.tpm_vendor_id) {
return true;
}
}
uint16_t device_id;
uint16_t vendor_id;
if (!GetDidVid(&device_id, &vendor_id)) {
LOG(ERROR) << __func__ << ": Failed to get the TPM DID & VID.";
return false;
}
for (const DeviceModel& match : kTpm2ModelsAllowlist) {
if (sys_vendor == match.sys_vendor &&
product_name == match.product_name) {
if (vendor_id == match.vid_did.vendor_id &&
device_id == match.vid_did.device_id) {
return true;
}
}
}
for (const TpmVidDid& match : kTpm2DidVidAllowlist) {
if (device_id == match.device_id && vendor_id == match.vendor_id) {
return true;
}
}
LOG(INFO) << "Not allowed TPM2.0:";
LOG(INFO) << " System Vendor: " << sys_vendor;
LOG(INFO) << " Product Name: " << product_name;
LOG(INFO) << " Product Family: " << product_family;
LOG(INFO) << " TPM Vendor ID: " << std::hex << vendor_id;
LOG(INFO) << " TPM Device ID: " << std::hex << device_id;
return false;
});
TPM1_SECTION({
uint16_t device_id;
uint16_t vendor_id;
if (!GetDidVid(&device_id, &vendor_id)) {
LOG(ERROR) << __func__ << ": Failed to get the TPM DID & VID.";
return false;
}
for (const TpmVidDid& match : kTpm1DidVidAllowlist) {
if (device_id == match.device_id && vendor_id == match.vendor_id) {
return true;
}
}
LOG(INFO) << "Not allowed TPM1.2:";
LOG(INFO) << " TPM Vendor ID: " << std::hex << vendor_id;
LOG(INFO) << " TPM Device ID: " << std::hex << device_id;
return false;
});
OTHER_TPM_SECTION({
// We don't allow the other TPM cases.
return false;
});
TPM_SELECT_END;
#endif
}
} // namespace tpm_manager