@@ -5,21 +5,22 @@ use std::path::PathBuf;
55use anyhow:: Context ;
66use clap:: Parser ;
77use log:: info;
8- use tss_esapi:: constants:: SessionType ;
9- use tss_esapi:: handles:: { ObjectHandle , SessionHandle } ;
10- use tss_esapi:: structures:: { Digest , Nonce , Signature } ;
8+ use tss_esapi:: structures:: Signature ;
119use tss_esapi:: traits:: UnMarshall ;
12- use tss_esapi:: tss2_esys:: TPMT_TK_AUTH ;
10+ use tss_esapi:: tss2_esys:: * ;
1311
1412use crate :: cli:: GlobalOpts ;
15- use crate :: context:: create_context;
16- use crate :: handle:: { ContextSource , load_object_from_source} ;
17- use crate :: parse:: parse_context_source;
18- use crate :: session:: load_session_from_file;
13+ use crate :: parse:: { self , parse_context_source} ;
14+ use crate :: raw_esys:: RawEsysContext ;
1915
20- /// Authorize a policy with a signed authorization.
16+ use crate :: handle:: ContextSource ;
17+
18+ /// Extend a policy with PolicySigned.
2119///
22- /// Wraps TPM2_PolicySigned.
20+ /// Wraps TPM2_PolicySigned (raw FFI). PolicySigned uses no
21+ /// authorization sessions because the command has no authIndex.
22+ /// Instead, the TPM validates the provided signature against
23+ /// authObject's public key as part of the policy assertion.
2324#[ derive( Parser ) ]
2425pub struct PolicySignedCmd {
2526 /// Policy session file
@@ -43,8 +44,8 @@ pub struct PolicySignedCmd {
4344 pub cphash_input : Option < PathBuf > ,
4445
4546 /// Policy reference (digest) (hex:<hex_bytes> or file:<path>)
46- #[ arg( short = 'q' , long = "qualification" , value_parser = crate :: parse:: parse_qualification) ]
47- pub qualification : Option < crate :: parse:: Qualification > ,
47+ #[ arg( short = 'q' , long = "qualification" , value_parser = parse:: parse_qualification) ]
48+ pub qualification : Option < parse:: Qualification > ,
4849
4950 /// Output file for the timeout
5051 #[ arg( short = 't' , long = "timeout" ) ]
@@ -61,84 +62,160 @@ pub struct PolicySignedCmd {
6162
6263impl PolicySignedCmd {
6364 pub fn execute ( & self , global : & GlobalOpts ) -> anyhow:: Result < ( ) > {
64- let mut ctx = create_context ( global. tcti . as_deref ( ) ) ?;
65+ let mut raw = RawEsysContext :: new ( global. tcti . as_deref ( ) ) ?;
6566
66- let session = load_session_from_file ( & mut ctx, & self . session , SessionType :: Policy ) ?;
67- let policy_session = session
68- . try_into ( )
69- . map_err ( |_| anyhow:: anyhow!( "expected a policy session" ) ) ?;
67+ let session_handle = raw. context_load (
68+ self . session
69+ . to_str ( )
70+ . ok_or_else ( || anyhow:: anyhow!( "invalid session path" ) ) ?,
71+ ) ?;
7072
71- let auth_object = load_object_from_source ( & mut ctx , & self . key_context ) ?;
73+ let auth_object = raw . resolve_handle_from_source ( & self . key_context ) ?;
7274
7375 let sig_data = std:: fs:: read ( & self . signature )
7476 . with_context ( || format ! ( "reading signature from {}" , self . signature. display( ) ) ) ?;
7577 let signature = Signature :: unmarshall ( & sig_data)
7678 . map_err ( |e| anyhow:: anyhow!( "invalid signature: {e}" ) ) ?;
79+ let tpmt_sig: TPMT_SIGNATURE = signature
80+ . try_into ( )
81+ . map_err ( |e| anyhow:: anyhow!( "signature conversion: {e:?}" ) ) ?;
82+
83+ let nonce_tpm = TPM2B_NONCE :: default ( ) ;
7784
7885 let cp_hash = match & self . cphash_input {
7986 Some ( path) => {
8087 let data = std:: fs:: read ( path) ?;
81- Digest :: try_from ( data) . map_err ( |e| anyhow:: anyhow!( "invalid cpHash: {e}" ) ) ?
88+ let mut buf = TPM2B_DIGEST :: default ( ) ;
89+ if data. len ( ) > buf. buffer . len ( ) {
90+ anyhow:: bail!(
91+ "cpHash from {} is too large: {} bytes (maximum {} bytes)" ,
92+ path. display( ) ,
93+ data. len( ) ,
94+ buf. buffer. len( )
95+ ) ;
96+ }
97+ buf. size = data. len ( ) as u16 ;
98+ buf. buffer [ ..data. len ( ) ] . copy_from_slice ( & data) ;
99+ buf
82100 }
83- None => Digest :: default ( ) ,
101+ None => TPM2B_DIGEST :: default ( ) ,
84102 } ;
85103
86104 let policy_ref = match & self . qualification {
87- Some ( bytes) => Nonce :: try_from ( bytes. as_slice ( ) . to_vec ( ) )
88- . map_err ( |e| anyhow:: anyhow!( "qualifying data: {e}" ) ) ?,
89- None => Nonce :: default ( ) ,
105+ Some ( q) => {
106+ let data = q. as_slice ( ) ;
107+ let mut buf = TPM2B_NONCE :: default ( ) ;
108+ if data. len ( ) > buf. buffer . len ( ) {
109+ anyhow:: bail!(
110+ "qualification is too large: {} bytes (maximum {} bytes)" ,
111+ data. len( ) ,
112+ buf. buffer. len( )
113+ ) ;
114+ }
115+ buf. size = data. len ( ) as u16 ;
116+ buf. buffer [ ..data. len ( ) ] . copy_from_slice ( data) ;
117+ buf
118+ }
119+ None => TPM2B_NONCE :: default ( ) ,
90120 } ;
91121
92- let expiration = if self . expiration == 0 {
93- None
94- } else {
95- Some ( std:: time :: Duration :: from_secs ( self . expiration as u64 ) )
96- } ;
122+ // Extract data from ESYS-allocated pointers immediately, then free
123+ // them before performing any I/O that could fail and leak memory.
124+ let ( timeout_data , ticket_data ) = unsafe {
125+ let mut timeout_ptr : * mut TPM2B_TIMEOUT = std:: ptr :: null_mut ( ) ;
126+ let mut ticket_ptr : * mut TPMT_TK_AUTH = std :: ptr :: null_mut ( ) ;
97127
98- let ( timeout, ticket) = ctx
99- . policy_signed (
100- policy_session,
128+ // PolicySigned has Auth Index: None for both handles,
129+ // so all session handles are ESYS_TR_NONE.
130+ let rc = Esys_PolicySigned (
131+ raw. ptr ( ) ,
101132 auth_object,
102- Nonce :: default ( ) , // nonce_tpm
103- cp_hash,
104- policy_ref,
105- expiration,
106- signature,
107- )
108- . context ( "TPM2_PolicySigned failed" ) ?;
133+ session_handle,
134+ ESYS_TR_NONE ,
135+ ESYS_TR_NONE ,
136+ ESYS_TR_NONE ,
137+ & nonce_tpm,
138+ & cp_hash,
139+ & policy_ref,
140+ self . expiration ,
141+ & tpmt_sig,
142+ & mut timeout_ptr,
143+ & mut ticket_ptr,
144+ ) ;
145+ if rc != 0 {
146+ anyhow:: bail!( "Esys_PolicySigned failed: 0x{rc:08x}" ) ;
147+ }
109148
110- info ! ( "policy signed succeeded" ) ;
149+ let timeout_data = if !timeout_ptr. is_null ( ) {
150+ let t = & * timeout_ptr;
151+ Some ( t. buffer [ ..t. size as usize ] . to_vec ( ) )
152+ } else {
153+ None
154+ } ;
155+
156+ let ticket_data = if !ticket_ptr. is_null ( ) {
157+ let ticket = & * ticket_ptr;
158+ let bytes = std:: slice:: from_raw_parts (
159+ ticket as * const TPMT_TK_AUTH as * const u8 ,
160+ std:: mem:: size_of :: < TPMT_TK_AUTH > ( ) ,
161+ ) ;
162+ Some ( bytes. to_vec ( ) )
163+ } else {
164+ None
165+ } ;
111166
112- if let Some ( ref path) = self . timeout_out {
113- std:: fs:: write ( path, timeout. as_bytes ( ) )
167+ if !timeout_ptr. is_null ( ) {
168+ Esys_Free ( timeout_ptr as * mut _ ) ;
169+ }
170+ if !ticket_ptr. is_null ( ) {
171+ Esys_Free ( ticket_ptr as * mut _ ) ;
172+ }
173+
174+ ( timeout_data, ticket_data)
175+ } ;
176+
177+ if let ( Some ( path) , Some ( data) ) = ( & self . timeout_out , & timeout_data) {
178+ std:: fs:: write ( path, data)
114179 . with_context ( || format ! ( "writing timeout to {}" , path. display( ) ) ) ?;
115180 }
116181
117- if let Some ( ref path) = self . ticket_out {
118- let tss_ticket: TPMT_TK_AUTH = ticket
119- . try_into ( )
120- . map_err ( |e| anyhow:: anyhow!( "failed to convert ticket: {e:?}" ) ) ?;
121- let bytes = unsafe {
122- std:: slice:: from_raw_parts (
123- & tss_ticket as * const TPMT_TK_AUTH as * const u8 ,
124- std:: mem:: size_of :: < TPMT_TK_AUTH > ( ) ,
125- )
126- } ;
127- std:: fs:: write ( path, bytes)
182+ if let ( Some ( path) , Some ( data) ) = ( & self . ticket_out , & ticket_data) {
183+ std:: fs:: write ( path, data)
128184 . with_context ( || format ! ( "writing ticket to {}" , path. display( ) ) ) ?;
129185 }
130186
131187 if let Some ( ref path) = self . policy {
132- let digest = ctx
133- . policy_get_digest ( policy_session)
134- . context ( "TPM2_PolicyGetDigest failed" ) ?;
135- std:: fs:: write ( path, digest. as_bytes ( ) )
136- . with_context ( || format ! ( "writing policy digest to {}" , path. display( ) ) ) ?;
188+ let digest_data = unsafe {
189+ let mut digest_ptr: * mut TPM2B_DIGEST = std:: ptr:: null_mut ( ) ;
190+ let rc = Esys_PolicyGetDigest (
191+ raw. ptr ( ) ,
192+ session_handle,
193+ ESYS_TR_NONE ,
194+ ESYS_TR_NONE ,
195+ ESYS_TR_NONE ,
196+ & mut digest_ptr,
197+ ) ;
198+ if rc != 0 {
199+ anyhow:: bail!( "Esys_PolicyGetDigest failed: 0x{rc:08x}" ) ;
200+ }
201+
202+ if !digest_ptr. is_null ( ) {
203+ let d = & * digest_ptr;
204+ let v = d. buffer [ ..d. size as usize ] . to_vec ( ) ;
205+ Esys_Free ( digest_ptr as * mut _ ) ;
206+ Some ( v)
207+ } else {
208+ None
209+ }
210+ } ;
211+ if let Some ( ref data) = digest_data {
212+ std:: fs:: write ( path, data)
213+ . with_context ( || format ! ( "writing policy digest to {}" , path. display( ) ) ) ?;
214+ }
137215 }
138216
139- let handle: ObjectHandle = SessionHandle :: from ( policy_session) . into ( ) ;
140- crate :: session:: save_session_and_forget ( ctx, handle, & self . session ) ?;
141-
217+ raw. context_save_to_file ( session_handle, & self . session ) ?;
218+ info ! ( "policy signed succeeded" ) ;
142219 Ok ( ( ) )
143220 }
144221}
0 commit comments