xref: /cloud-hypervisor/tpm/src/emulator.rs (revision 6f8bd27cf7629733582d930519e98d19e90afb16)
1 // Copyright © 2022, Microsoft Corporation
2 //
3 // SPDX-License-Identifier: Apache-2.0
4 //
5 
6 use crate::socket::SocketDev;
7 use crate::{Commands, MemberType, Ptm, PtmCap, PtmEst, PtmInit, PtmResult, PtmSetBufferSize};
8 use crate::{TPM_CRB_BUFFER_MAX, TPM_SUCCESS};
9 use anyhow::anyhow;
10 use libc::c_void;
11 use libc::{sockaddr_storage, socklen_t};
12 use std::convert::TryInto;
13 use std::os::unix::io::RawFd;
14 use std::path::Path;
15 use std::{mem, ptr};
16 use thiserror::Error;
17 
18 const TPM_REQ_HDR_SIZE: usize = 10;
19 
20 /* capability flags returned by PTM_GET_CAPABILITY */
21 const PTM_CAP_INIT: u64 = 1;
22 const PTM_CAP_SHUTDOWN: u64 = 1 << 1;
23 const PTM_CAP_GET_TPMESTABLISHED: u64 = 1 << 2;
24 const PTM_CAP_SET_LOCALITY: u64 = 1 << 3;
25 const PTM_CAP_CANCEL_TPM_CMD: u64 = 1 << 5;
26 const PTM_CAP_RESET_TPMESTABLISHED: u64 = 1 << 7;
27 const PTM_CAP_STOP: u64 = 1 << 10;
28 const PTM_CAP_SET_DATAFD: u64 = 1 << 12;
29 const PTM_CAP_SET_BUFFERSIZE: u64 = 1 << 13;
30 
31 ///Check if the input command is selftest
32 ///
33 pub fn is_selftest(input: Vec<u8>, in_len: usize) -> bool {
34     if in_len >= TPM_REQ_HDR_SIZE {
35         let ordinal: &[u8; 4] = input[6..6 + 4]
36             .try_into()
37             .expect("slice with incorrect length");
38 
39         return u32::from_ne_bytes(*ordinal).to_be() == 0x143;
40     }
41     false
42 }
43 
44 #[derive(Error, Debug)]
45 pub enum Error {
46     #[error("Could not initialize emulator's backend: {0}")]
47     InitializeEmulator(#[source] anyhow::Error),
48     #[error("Failed to create data fd to pass to swtpm: {0}")]
49     PrepareDataFd(#[source] anyhow::Error),
50     #[error("Failed to run Control Cmd: {0}")]
51     RunControlCmd(#[source] anyhow::Error),
52     #[error("Emulator doesn't implement min required capabilities: {0}")]
53     CheckCaps(#[source] anyhow::Error),
54     #[error("Emulator failed to deliver request: {0}")]
55     DeliverRequest(#[source] anyhow::Error),
56     #[error("Emulator failed to send/receive msg on data fd: {0}")]
57     SendReceive(#[source] anyhow::Error),
58     #[error("Incorrect response to Self Test: {0}")]
59     SelfTest(#[source] anyhow::Error),
60 }
61 
62 type Result<T> = anyhow::Result<T, Error>;
63 
64 #[derive(Clone)]
65 pub struct BackendCmd {
66     pub locality: u8,
67     pub input: Vec<u8>,
68     pub input_len: usize,
69     pub output: Vec<u8>,
70     pub output_len: usize,
71     pub selftest_done: bool,
72 }
73 
74 pub struct Emulator {
75     cmd: Option<BackendCmd>,
76     caps: PtmCap, /* capabilities of the TPM */
77     control_socket: SocketDev,
78     data_fd: RawFd,
79     established_flag_cached: bool,
80     established_flag: bool,
81 }
82 
83 impl Emulator {
84     /// Create Emulator Instance
85     ///
86     /// # Arguments
87     ///
88     /// * `path` - A path to the Unix Domain Socket swtpm is listening on
89     ///
90     pub fn new(path: String) -> Result<Self> {
91         if !Path::new(&path).exists() {
92             return Err(Error::InitializeEmulator(anyhow!(
93                 "The input TPM Socket path: {:?} does not exist",
94                 path
95             )));
96         }
97         let mut socket = SocketDev::new();
98         socket.init(path).map_err(|e| {
99             Error::InitializeEmulator(anyhow!("Failed while initializing tpm emulator: {:?}", e))
100         })?;
101 
102         let mut emulator = Self {
103             cmd: None,
104             caps: 0,
105             control_socket: socket,
106             data_fd: -1,
107             established_flag_cached: false,
108             established_flag: false,
109         };
110 
111         emulator.prepare_data_fd()?;
112 
113         emulator.probe_caps()?;
114         if !emulator.check_caps() {
115             return Err(Error::InitializeEmulator(anyhow!(
116                 "Required capabilities not supported by tpm backend"
117             )));
118         }
119 
120         if !emulator.get_established_flag() {
121             return Err(Error::InitializeEmulator(anyhow!(
122                 "TPM not in established state"
123             )));
124         }
125 
126         Ok(emulator)
127     }
128 
129     /// Create socketpair, assign one socket/FD as data_fd to Control Socket
130     /// The other socket/FD will be assigned to msg_fd, which will be sent to swtpm
131     /// via CmdSetDatafd control command
132     fn prepare_data_fd(&mut self) -> Result<()> {
133         let mut res: PtmResult = 0;
134 
135         let mut fds = [-1, -1];
136         // SAFETY: FFI calls and return value of the unsafe call is checked
137         unsafe {
138             let ret = libc::socketpair(libc::AF_UNIX, libc::SOCK_STREAM, 0, fds.as_mut_ptr());
139             if ret == -1 {
140                 return Err(Error::PrepareDataFd(anyhow!(
141                     "Failed to prepare data fd for tpm emulator. Error Code {:?}",
142                     std::io::Error::last_os_error()
143                 )));
144             }
145         }
146         self.control_socket.set_msgfd(fds[1]);
147         debug!("data fd to be configured in swtpm = {:?}", fds[1]);
148         self.run_control_cmd(Commands::CmdSetDatafd, &mut res, 0, mem::size_of::<u32>())?;
149         debug!("data fd in cloud-hypervisor = {:?}", fds[0]);
150         self.data_fd = fds[0];
151         self.control_socket.set_datafd(fds[0]);
152         Ok(())
153     }
154 
155     /// Gather TPM Capabilities and cache them in Emulator
156     ///
157     fn probe_caps(&mut self) -> Result<()> {
158         let mut caps: u64 = 0;
159         self.run_control_cmd(
160             Commands::CmdGetCapability,
161             &mut caps,
162             0,
163             mem::size_of::<u64>(),
164         )?;
165         self.caps = caps;
166         Ok(())
167     }
168 
169     /// Check if minimum set of capabitlies are supported
170     fn check_caps(&mut self) -> bool {
171         /* min. required capabilities for TPM 2.0*/
172         let caps: PtmCap = PTM_CAP_INIT
173             | PTM_CAP_SHUTDOWN
174             | PTM_CAP_GET_TPMESTABLISHED
175             | PTM_CAP_SET_LOCALITY
176             | PTM_CAP_RESET_TPMESTABLISHED
177             | PTM_CAP_SET_DATAFD
178             | PTM_CAP_STOP
179             | PTM_CAP_SET_BUFFERSIZE;
180 
181         if (self.caps & caps) != caps {
182             return false;
183         }
184         true
185     }
186 
187     ///
188     /// # Arguments
189     ///
190     /// * `cmd` - Control Command to run
191     /// * `msg` - Optional msg to be sent along with Control Command
192     /// * `msg_len_in` - len of 'msg' in bytes, if passed
193     /// * `msg_len_out` - length of expected output from Control Command in bytes
194     ///
195     fn run_control_cmd(
196         &mut self,
197         cmd: Commands,
198         msg: &mut dyn Ptm,
199         msg_len_in: usize,
200         msg_len_out: usize,
201     ) -> Result<()> {
202         debug!("Control Cmd to send : {:02X?}", cmd);
203 
204         let cmd_no = (cmd as u32).to_be_bytes();
205         let n: isize = (mem::size_of::<u32>() + msg_len_in) as isize;
206 
207         let converted_req = msg.ptm_to_request();
208         debug!("converted request: {:02X?}", converted_req);
209 
210         let mut buf = Vec::<u8>::with_capacity(n as usize);
211 
212         buf.extend(cmd_no);
213         buf.extend(converted_req);
214         debug!("full Control request {:02X?}", buf);
215 
216         let _res = self.control_socket.write(&buf).map_err(|e| {
217             Error::RunControlCmd(anyhow!(
218                 "Failed while running {:02X?} Control Cmd. Error: {:?}",
219                 cmd,
220                 e
221             ))
222         })?;
223 
224         let mut output = [0_u8; TPM_CRB_BUFFER_MAX];
225 
226         // Every Control Cmd gets atleast a result code in response. Read it
227         let read_size = self.control_socket.read(&mut output).map_err(|e| {
228             Error::RunControlCmd(anyhow!(
229                 "Failed while reading response for Control Cmd: {:02X?}. Error: {:?}",
230                 cmd,
231                 e
232             ))
233         })?;
234 
235         if msg_len_out != 0 {
236             msg.update_ptm_with_response(&output[0..read_size])
237                 .map_err(|e| {
238                     Error::RunControlCmd(anyhow!(
239                         "Failed while converting response of Control Cmd: {:02X?} to PTM. Error: {:?}",
240                         cmd,
241                         e
242                     ))
243                 })?;
244         } else {
245             // No response expected, only handle return code
246             msg.set_member_type(MemberType::Response);
247         }
248 
249         if msg.get_result_code() != TPM_SUCCESS {
250             return Err(Error::RunControlCmd(anyhow!(
251                 "Control Cmd returned error code : {:?}",
252                 msg.get_result_code()
253             )));
254         }
255         debug!("Control Cmd Response : {:02X?}", &output[0..read_size]);
256         Ok(())
257     }
258 
259     pub fn get_established_flag(&mut self) -> bool {
260         let mut est: PtmEst = PtmEst::new();
261 
262         if self.established_flag_cached {
263             return self.established_flag;
264         }
265 
266         if let Err(e) = self.run_control_cmd(
267             Commands::CmdGetTpmEstablished,
268             &mut est,
269             0,
270             2 * mem::size_of::<u32>(),
271         ) {
272             error!(
273                 "Failed to run CmdGetTpmEstablished Control Cmd. Error: {:?}",
274                 e
275             );
276             return false;
277         }
278 
279         self.established_flag_cached = true;
280         if est.resp.bit != 0 {
281             self.established_flag = false;
282         } else {
283             self.established_flag = true;
284         }
285 
286         self.established_flag
287     }
288 
289     /// Function to write to data socket and read the response from it
290     fn unix_tx_bufs(&mut self) -> Result<()> {
291         let isselftest: bool;
292         // SAFETY: type "sockaddr_storage" is valid with an all-zero byte-pattern value
293         let mut addr: sockaddr_storage = unsafe { mem::zeroed() };
294         let mut len = mem::size_of::<sockaddr_storage>() as socklen_t;
295 
296         if let Some(ref mut cmd) = self.cmd {
297             cmd.selftest_done = false;
298             isselftest = is_selftest(cmd.input.to_vec(), cmd.input_len);
299 
300             debug!(
301                 "Send cmd: {:02X?}  of len {:?} on data_ioc ",
302                 cmd.input, cmd.input_len
303             );
304 
305             let data_vecs = [libc::iovec {
306                 iov_base: cmd.input.as_mut_ptr() as *mut libc::c_void,
307                 iov_len: cmd.input.len(),
308             }; 1];
309 
310             // SAFETY: all zero values from the unsafe method are updated before usage
311             let mut msghdr: libc::msghdr = unsafe { mem::zeroed() };
312             msghdr.msg_name = ptr::null_mut();
313             msghdr.msg_namelen = 0;
314             msghdr.msg_iov = data_vecs.as_ptr() as *mut libc::iovec;
315             msghdr.msg_iovlen = data_vecs.len() as _;
316             msghdr.msg_control = ptr::null_mut();
317             msghdr.msg_controllen = 0;
318             msghdr.msg_flags = 0;
319             // SAFETY: FFI call and the return value of the unsafe method is checked
320             unsafe {
321                 let ret = libc::sendmsg(self.data_fd, &msghdr, 0);
322                 if ret == -1 {
323                     return Err(Error::SendReceive(anyhow!(
324                         "Failed to send tpm command over Data FD. Error Code {:?}",
325                         std::io::Error::last_os_error()
326                     )));
327                 }
328             }
329 
330             cmd.output.fill(0);
331             // SAFETY: FFI calls and return value from unsafe method is checked
332             unsafe {
333                 let ret = libc::recvfrom(
334                     self.data_fd,
335                     cmd.output.as_ptr() as *mut c_void,
336                     cmd.output.len(),
337                     0,
338                     &mut addr as *mut libc::sockaddr_storage as *mut libc::sockaddr,
339                     &mut len as *mut socklen_t,
340                 );
341                 if ret == -1 {
342                     return Err(Error::SendReceive(anyhow!(
343                         "Failed to receive response for tpm command over Data FD. Error Code {:?}",
344                         std::io::Error::last_os_error()
345                     )));
346                 }
347                 cmd.output_len = ret as usize;
348             }
349             debug!(
350                 "response = {:02X?} len = {:?} selftest = {:?}",
351                 cmd.output, cmd.output_len, isselftest
352             );
353 
354             if isselftest {
355                 if cmd.output_len < 10 {
356                     return Err(Error::SelfTest(anyhow!(
357                         "Self test response should have 10 bytes. Only {:?} returned",
358                         cmd.output_len
359                     )));
360                 }
361                 let mut errcode: [u8; 4] = [0; 4];
362                 errcode.copy_from_slice(&cmd.output[6..6 + 4]);
363                 cmd.selftest_done = u32::from_ne_bytes(errcode).to_be() == 0;
364             }
365         }
366 
367         Ok(())
368     }
369 
370     pub fn deliver_request(&mut self, in_cmd: &mut BackendCmd) -> Result<Vec<u8>> {
371         if self.cmd.is_some() {
372             //previous request did not finish cleanly
373             return Err(Error::DeliverRequest(anyhow!(
374                 "Cannot deliver tpm Request as previous cmd did not complete."
375             )));
376         }
377         self.cmd = Some(in_cmd.clone());
378 
379         self.unix_tx_bufs()?;
380 
381         let output = self.cmd.as_ref().unwrap().output.clone();
382         in_cmd.output.fill(0);
383         in_cmd.output.clone_from(&output);
384 
385         self.backend_request_completed();
386         Ok(output)
387     }
388 
389     pub fn backend_request_completed(&mut self) {
390         self.cmd = None;
391     }
392 
393     pub fn cancel_cmd(&mut self) -> Result<()> {
394         let mut res: PtmResult = 0;
395 
396         // Check if emulator implements Cancel Cmd
397         if (self.caps & PTM_CAP_CANCEL_TPM_CMD) != PTM_CAP_CANCEL_TPM_CMD {
398             return Err(Error::CheckCaps(anyhow!(
399                 "Emulator does not implement 'Cancel Command' Capability"
400             )));
401         }
402         self.run_control_cmd(
403             Commands::CmdCancelTpmCmd,
404             &mut res,
405             0,
406             mem::size_of::<u32>(),
407         )?;
408         Ok(())
409     }
410 
411     /// Configure buffersize to use while communicating with swtpm
412     fn set_buffer_size(&mut self, wantedsize: usize, actualsize: &mut usize) -> Result<()> {
413         let mut psbs: PtmSetBufferSize = PtmSetBufferSize::new(wantedsize as u32);
414 
415         self.stop_tpm()?;
416 
417         self.run_control_cmd(
418             Commands::CmdSetBufferSize,
419             &mut psbs,
420             mem::size_of::<u32>(),
421             4 * mem::size_of::<u32>(),
422         )?;
423 
424         *actualsize = psbs.get_bufsize() as usize;
425 
426         Ok(())
427     }
428 
429     pub fn startup_tpm(&mut self, buffersize: usize) -> Result<()> {
430         let mut init: PtmInit = PtmInit::new();
431 
432         let mut actual_size: usize = 0;
433 
434         if buffersize != 0 {
435             self.set_buffer_size(buffersize, &mut actual_size)?;
436             debug!("set tpm buffersize to {:?} during Startup", buffersize);
437         }
438 
439         self.run_control_cmd(
440             Commands::CmdInit,
441             &mut init,
442             mem::size_of::<u32>(),
443             mem::size_of::<u32>(),
444         )?;
445 
446         Ok(())
447     }
448 
449     fn stop_tpm(&mut self) -> Result<()> {
450         let mut res: PtmResult = 0;
451 
452         self.run_control_cmd(Commands::CmdStop, &mut res, 0, mem::size_of::<u32>())?;
453 
454         Ok(())
455     }
456 
457     pub fn get_buffer_size(&mut self) -> Result<usize> {
458         let mut curr_buf_size: usize = 0;
459 
460         match self.set_buffer_size(0, &mut curr_buf_size) {
461             Err(_) => Ok(TPM_CRB_BUFFER_MAX),
462             _ => Ok(curr_buf_size),
463         }
464     }
465 }
466