Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 0 additions & 15 deletions vm/devices/tpm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -307,8 +307,6 @@ pub enum TpmErrorKind {
#[source]
error: Box<dyn std::error::Error + Send + Sync>,
},
#[error("failed to clear platform hierarchy")]
ClearPlatformHierarchy(#[source] TpmHelperError),
#[error("failed to set pcr banks")]
SetPcrBanks(#[source] TpmHelperError),
}
Expand Down Expand Up @@ -621,19 +619,6 @@ impl Tpm {
}
}

// clear tpm hierarchy control
self.tpm_engine_helper
.hierarchy_control(TPM20_RH_PLATFORM, TPM20_RH_PLATFORM, false)
.map_err(|error| TpmHelperError::TpmCommandError {
command_debug_info: CommandDebugInfo {
command_code: CommandCodeEnum::HierarchyControl,
auth_handle: Some(TPM20_RH_PLATFORM),
nv_index: None,
},
error,
})
.map_err(TpmErrorKind::ClearPlatformHierarchy)?;

self.flush_pending_nvram()
.await
.map_err(TpmErrorKind::PersistNvramState)?;
Expand Down
58 changes: 0 additions & 58 deletions vm/devices/tpm/src/tpm20proto.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2038,64 +2038,6 @@ pub mod protocol {
}
}

// === Hierarchy Control === //

#[repr(C)]
#[derive(IntoBytes, Immutable, KnownLayout, FromBytes)]
pub struct HierarchyControlCmd {
header: CmdHeader,

auth_handle: ReservedHandle,
auth_size: u32_be,
auth: common::CmdAuth,

hierarchy: ReservedHandle,
state: u8,
}

impl HierarchyControlCmd {
pub fn new(
session: SessionTag,
auth_handle: ReservedHandle,
auth: common::CmdAuth,
hierarchy: ReservedHandle,
state: bool,
) -> Self {
Self {
header: CmdHeader::new::<Self>(session, CommandCodeEnum::HierarchyControl.into()),
auth_handle,
auth_size: (size_of::<common::CmdAuth>() as u32).into(),
auth,
hierarchy,
state: state as u8,
}
}
}

#[repr(C)]
#[derive(Debug, IntoBytes, Immutable, KnownLayout, FromBytes)]
pub struct HierarchyControlReply {
pub header: ReplyHeader,
pub param_size: u32_be,
pub auth: common::ReplyAuth,
}

impl TpmCommand for HierarchyControlCmd {
type Reply = HierarchyControlReply;
}

impl TpmReply for HierarchyControlReply {
type Command = HierarchyControlCmd;

fn deserialize(bytes: &[u8]) -> Option<Self> {
Some(Self::read_from_prefix(bytes).ok()?.0) // TODO: zerocopy: tpm better error? (https://github.com/microsoft/openvmm/issues/759)
}

fn payload_size(&self) -> usize {
size_of::<Self>()
}
}

// === Pcr Allocate === //

#[repr(C)]
Expand Down
60 changes: 0 additions & 60 deletions vm/devices/tpm/src/tpm_helper.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1146,43 +1146,6 @@ impl TpmEngineHelper {
}
}

/// Helper function to send HierarchyControl command.
///
/// # Arguments
/// * `auth_handle`: The authorization handle used in the command.
/// * `hierarchy`: The hierarchy to control.
/// * `state`: Enable the target hierarchy or not.
///
pub fn hierarchy_control(
&mut self,
auth_handle: ReservedHandle,
hierarchy: ReservedHandle,
state: bool,
) -> Result<(), TpmCommandError> {
use tpm20proto::protocol::HierarchyControlCmd;

let session_tag = SessionTagEnum::Sessions;
let mut cmd = HierarchyControlCmd::new(
session_tag.into(),
auth_handle,
CmdAuth::new(TPM20_RS_PW, 0, 0, 0),
hierarchy,
state,
);

self.tpm_engine
.execute_command(cmd.as_mut_bytes(), &mut self.reply_buffer)
.map_err(TpmCommandError::TpmExecuteCommand)?;

match HierarchyControlCmd::base_validate_reply(&self.reply_buffer, session_tag) {
Err(error) => Err(TpmCommandError::InvalidResponse(error))?,
Ok((res, false)) => Err(TpmCommandError::TpmCommandFailed {
response_code: res.header.response_code.get(),
})?,
Ok((_res, true)) => Ok(()),
}
}

/// Helper function to send ClearControl command.
///
/// # Arguments
Expand Down Expand Up @@ -3412,29 +3375,6 @@ mod tests {
}
}

#[test]
fn test_hierarchy_control() {
let mut tpm_engine_helper = create_tpm_engine_helper();
restart_tpm_engine(&mut tpm_engine_helper, false, true);

// Positive test
let auth_handle = TPM20_RH_PLATFORM;
let result = tpm_engine_helper.hierarchy_control(auth_handle, TPM20_RH_PLATFORM, false);
assert!(result.is_ok());

// Negative test
let invalid_auth_handle = ReservedHandle(0.into());
let result =
tpm_engine_helper.hierarchy_control(invalid_auth_handle, TPM20_RH_PLATFORM, false);
assert!(result.is_err());
let err = result.unwrap_err();
if let TpmCommandError::TpmCommandFailed { response_code } = err {
assert_ne!(response_code, ResponseCode::Success as u32);
} else {
panic!()
}
}

struct TpmtSensitive {
/// TPMI_ALG_PUBLIC
sensitive_type: AlgId,
Expand Down
56 changes: 56 additions & 0 deletions vmm_tests/vmm_tests/test_data/tpm_platform_hierarchy.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# Copyright (c) Microsoft Corporation.
# Licensed under the MIT License.

#
# Simple test to verify TPM platform hierarchy is disabled for guest access

import struct

# TPM Response Codes
TPM_RC_SUCCESS = 0x0000
TPM_RC_HIERARCHY = 0x0085 # Hierarchy is disabled/not available
TPM_RC_AUTH_FAIL = 0x008E # Authorization failure
TPM_RC_COMMAND_CODE = 0x0143 # Command not allowed

# TPM error code masks and structure (TPM 2.0 spec)
TPM_RC_FORMAT_ONE_MASK = 0x0080 # Format-One response codes have this bit set
TPM_RC_P = 0x0100 # Parameter error bit
TPM_RC_1 = 0x0001 # Parameter 1 (our platform hierarchy handle)
TPM_RC_H = 0x0000 # Handle error (bits 8-10 = 000)
TPM_RC_S = 0x0800 # Session error (bits 8-10 = 100)

# Expected error patterns for platform hierarchy being disabled
# 0x0085 | 0x0080 | 0x0100 | 0x0001 = 0x0185 (hierarchy error + format-one + parameter error + param 1)
TPM_RC_HIERARCHY_P1 = TPM_RC_HIERARCHY | TPM_RC_FORMAT_ONE_MASK | TPM_RC_P | TPM_RC_1 # 0x0185 expected

# TPM2_Clear with platform hierarchy - simplest test case
# Command format: tag(2) + size(4) + command_code(4) + auth_handle(4)
tpm_clear_platform = (
b'\x80\x01' # TPM_ST_NO_SESSIONS
b'\x00\x00\x00\x0E' # Command size (14 bytes)
b'\x00\x00\x01\x26' # TPM_CC_CLEAR
b'\x40\x00\x00\x0C' # TPM_RH_PLATFORM
)

with open('/dev/tpmrm0', 'r+b', buffering=0) as tpm:
tpm.write(tpm_clear_platform)
response = tpm.read()

# Parse response code from bytes 6-9
if len(response) >= 10:
response_code = struct.unpack('>I', response[6:10])[0]

# Check for specific platform hierarchy related errors
expected_errors = [
TPM_RC_HIERARCHY, # Direct hierarchy disabled error
TPM_RC_HIERARCHY_P1, # Hierarchy error for parameter 1 (0x0185)
TPM_RC_AUTH_FAIL, # Authorization failure
TPM_RC_COMMAND_CODE # Command not allowed
]

if response_code in expected_errors:
print('succeeded')
else:
print(f'failed - unexpected response: 0x{response_code:08X}')
else:
print('failed - invalid response length')
35 changes: 35 additions & 0 deletions vmm_tests/vmm_tests/tests/tests/x86_64/tpm.rs
Original file line number Diff line number Diff line change
Expand Up @@ -204,3 +204,38 @@ async fn vbs_boot_with_attestation(
vm.wait_for_clean_teardown().await?;
Ok(())
}

/// Test that TPM platform hierarchy is disabled for guest access on Linux.
/// The platform hierarchy should only be accessible by the host/hypervisor.
#[openvmm_test(openhcl_uefi_x64(vhd(ubuntu_2504_server_x64)))]
async fn tpm_test_platform_hierarchy_disabled(
config: PetriVmBuilder<OpenVmmPetriBackend>,
) -> anyhow::Result<()> {
let config = config
.with_guest_state_lifetime(PetriGuestStateLifetime::Disk)
.modify_backend(|b| b.with_tpm())
// TODO: this shouldn't be needed once with_tpm() is
// backend-agnostic.
.with_expect_reset();

let (vm, agent) = config.run().await?;

// Use the python script to test that platform hierarchy operations fail
const TEST_FILE: &str = "tpm_platform_hierarchy.py";
const TEST_CONTENT: &str = include_str!("../../../test_data/tpm_platform_hierarchy.py");

agent.write_file(TEST_FILE, TEST_CONTENT.as_bytes()).await?;
assert_eq!(agent.read_file(TEST_FILE).await?, TEST_CONTENT.as_bytes());

let sh = agent.unix_shell();
let output = cmd!(sh, "python3 tpm_platform_hierarchy.py").read().await?;

println!("TPM platform hierarchy test output: {}", output);

// Check if platform hierarchy operations properly failed as expected
assert!(output.contains("succeeded"));

agent.power_off().await?;
vm.wait_for_clean_teardown().await?;
Ok(())
}