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