@@ -24,12 +24,34 @@ const TABRMD: &str = "tabrmd";
2424const LIBTPMS : & str = "libtpms" ;
2525const TBS : & str = "tbs" ;
2626
27+ // libc malloc/free, declared inline to avoid a new dependency. Required for
28+ // Tss2_Tcti_Device_Init, which initialises a caller-
29+ // provided buffer in place, and on Drop we libc-free it ourselves.
30+ #[ cfg( target_os = "linux" ) ]
31+ unsafe extern "C" {
32+ fn malloc ( size : usize ) -> * mut std:: ffi:: c_void ;
33+ fn free ( p : * mut std:: ffi:: c_void ) ;
34+ }
35+
36+ #[ derive( Debug , Clone , Copy ) ]
37+ enum TctiInitMode {
38+ /// Allocated and initialized via `Tss2_TctiLdr_Initialize`; clean up
39+ /// with the matching `Tss2_TctiLdr_Finalize`.
40+ Loader ,
41+ /// Allocated by us with `malloc` and initialized via
42+ /// `Tss2_Tcti_Device_Init`; clean up by calling the context's
43+ /// `finalize` fn pointer and then `free`-ing the buffer.
44+ #[ cfg( target_os = "linux" ) ]
45+ Direct ,
46+ }
47+
2748/// TCTI Context created via a TCTI Loader Library.
2849/// Wrapper around the TSS2_TCTI_CONTEXT structure.
2950#[ derive( Debug ) ]
3051#[ allow( missing_copy_implementations) ]
3152pub struct TctiContext {
3253 tcti_context : * mut tss_esapi_sys:: TSS2_TCTI_CONTEXT ,
54+ init_mode : TctiInitMode ,
3355}
3456
3557impl TctiContext {
@@ -48,7 +70,61 @@ impl TctiContext {
4870 } ,
4971 ) ?;
5072
51- Ok ( TctiContext { tcti_context } )
73+ Ok ( TctiContext {
74+ tcti_context,
75+ init_mode : TctiInitMode :: Loader ,
76+ } )
77+ }
78+
79+ /// Allocate and initialize a Device TCTI context by calling
80+ /// `Tss2_Tcti_Device_Init` directly, bypassing the dlopen-based
81+ /// tctildr loader. Use this when the loader can't dlopen plugin
82+ /// shared objects — e.g. a statically-linked glibc binary.
83+ ///
84+ /// Linux-only: depends on `/dev/tpm0` and on libtss2-tcti-device,
85+ /// neither of which exists on other platforms.
86+ #[ cfg( target_os = "linux" ) ]
87+ pub fn initialize_device_direct ( device_path : & std:: path:: Path ) -> Result < Self > {
88+ let conf = device_path
89+ . to_str ( )
90+ . and_then ( |s| CString :: new ( s) . ok ( ) )
91+ . ok_or ( Error :: WrapperError ( WrapperErrorKind :: InvalidParam ) ) ?;
92+
93+ unsafe {
94+ let mut size: tss_esapi_sys:: size_t = 0 ;
95+ let ret = tss_esapi_sys:: Tss2_Tcti_Device_Init ( null_mut ( ) , & mut size, conf. as_ptr ( ) ) ;
96+ let ret = Error :: from_tss_rc ( ret) ;
97+ if !ret. is_success ( ) {
98+ error ! ( "Error querying Device TCTI size: {}" , ret) ;
99+ return Err ( ret) ;
100+ }
101+
102+ let buf = malloc ( size as usize ) as * mut tss_esapi_sys:: TSS2_TCTI_CONTEXT ;
103+ if buf. is_null ( ) {
104+ return Err ( Error :: WrapperError ( WrapperErrorKind :: InternalError ) ) ;
105+ }
106+
107+ let ret = tss_esapi_sys:: Tss2_Tcti_Device_Init ( buf, & mut size, conf. as_ptr ( ) ) ;
108+ let ret = Error :: from_tss_rc ( ret) ;
109+ if !ret. is_success ( ) {
110+ // The TSS2 TCTI spec requires init to be atomic: on a non-
111+ // success return, no externally-visible state (file
112+ // descriptors, allocations beyond the caller's buffer) is
113+ // owned by the partially-initialized context. libtss2's
114+ // device TCTI honors this — it closes the fd before
115+ // returning the error — so freeing the buffer without
116+ // calling the (possibly-uninitialized) finalize fn pointer
117+ // is the correct cleanup here.
118+ free ( buf as * mut std:: ffi:: c_void ) ;
119+ error ! ( "Error initializing Device TCTI: {}" , ret) ;
120+ return Err ( ret) ;
121+ }
122+
123+ Ok ( TctiContext {
124+ tcti_context : buf,
125+ init_mode : TctiInitMode :: Direct ,
126+ } )
127+ }
52128 }
53129
54130 /// Get access to the inner C pointer
@@ -120,7 +196,25 @@ impl TctiInfo {
120196impl Drop for TctiInfo {
121197 fn drop ( & mut self ) {
122198 unsafe {
123- tss_esapi_sys:: Tss2_TctiLdr_FreeInfo ( & mut self . tcti_info ) ;
199+ match self . init_mode {
200+ TctiInitMode :: Loader => {
201+ tss_esapi_sys:: Tss2_TctiLdr_Finalize ( & mut self . tcti_context ) ;
202+ }
203+ #[ cfg( target_os = "linux" ) ]
204+ TctiInitMode :: Direct => {
205+ // Tss2_TctiLdr_Finalize would cast the buffer to a
206+ // TSS2_TCTILDR_CONTEXT and dereference loader-only fields
207+ // that don't exist here, leaking the TPM fd. Use the
208+ // TCTI's own finalize fn pointer (offset is identical in
209+ // V1, V2, and V3 layouts), then libc-free the buffer.
210+ let common =
211+ self . tcti_context as * mut tss_esapi_sys:: TSS2_TCTI_CONTEXT_COMMON_V1 ;
212+ if let Some ( finalize) = ( * common) . finalize {
213+ finalize ( self . tcti_context ) ;
214+ }
215+ free ( self . tcti_context as * mut std:: ffi:: c_void ) ;
216+ }
217+ }
124218 }
125219 }
126220}
0 commit comments