Skip to content

Commit 810c452

Browse files
committed
fix(scout): skip attestation on hosts with no TPM device
Gate the attestation block on actual TPM presence so a host with no TPM skips AK/EK setup and sends discovery without AttestKeyInfo, which the API accepts when attestation_enabled is false. Signed-off-by: s3rj1k <evasive.gyron@gmail.com>
1 parent 7f188ef commit 810c452

2 files changed

Lines changed: 70 additions & 2 deletions

File tree

crates/scout/src/register.rs

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,7 +51,17 @@ pub async fn run(
5151
let mut att_key_handle_opt: Option<KeyHandle> = None;
5252
let mut tss_ctx_opt: Option<Context> = None;
5353

54-
if !is_dpu {
54+
// A host with no TPM cannot attest, so gate on actual TPM presence (not just is_dpu) and skip
55+
// the flow rather than hard failing in create_context_from_path.
56+
let do_attestation = !is_dpu && tpm::tpm_present(tpm_path);
57+
if !is_dpu && !do_attestation {
58+
tracing::warn!(
59+
tpm_path = ?tpm_path,
60+
"Host has no TPM device; skipping attestation key setup"
61+
);
62+
}
63+
64+
if do_attestation {
5565
// set the max auth fail to 256 as a stop gap measure to prevent machines from failing during
5666
// repeated reingestion cycle
5767
crate::tpm::set_tpm_max_auth_fail()?;
@@ -100,7 +110,7 @@ pub async fn run(
100110

101111
// If we are not on a DPU and have some post-registration things to do,
102112
// we do them here.
103-
if !is_dpu {
113+
if do_attestation {
104114
// If we have received back an attestation key challenge, this means
105115
// that Carbide has requested an attestation, so do it!
106116
//

crates/scout/src/tpm.rs

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,33 @@ pub(crate) fn set_tpm_max_auth_fail() -> Result<(), CarbideClientError> {
6262
Ok(())
6363
}
6464

65+
/// Kernel device paths to probe for `tpm_path`. An explicit `/dev/` path (optionally written with a
66+
/// `device` TCTI prefix) resolves to just itself, anything else falls back to the standard nodes.
67+
fn tpm_device_candidates(tpm_path: &str) -> Vec<&str> {
68+
let conf = tpm_path.strip_prefix("device:").unwrap_or(tpm_path);
69+
if conf.starts_with("/dev/") {
70+
vec![conf]
71+
} else {
72+
vec!["/dev/tpmrm0", "/dev/tpm0"]
73+
}
74+
}
75+
76+
/// True when a kernel TPM device exists for `tpm_path`. Socket TCTIs such as swtpm and mssim are not
77+
/// detected because the lab does not use them.
78+
pub(crate) fn tpm_present(tpm_path: &str) -> bool {
79+
// try_exists tells a clean absent (Ok(false)) apart from an IO error. On error we assume the
80+
// device is present rather than silently treating the host as having no TPM.
81+
let dev_exists = |path: &str| {
82+
Path::new(path).try_exists().unwrap_or_else(|e| {
83+
tracing::warn!(path = %path, error = %e, "tpm_present: cannot stat TPM device; assuming present");
84+
true
85+
})
86+
};
87+
tpm_device_candidates(tpm_path)
88+
.iter()
89+
.any(|&p| dev_exists(p))
90+
}
91+
6592
/// Clears the TPM storage hierarchies via TPM2_Clear (lockout authorization), after dictionary
6693
/// lockout setup.
6794
pub(crate) fn clear_tpm(tpm_path: &str) -> Result<(), CarbideClientError> {
@@ -164,4 +191,35 @@ mod tests {
164191
);
165192
}
166193
}
194+
195+
#[test]
196+
fn tpm_device_candidates_cases() {
197+
let cases: &[(&str, &[&str])] = &[
198+
// explicit device file, with and without the device prefix
199+
("device:/dev/tpmrm0", &["/dev/tpmrm0"]),
200+
("device:/dev/tpm0", &["/dev/tpm0"]),
201+
("/dev/tpmrm0", &["/dev/tpmrm0"]),
202+
// socket and default TCTIs fall back to the standard nodes
203+
(
204+
"mssim:host=localhost,port=2321",
205+
&["/dev/tpmrm0", "/dev/tpm0"],
206+
),
207+
("swtpm:path=/tmp/swtpm-sock", &["/dev/tpmrm0", "/dev/tpm0"]),
208+
("device:", &["/dev/tpmrm0", "/dev/tpm0"]),
209+
("", &["/dev/tpmrm0", "/dev/tpm0"]),
210+
];
211+
for (input, want) in cases {
212+
assert_eq!(tpm_device_candidates(input), *want, "input={input:?}");
213+
}
214+
}
215+
216+
#[test]
217+
fn tpm_present_probes_explicit_device_path() {
218+
// /dev/null always exists on the Linux hosts scout runs on, so an explicit path pointing at
219+
// it reports present, and a bogus /dev path reports absent.
220+
assert!(tpm_present("device:/dev/null"));
221+
assert!(tpm_present("/dev/null"));
222+
assert!(!tpm_present("device:/dev/forge_scout_nonexistent_tpm"));
223+
assert!(!tpm_present("/dev/forge_scout_nonexistent_tpm"));
224+
}
167225
}

0 commit comments

Comments
 (0)