1 // Copyright 2022 The ChromiumOS Authors 2 // Use of this source code is governed by a BSD-style license that can be 3 // found in the LICENSE file. 4 5 //! vTPM Proxy backend using the vtpmd on ChromeOS to virtualize TPM commands. 6 7 use std::time::Duration; 8 9 use base::error; 10 use protobuf::Message; 11 use remain::sorted; 12 use system_api::client::OrgChromiumVtpm; 13 use system_api::vtpm_interface::SendCommandRequest; 14 use system_api::vtpm_interface::SendCommandResponse; 15 use thiserror::Error; 16 17 use super::virtio::TpmBackend; 18 19 // 5 minutes is the default timeout for tpm commands. 20 const VTPM_DBUS_TIMEOUT: Duration = Duration::from_secs(300); 21 22 // The response of TPM_RC_INSUFFICIENT 23 const TPM_RC_INSUFFICIENT_RESPONSE: &[u8] = &[ 24 0x80, 0x01, // TPM_ST_NO_SESSIONS 25 0x00, 0x00, 0x00, 0x0A, // Header Size = 10 26 0x00, 0x00, 0x00, 0x9A, // TPM_RC_INSUFFICIENT 27 ]; 28 29 // The response of TPM_RC_FAILURE 30 const TPM_RC_FAILURE_RESPONSE: &[u8] = &[ 31 0x80, 0x01, // TPM_ST_NO_SESSIONS 32 0x00, 0x00, 0x00, 0x0A, // Header Size = 10 33 0x00, 0x00, 0x01, 0x01, // TPM_RC_FAILURE 34 ]; 35 36 /// A proxy object that connects to the vtpmd on ChromeOS. 37 pub struct VtpmProxy { 38 dbus_connection: Option<dbus::blocking::Connection>, 39 buf: Vec<u8>, 40 } 41 42 impl VtpmProxy { 43 /// Returns a proxy that can be connected to the vtpmd. new() -> Self44 pub fn new() -> Self { 45 VtpmProxy { 46 dbus_connection: None, 47 buf: Vec::new(), 48 } 49 } 50 get_or_create_dbus_connection( &mut self, ) -> anyhow::Result<&dbus::blocking::Connection, dbus::Error>51 fn get_or_create_dbus_connection( 52 &mut self, 53 ) -> anyhow::Result<&dbus::blocking::Connection, dbus::Error> { 54 return match self.dbus_connection { 55 Some(ref dbus_connection) => Ok(dbus_connection), 56 None => { 57 let dbus_connection = dbus::blocking::Connection::new_system()?; 58 self.dbus_connection = Some(dbus_connection); 59 return self.get_or_create_dbus_connection(); 60 } 61 }; 62 } 63 try_execute_command(&mut self, command: &[u8]) -> anyhow::Result<(), Error>64 fn try_execute_command(&mut self, command: &[u8]) -> anyhow::Result<(), Error> { 65 let dbus_connection = self 66 .get_or_create_dbus_connection() 67 .map_err(Error::DBusError)?; 68 69 let proxy = dbus_connection.with_proxy( 70 "org.chromium.Vtpm", 71 "/org/chromium/Vtpm", 72 VTPM_DBUS_TIMEOUT, 73 ); 74 75 let mut proto = SendCommandRequest::new(); 76 proto.set_command(command.to_vec()); 77 78 let bytes = proto.write_to_bytes().map_err(Error::ProtobufError)?; 79 80 let resp_bytes = proxy.send_command(bytes).map_err(Error::DBusError)?; 81 82 let response = 83 SendCommandResponse::parse_from_bytes(&resp_bytes).map_err(Error::ProtobufError)?; 84 85 self.buf = response.response().to_vec(); 86 87 Ok(()) 88 } 89 } 90 91 impl Default for VtpmProxy { default() -> Self92 fn default() -> Self { 93 Self::new() 94 } 95 } 96 97 impl TpmBackend for VtpmProxy { execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8]98 fn execute_command<'a>(&'a mut self, command: &[u8]) -> &'a [u8] { 99 match self.try_execute_command(command) { 100 Ok(()) => &self.buf, 101 Err(e) => { 102 error!("{:#}", e); 103 match e { 104 Error::ProtobufError(_) => TPM_RC_INSUFFICIENT_RESPONSE, 105 Error::DBusError(_) => TPM_RC_FAILURE_RESPONSE, 106 } 107 } 108 } 109 } 110 } 111 112 #[sorted] 113 #[derive(Error, Debug)] 114 enum Error { 115 #[error("D-Bus failure: {0:#}")] 116 DBusError(dbus::Error), 117 #[error("protocol buffers failure: {0:#}")] 118 ProtobufError(protobuf::Error), 119 } 120