@@ -33,6 +33,7 @@ const CONFIGFS_BASE: &str = "/sys/kernel/config/tsm/report";
3333const CONFIGFS_DEFAULT : & str = "/sys/kernel/config/tsm/report/com.intel.dcap" ;
3434const CONFIGFS_PATH_ENV : & str = "DCAP_TDX_QUOTE_CONFIGFS_PATH" ;
3535const RTMR_SYSFS_BASE : & str = "/sys/devices/virtual/misc/tdx_guest/measurements" ;
36+ const RTMR_SYSFS_PATH_ENV : & str = "DCAP_TDX_RTMR_SYSFS_PATH" ;
3637const TDX_ATTEST_CONFIG_PATH : & str = "/etc/tdx-attest.conf" ;
3738
3839const TDX_REPORT_DATA_SIZE : usize = 64 ;
@@ -166,7 +167,7 @@ static CONFIGFS_MKDIR_TRIED: Mutex<bool> = Mutex::new(false);
166167pub fn get_quote ( report_data : & TdxReportData ) -> Result < Vec < u8 > > {
167168 let _guard = TDX_LOCK . lock ( ) . map_err ( |_| TdxAttestError :: Busy ) ?;
168169
169- if is_configfs_available ( ) {
170+ if should_try_configfs ( ) {
170171 return get_quote_via_configfs ( report_data) ;
171172 }
172173
@@ -179,8 +180,35 @@ pub fn get_quote(report_data: &TdxReportData) -> Result<Vec<u8>> {
179180 ) )
180181}
181182
182- fn is_configfs_available ( ) -> bool {
183- Path :: new ( CONFIGFS_DEFAULT ) . is_dir ( ) || Path :: new ( CONFIGFS_BASE ) . is_dir ( )
183+ fn should_try_configfs ( ) -> bool {
184+ // An explicit override is authoritative. Let prepare_configfs() report an
185+ // invalid path instead of silently falling back to another quote method.
186+ std:: env:: var_os ( CONFIGFS_PATH_ENV ) . is_some ( )
187+ || Path :: new ( CONFIGFS_DEFAULT ) . is_dir ( )
188+ || Path :: new ( CONFIGFS_BASE ) . is_dir ( )
189+ }
190+
191+ /// Return whether a TDX guest provider is available through either the legacy
192+ /// misc device or the standard TSM report interface.
193+ pub fn is_tdx_available ( ) -> bool {
194+ if Path :: new ( TDX_GUEST_DEVICE ) . exists ( ) {
195+ return true ;
196+ }
197+
198+ if let Some ( path) = std:: env:: var_os ( CONFIGFS_PATH_ENV ) {
199+ return verify_configfs_provider ( Path :: new ( & path) ) . unwrap_or ( false ) ;
200+ }
201+
202+ if verify_configfs_provider ( Path :: new ( CONFIGFS_DEFAULT ) ) . unwrap_or ( false ) {
203+ return true ;
204+ }
205+
206+ fs:: read_dir ( CONFIGFS_BASE )
207+ . ok ( )
208+ . into_iter ( )
209+ . flatten ( )
210+ . filter_map ( std:: result:: Result :: ok)
211+ . any ( |entry| verify_configfs_provider ( & entry. path ( ) ) . unwrap_or ( false ) )
184212}
185213
186214fn is_vsock_available ( ) -> bool {
@@ -223,8 +251,8 @@ pub fn extend_rtmr(index: u32, _event_type: u32, digest: [u8; 48]) -> Result<()>
223251 return Err ( TdxAttestError :: InvalidRtmrIndex ( index) ) ;
224252 }
225253
226- if is_rtmr_sysfs_available ( ) {
227- return extend_rtmr_via_sysfs ( index, & digest) ;
254+ if let Some ( base ) = rtmr_sysfs_base ( ) ? {
255+ return extend_rtmr_via_sysfs ( & base , index, & digest) ;
228256 }
229257
230258 if Path :: new ( TDX_GUEST_DEVICE ) . exists ( ) {
@@ -236,24 +264,37 @@ pub fn extend_rtmr(index: u32, _event_type: u32, digest: [u8; 48]) -> Result<()>
236264 ) )
237265}
238266
239- fn is_rtmr_sysfs_available ( ) -> bool {
240- Path :: new ( RTMR_SYSFS_BASE ) . is_dir ( )
267+ fn rtmr_sysfs_base ( ) -> Result < Option < std:: path:: PathBuf > > {
268+ if let Some ( path) = std:: env:: var_os ( RTMR_SYSFS_PATH_ENV ) {
269+ let path = std:: path:: PathBuf :: from ( path) ;
270+ if path. as_os_str ( ) . len ( ) < 240 && path. is_dir ( ) {
271+ return Ok ( Some ( path) ) ;
272+ }
273+ return Err ( TdxAttestError :: NotSupported ( format ! (
274+ "invalid RTMR sysfs path from {RTMR_SYSFS_PATH_ENV}: {}" ,
275+ path. display( )
276+ ) ) ) ;
277+ }
278+
279+ Ok ( Path :: new ( RTMR_SYSFS_BASE )
280+ . is_dir ( )
281+ . then ( || std:: path:: PathBuf :: from ( RTMR_SYSFS_BASE ) ) )
241282}
242283
243- fn extend_rtmr_via_sysfs ( index : u32 , digest : & [ u8 ; 48 ] ) -> Result < ( ) > {
244- let path = format ! ( "{}/ rtmr{}:sha384" , RTMR_SYSFS_BASE , index ) ;
284+ fn extend_rtmr_via_sysfs ( base : & Path , index : u32 , digest : & [ u8 ; 48 ] ) -> Result < ( ) > {
285+ let path = base . join ( format ! ( "rtmr{index }:sha384" ) ) ;
245286
246287 let mut file = OpenOptions :: new ( )
247288 . write ( true )
248289 . open ( & path)
249- . map_err ( |e| TdxAttestError :: ExtendFailure ( format ! ( "open {path }: {e}" ) ) ) ?;
290+ . map_err ( |e| TdxAttestError :: ExtendFailure ( format ! ( "open {}: {e}" , path . display ( ) ) ) ) ?;
250291
251292 file. write_all ( digest) . map_err ( |e| match e. raw_os_error ( ) {
252293 Some ( libc:: EINVAL ) => TdxAttestError :: InvalidRtmrIndex ( index) ,
253294 Some ( libc:: EPERM ) | Some ( libc:: EACCES ) => {
254295 TdxAttestError :: ExtendFailure ( format ! ( "permission denied for RTMR {index}" ) )
255296 }
256- _ => TdxAttestError :: ExtendFailure ( format ! ( "write {path }: {e}" ) ) ,
297+ _ => TdxAttestError :: ExtendFailure ( format ! ( "write {}: {e}" , path . display ( ) ) ) ,
257298 } )
258299}
259300
@@ -334,7 +375,10 @@ fn get_quote_via_configfs(report_data: &TdxReportData) -> Result<Vec<u8>> {
334375
335376fn prepare_configfs ( ) -> Result < String > {
336377 if let Ok ( path) = std:: env:: var ( CONFIGFS_PATH_ENV ) {
337- if path. len ( ) < 240 && Path :: new ( & path) . is_dir ( ) && verify_configfs_provider ( & path) ? {
378+ if path. len ( ) < 240
379+ && Path :: new ( & path) . is_dir ( )
380+ && verify_configfs_provider ( Path :: new ( & path) ) ?
381+ {
338382 return Ok ( path) ;
339383 }
340384 return Err ( TdxAttestError :: NotSupported ( format ! (
@@ -343,7 +387,7 @@ fn prepare_configfs() -> Result<String> {
343387 }
344388
345389 let default_path = CONFIGFS_DEFAULT ;
346- if Path :: new ( default_path) . is_dir ( ) && verify_configfs_provider ( default_path) ? {
390+ if Path :: new ( default_path) . is_dir ( ) && verify_configfs_provider ( Path :: new ( default_path) ) ? {
347391 return Ok ( default_path. to_string ( ) ) ;
348392 }
349393
@@ -369,7 +413,7 @@ fn prepare_configfs() -> Result<String> {
369413 let provider_path = format ! ( "{}/provider" , default_path) ;
370414 for i in 0 ..5 {
371415 if Path :: new ( & provider_path) . exists ( ) {
372- if verify_configfs_provider ( default_path) ? {
416+ if verify_configfs_provider ( Path :: new ( default_path) ) ? {
373417 return Ok ( default_path. to_string ( ) ) ;
374418 }
375419 break ;
@@ -383,10 +427,11 @@ fn prepare_configfs() -> Result<String> {
383427 ) ) )
384428}
385429
386- fn verify_configfs_provider ( path : & str ) -> Result < bool > {
387- let provider_path = format ! ( "{}/provider" , path) ;
388- let provider = fs:: read_to_string ( & provider_path)
389- . map_err ( |e| TdxAttestError :: Unexpected ( format ! ( "read {provider_path}: {e}" ) ) ) ?;
430+ fn verify_configfs_provider ( path : & Path ) -> Result < bool > {
431+ let provider_path = path. join ( "provider" ) ;
432+ let provider = fs:: read_to_string ( & provider_path) . map_err ( |e| {
433+ TdxAttestError :: Unexpected ( format ! ( "read {}: {e}" , provider_path. display( ) ) )
434+ } ) ?;
390435
391436 Ok ( provider. trim ( ) . starts_with ( "tdx_guest" ) )
392437}
@@ -647,3 +692,39 @@ fn parse_qgs_get_quote_response(data: &[u8]) -> Result<Vec<u8>> {
647692
648693 Ok ( quote)
649694}
695+
696+ #[ cfg( test) ]
697+ mod tests {
698+ use super :: * ;
699+
700+ #[ test]
701+ fn recognizes_tdx_configfs_provider ( ) {
702+ let temp = tempfile:: tempdir ( ) . unwrap ( ) ;
703+ fs:: write ( temp. path ( ) . join ( "provider" ) , "tdx_guest_sim\n " ) . unwrap ( ) ;
704+ assert ! ( verify_configfs_provider( temp. path( ) ) . unwrap( ) ) ;
705+
706+ fs:: write ( temp. path ( ) . join ( "provider" ) , "sev_guest\n " ) . unwrap ( ) ;
707+ assert ! ( !verify_configfs_provider( temp. path( ) ) . unwrap( ) ) ;
708+ }
709+
710+ #[ test]
711+ fn invalid_configfs_override_does_not_fall_back ( ) {
712+ let previous = std:: env:: var_os ( CONFIGFS_PATH_ENV ) ;
713+ std:: env:: set_var (
714+ CONFIGFS_PATH_ENV ,
715+ "/definitely/not/a/real/tdx-configfs-provider" ,
716+ ) ;
717+
718+ let result = get_quote ( & [ 0u8 ; 64 ] ) ;
719+
720+ match previous {
721+ Some ( value) => std:: env:: set_var ( CONFIGFS_PATH_ENV , value) ,
722+ None => std:: env:: remove_var ( CONFIGFS_PATH_ENV ) ,
723+ }
724+ assert ! ( matches!(
725+ result,
726+ Err ( TdxAttestError :: NotSupported ( message) )
727+ if message. contains( "invalid configfs path from env" )
728+ ) ) ;
729+ }
730+ }
0 commit comments