1use crate::{Error, Result, WrapperErrorKind};
9use log::error;
10use regex::Regex;
11use std::convert::TryFrom;
12use std::env;
13use std::ffi::CStr;
14use std::ffi::CString;
15use std::net::IpAddr;
16use std::path::PathBuf;
17use std::ptr::null_mut;
18use std::str::FromStr;
19
20const DEVICE: &str = "device";
21const MSSIM: &str = "mssim";
22const SWTPM: &str = "swtpm";
23const TABRMD: &str = "tabrmd";
24
25#[derive(Debug)]
28#[allow(missing_copy_implementations)]
29pub struct TctiContext {
30 tcti_context: *mut tss_esapi_sys::TSS2_TCTI_CONTEXT,
31}
32
33impl TctiContext {
34 pub fn initialize(name_conf: TctiNameConf) -> Result<Self> {
36 let mut tcti_context = null_mut();
37
38 let tcti_name_conf = CString::try_from(name_conf)?;
39
40 unsafe {
41 let ret =
42 tss_esapi_sys::Tss2_TctiLdr_Initialize(tcti_name_conf.as_ptr(), &mut tcti_context);
43 let ret = Error::from_tss_rc(ret);
44 if !ret.is_success() {
45 error!("Error when creating a TCTI context: {}", ret);
46 return Err(ret);
47 }
48 }
49 Ok(TctiContext { tcti_context })
50 }
51
52 pub(crate) fn tcti_context_ptr(&mut self) -> *mut tss_esapi_sys::TSS2_TCTI_CONTEXT {
54 self.tcti_context
55 }
56}
57
58impl Drop for TctiContext {
59 fn drop(&mut self) {
60 unsafe {
61 tss_esapi_sys::Tss2_TctiLdr_Finalize(&mut self.tcti_context);
62 }
63 }
64}
65
66unsafe impl Send for TctiContext {}
71unsafe impl Sync for TctiContext {}
72
73#[derive(Debug)]
75#[allow(missing_copy_implementations)]
76pub struct TctiInfo {
77 tcti_info: *mut tss_esapi_sys::TSS2_TCTI_INFO,
78}
79
80impl TctiInfo {
81 pub fn get_info(name_conf: TctiNameConf) -> Result<Self> {
83 let mut tcti_info = null_mut();
84
85 let tcti_name_conf = CString::try_from(name_conf)?;
86
87 unsafe {
88 let ret = tss_esapi_sys::Tss2_TctiLdr_GetInfo(tcti_name_conf.as_ptr(), &mut tcti_info);
89 let ret = Error::from_tss_rc(ret);
90 if !ret.is_success() {
91 error!("Error when getting the TCTI_INFO structure: {}", ret);
92 return Err(ret);
93 }
94 }
95 Ok(TctiInfo { tcti_info })
96 }
97
98 pub fn version(&self) -> u32 {
100 unsafe { (*(self.tcti_info)).version }
101 }
102
103 pub fn name(&self) -> &CStr {
105 unsafe { CStr::from_ptr((*(self.tcti_info)).name) }
106 }
107
108 pub fn description(&self) -> &CStr {
110 unsafe { CStr::from_ptr((*(self.tcti_info)).description) }
111 }
112
113 pub fn config_help(&self) -> &CStr {
115 unsafe { CStr::from_ptr((*(self.tcti_info)).config_help) }
116 }
117}
118
119impl Drop for TctiInfo {
120 fn drop(&mut self) {
121 unsafe {
122 tss_esapi_sys::Tss2_TctiLdr_FreeInfo(&mut self.tcti_info);
123 }
124 }
125}
126
127#[derive(Clone, Debug, PartialEq, Eq)]
130pub enum TctiNameConf {
131 Device(DeviceConfig),
135 Mssim(NetworkTPMConfig),
139 Swtpm(NetworkTPMConfig),
143 Tabrmd(TabrmdConfig),
147}
148
149impl TctiNameConf {
150 pub fn from_environment_variable() -> Result<Self> {
161 env::var("TPM2TOOLS_TCTI")
162 .or_else(|_| env::var("TCTI"))
163 .or_else(|_| env::var("TEST_TCTI"))
164 .map_err(|_| Error::WrapperError(WrapperErrorKind::ParamsMissing))
165 .and_then(|val| TctiNameConf::from_str(&val))
166 }
167}
168
169impl TryFrom<TctiNameConf> for CString {
170 type Error = Error;
171
172 fn try_from(tcti: TctiNameConf) -> Result<Self> {
173 let tcti_name = match tcti {
174 TctiNameConf::Device(..) => DEVICE,
175 TctiNameConf::Mssim(..) => MSSIM,
176 TctiNameConf::Swtpm(..) => SWTPM,
177 TctiNameConf::Tabrmd(..) => TABRMD,
178 };
179
180 let tcti_conf = match tcti {
181 TctiNameConf::Mssim(config) => {
182 if let ServerAddress::Hostname(name) = &config.host {
183 if !hostname_validator::is_valid(name) {
184 return Err(Error::WrapperError(WrapperErrorKind::InvalidParam));
185 }
186 }
187 format!("host={},port={}", config.host, config.port)
188 }
189 TctiNameConf::Swtpm(config) => {
190 if let ServerAddress::Hostname(name) = &config.host {
191 if !hostname_validator::is_valid(name) {
192 return Err(Error::WrapperError(WrapperErrorKind::InvalidParam));
193 }
194 }
195 format!("host={},port={}", config.host, config.port)
196 }
197 TctiNameConf::Device(DeviceConfig { path }) => path
198 .to_str()
199 .ok_or(Error::WrapperError(WrapperErrorKind::InvalidParam))?
200 .to_owned(),
201 TctiNameConf::Tabrmd(config) => {
202 format!("bus_name={},bus_type={}", config.bus_name, config.bus_type)
203 }
204 };
205
206 if tcti_conf.is_empty() {
207 CString::new(tcti_name).or(Err(Error::WrapperError(WrapperErrorKind::InvalidParam)))
208 } else {
209 CString::new(format!("{}:{}", tcti_name, tcti_conf))
210 .or(Err(Error::WrapperError(WrapperErrorKind::InvalidParam)))
211 }
212 }
213}
214
215impl FromStr for TctiNameConf {
216 type Err = Error;
217
218 fn from_str(config_str: &str) -> Result<Self> {
219 let device_pattern = Regex::new(r"^device(:(.*))?$").unwrap(); if let Some(captures) = device_pattern.captures(config_str) {
221 return Ok(TctiNameConf::Device(DeviceConfig::from_str(
222 captures.get(2).map_or("", |m| m.as_str()),
223 )?));
224 }
225
226 let mssim_pattern = Regex::new(r"^mssim(:(.*))?$").unwrap(); if let Some(captures) = mssim_pattern.captures(config_str) {
228 return Ok(TctiNameConf::Mssim(NetworkTPMConfig::from_str(
229 captures.get(2).map_or("", |m| m.as_str()),
230 )?));
231 }
232
233 let swtpm_pattern = Regex::new(r"^swtpm(:(.*))?$").unwrap(); if let Some(captures) = swtpm_pattern.captures(config_str) {
235 return Ok(TctiNameConf::Swtpm(NetworkTPMConfig::from_str(
236 captures.get(2).map_or("", |m| m.as_str()),
237 )?));
238 }
239
240 let tabrmd_pattern = Regex::new(r"^tabrmd(:(.*))?$").unwrap(); if let Some(captures) = tabrmd_pattern.captures(config_str) {
242 return Ok(TctiNameConf::Tabrmd(TabrmdConfig::from_str(
243 captures.get(2).map_or("", |m| m.as_str()),
244 )?));
245 }
246
247 Err(Error::WrapperError(WrapperErrorKind::InvalidParam))
248 }
249}
250
251#[test]
252fn validate_from_str_tcti() {
253 let tcti = TctiNameConf::from_str("mssim:port=1234,host=168.0.0.1").unwrap();
254 assert_eq!(
255 tcti,
256 TctiNameConf::Mssim(NetworkTPMConfig {
257 port: 1234,
258 host: ServerAddress::Ip(IpAddr::V4(std::net::Ipv4Addr::new(168, 0, 0, 1)))
259 })
260 );
261
262 let tcti = TctiNameConf::from_str("mssim").unwrap();
263 assert_eq!(
264 tcti,
265 TctiNameConf::Mssim(NetworkTPMConfig {
266 port: DEFAULT_SERVER_PORT,
267 host: Default::default()
268 })
269 );
270
271 let tcti = TctiNameConf::from_str("swtpm:port=1234,host=168.0.0.1").unwrap();
272 assert_eq!(
273 tcti,
274 TctiNameConf::Swtpm(NetworkTPMConfig {
275 port: 1234,
276 host: ServerAddress::Ip(IpAddr::V4(std::net::Ipv4Addr::new(168, 0, 0, 1)))
277 })
278 );
279
280 let tcti = TctiNameConf::from_str("swtpm").unwrap();
281 assert_eq!(
282 tcti,
283 TctiNameConf::Swtpm(NetworkTPMConfig {
284 port: DEFAULT_SERVER_PORT,
285 host: Default::default()
286 })
287 );
288
289 let tcti = TctiNameConf::from_str("device:/try/this/path").unwrap();
290 assert_eq!(
291 tcti,
292 TctiNameConf::Device(DeviceConfig {
293 path: PathBuf::from("/try/this/path"),
294 })
295 );
296
297 let tcti = TctiNameConf::from_str("device").unwrap();
298 assert_eq!(tcti, TctiNameConf::Device(Default::default()));
299
300 let tcti = TctiNameConf::from_str("tabrmd:bus_name=some.bus.Name2,bus_type=session").unwrap();
301 assert_eq!(
302 tcti,
303 TctiNameConf::Tabrmd(TabrmdConfig {
304 bus_name: String::from("some.bus.Name2"),
305 bus_type: BusType::Session
306 })
307 );
308
309 let tcti = TctiNameConf::from_str("tabrmd").unwrap();
310 assert_eq!(tcti, TctiNameConf::Tabrmd(Default::default()));
311}
312
313#[derive(Clone, Debug, PartialEq, Eq)]
318pub struct DeviceConfig {
319 path: PathBuf,
323}
324
325impl Default for DeviceConfig {
326 fn default() -> Self {
327 DeviceConfig {
328 path: PathBuf::from("/dev/tpm0"),
329 }
330 }
331}
332
333impl FromStr for DeviceConfig {
334 type Err = Error;
335
336 fn from_str(config_str: &str) -> Result<Self> {
337 if config_str.is_empty() {
338 return Ok(Default::default());
339 }
340
341 Ok(DeviceConfig {
342 path: PathBuf::from(config_str),
343 })
344 }
345}
346
347#[test]
348fn validate_from_str_device_config() {
349 let config = DeviceConfig::from_str("").unwrap();
350 assert_eq!(config, Default::default());
351
352 let config = DeviceConfig::from_str("/dev/tpm0").unwrap();
353 assert_eq!(config.path, PathBuf::from("/dev/tpm0"));
354}
355
356#[derive(Clone, Debug, PartialEq, Eq)]
360pub struct NetworkTPMConfig {
361 host: ServerAddress,
365 port: u16,
369}
370
371const DEFAULT_SERVER_PORT: u16 = 2321;
372
373impl Default for NetworkTPMConfig {
374 fn default() -> Self {
375 NetworkTPMConfig {
376 host: Default::default(),
377 port: DEFAULT_SERVER_PORT,
378 }
379 }
380}
381
382impl FromStr for NetworkTPMConfig {
383 type Err = Error;
384
385 fn from_str(config_str: &str) -> Result<Self> {
386 if config_str.is_empty() {
387 return Ok(Default::default());
388 }
389 let host_pattern = Regex::new(r"(,|^)host=(.*?)(,|$)").unwrap(); let host = host_pattern
391 .captures(config_str)
392 .map_or(Ok(Default::default()), |captures| {
393 ServerAddress::from_str(captures.get(2).map_or("", |m| m.as_str()))
394 })?;
395
396 let port_pattern = Regex::new(r"(,|^)port=(.*?)(,|$)").unwrap(); let port =
398 port_pattern
399 .captures(config_str)
400 .map_or(Ok(DEFAULT_SERVER_PORT), |captures| {
401 u16::from_str(captures.get(2).map_or("", |m| m.as_str()))
402 .or(Err(Error::WrapperError(WrapperErrorKind::InvalidParam)))
403 })?;
404 Ok(NetworkTPMConfig { host, port })
405 }
406}
407
408#[test]
409fn validate_from_str_networktpm_config() {
410 let config = NetworkTPMConfig::from_str("").unwrap();
411 assert_eq!(config, Default::default());
412
413 let config = NetworkTPMConfig::from_str("fjshd89943r=joishdf894u9r,sio0983=9u98jj").unwrap();
414 assert_eq!(config, Default::default());
415
416 let config = NetworkTPMConfig::from_str("host=127.0.0.1,random=value").unwrap();
417 assert_eq!(
418 config.host,
419 ServerAddress::Ip(IpAddr::V4(std::net::Ipv4Addr::new(127, 0, 0, 1)))
420 );
421 assert_eq!(config.port, DEFAULT_SERVER_PORT);
422
423 let config = NetworkTPMConfig::from_str("port=1234,random=value").unwrap();
424 assert_eq!(config.host, Default::default());
425 assert_eq!(config.port, 1234);
426
427 let config = NetworkTPMConfig::from_str("host=localhost,port=1234").unwrap();
428 assert_eq!(
429 config.host,
430 ServerAddress::Hostname(String::from("localhost"))
431 );
432 assert_eq!(config.port, 1234);
433
434 let config = NetworkTPMConfig::from_str("port=1234,host=localhost").unwrap();
435 assert_eq!(config.host, "localhost".parse::<ServerAddress>().unwrap());
436 assert_eq!(config.port, 1234);
437
438 let config = NetworkTPMConfig::from_str("port=1234,host=localhost,random=value").unwrap();
439 assert_eq!(config.host, "localhost".parse::<ServerAddress>().unwrap());
440 assert_eq!(config.port, 1234);
441
442 let config = NetworkTPMConfig::from_str("host=1234.1234.1234.1234.12445.111").unwrap();
443 assert_eq!(
444 config.host,
445 ServerAddress::Hostname(String::from("1234.1234.1234.1234.12445.111"))
446 );
447
448 let _ = NetworkTPMConfig::from_str("port=abdef").unwrap_err();
449 let _ = NetworkTPMConfig::from_str("host=-timey-wimey").unwrap_err();
450 let _ = NetworkTPMConfig::from_str("host=abc@def").unwrap_err();
451 let _ = NetworkTPMConfig::from_str("host=").unwrap_err();
452 let _ = NetworkTPMConfig::from_str("port=").unwrap_err();
453 let _ = NetworkTPMConfig::from_str("port=,host=,yas").unwrap_err();
454}
455
456#[derive(Clone, Debug, PartialEq, Eq)]
460pub enum ServerAddress {
461 Ip(IpAddr),
463 Hostname(String),
468}
469
470impl FromStr for ServerAddress {
471 type Err = Error;
472
473 fn from_str(config_str: &str) -> Result<Self> {
474 if let Ok(addr) = IpAddr::from_str(config_str) {
475 return Ok(ServerAddress::Ip(addr));
476 }
477
478 if !hostname_validator::is_valid(config_str) {
479 return Err(Error::WrapperError(WrapperErrorKind::InvalidParam));
480 }
481
482 Ok(ServerAddress::Hostname(config_str.to_owned()))
483 }
484}
485
486impl std::fmt::Display for ServerAddress {
487 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
488 match self {
489 ServerAddress::Ip(addr) => addr.fmt(f),
490 ServerAddress::Hostname(name) => name.fmt(f),
491 }
492 }
493}
494
495impl Default for ServerAddress {
496 fn default() -> Self {
497 ServerAddress::Hostname(String::from("localhost"))
498 }
499}
500
501#[derive(Clone, Debug, PartialEq, Eq)]
503pub struct TabrmdConfig {
504 bus_name: String,
510 bus_type: BusType,
514}
515
516const DEFAULT_BUS_NAME: &str = "com.intel.tss2.Tabrmd";
517
518impl Default for TabrmdConfig {
519 fn default() -> Self {
520 TabrmdConfig {
521 bus_name: String::from(DEFAULT_BUS_NAME),
522 bus_type: Default::default(),
523 }
524 }
525}
526
527impl FromStr for TabrmdConfig {
528 type Err = Error;
529
530 fn from_str(config_str: &str) -> Result<Self> {
531 if config_str.is_empty() {
532 return Ok(Default::default());
533 }
534 let bus_name_pattern = Regex::new(r"(,|^)bus_name=(.*?)(,|$)").unwrap(); let bus_name = bus_name_pattern.captures(config_str).map_or(
536 Ok(DEFAULT_BUS_NAME.to_owned()),
537 |captures| {
538 let valid_bus_name_pattern =
539 Regex::new(r"^[a-zA-Z0-9\-_]+(\.[a-zA-Z0-9\-_]+)+$").unwrap(); if !valid_bus_name_pattern.is_match(captures.get(2).map_or("", |m| m.as_str())) {
541 return Err(Error::WrapperError(WrapperErrorKind::InvalidParam));
542 }
543 Ok(captures.get(2).map_or("", |m| m.as_str()).to_owned())
544 },
545 )?;
546
547 let bus_type_pattern = Regex::new(r"(,|^)bus_type=(.*?)(,|$)").unwrap(); let bus_type = bus_type_pattern
549 .captures(config_str)
550 .map_or(Ok(Default::default()), |captures| {
551 BusType::from_str(captures.get(2).map_or("", |m| m.as_str()))
552 })?;
553
554 Ok(TabrmdConfig { bus_name, bus_type })
555 }
556}
557
558#[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
560pub enum BusType {
561 #[default]
562 System,
563 Session,
564}
565
566impl FromStr for BusType {
567 type Err = Error;
568
569 fn from_str(config_str: &str) -> Result<Self> {
570 match config_str {
571 "session" => Ok(BusType::Session),
572 "system" => Ok(BusType::System),
573 _ => Err(Error::WrapperError(WrapperErrorKind::InvalidParam)),
574 }
575 }
576}
577
578impl std::fmt::Display for BusType {
579 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
580 match self {
581 BusType::Session => write!(f, "session"),
582 BusType::System => write!(f, "system"),
583 }
584 }
585}
586
587#[test]
588fn validate_from_str_tabrmd_config() {
589 let config = TabrmdConfig::from_str("").unwrap();
590 assert_eq!(config, Default::default());
591
592 let config = TabrmdConfig::from_str("fjshd89943r=joishdf894u9r,sio0983=9u98jj").unwrap();
593 assert_eq!(config, Default::default());
594
595 let config = TabrmdConfig::from_str("bus_name=one.true.bus.Name,random=value").unwrap();
596 assert_eq!(&config.bus_name, "one.true.bus.Name");
597 assert_eq!(config.bus_type, Default::default());
598
599 let config = TabrmdConfig::from_str("bus_type=session,random=value").unwrap();
600 assert_eq!(&config.bus_name, DEFAULT_BUS_NAME);
601 assert_eq!(config.bus_type, BusType::Session);
602
603 let config = TabrmdConfig::from_str("bus_name=one.true.bus.Name,bus_type=system").unwrap();
604 assert_eq!(&config.bus_name, "one.true.bus.Name");
605 assert_eq!(config.bus_type, BusType::System);
606
607 let config = TabrmdConfig::from_str("bus_type=system,bus_name=one.true.bus.Name").unwrap();
608 assert_eq!(&config.bus_name, "one.true.bus.Name");
609 assert_eq!(config.bus_type, BusType::System);
610
611 let config =
612 TabrmdConfig::from_str("bus_type=system,bus_name=one.true.bus.Name,random=value").unwrap();
613 assert_eq!(&config.bus_name, "one.true.bus.Name");
614 assert_eq!(config.bus_type, BusType::System);
615
616 let _ = TabrmdConfig::from_str("bus_name=abc&.bcd").unwrap_err();
617 let _ = TabrmdConfig::from_str("bus_name=adfsdgdfg4gf4").unwrap_err();
618 let _ = TabrmdConfig::from_str("bus_name=,bus_type=,bla?").unwrap_err();
619 let _ = TabrmdConfig::from_str("bus_type=randooom").unwrap_err();
620}