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