diff --git a/vm/devices/tpm/src/lib.rs b/vm/devices/tpm/src/lib.rs index cd82ad1765..33905e88b8 100644 --- a/vm/devices/tpm/src/lib.rs +++ b/vm/devices/tpm/src/lib.rs @@ -307,8 +307,6 @@ pub enum TpmErrorKind { #[source] error: Box, }, - #[error("failed to clear platform hierarchy")] - ClearPlatformHierarchy(#[source] TpmHelperError), #[error("failed to set pcr banks")] SetPcrBanks(#[source] TpmHelperError), } @@ -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)?; diff --git a/vm/devices/tpm/src/tpm20proto.rs b/vm/devices/tpm/src/tpm20proto.rs index 6f8c47a2ca..9498924797 100644 --- a/vm/devices/tpm/src/tpm20proto.rs +++ b/vm/devices/tpm/src/tpm20proto.rs @@ -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::(session, CommandCodeEnum::HierarchyControl.into()), - auth_handle, - auth_size: (size_of::() 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 { - 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::() - } - } - // === Pcr Allocate === // #[repr(C)] diff --git a/vm/devices/tpm/src/tpm_helper.rs b/vm/devices/tpm/src/tpm_helper.rs index 321f23bd46..ed8d8edeb4 100644 --- a/vm/devices/tpm/src/tpm_helper.rs +++ b/vm/devices/tpm/src/tpm_helper.rs @@ -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 @@ -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, diff --git a/vmm_tests/vmm_tests/test_data/tpm_platform_hierarchy.py b/vmm_tests/vmm_tests/test_data/tpm_platform_hierarchy.py new file mode 100644 index 0000000000..403cb356cb --- /dev/null +++ b/vmm_tests/vmm_tests/test_data/tpm_platform_hierarchy.py @@ -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') diff --git a/vmm_tests/vmm_tests/tests/tests/x86_64/tpm.rs b/vmm_tests/vmm_tests/tests/tests/x86_64/tpm.rs index b96a9c69c5..f2c5a2aeac 100644 --- a/vmm_tests/vmm_tests/tests/tests/x86_64/tpm.rs +++ b/vmm_tests/vmm_tests/tests/tests/x86_64/tpm.rs @@ -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, +) -> 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(()) +}