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