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