1use crate::{
6 cli::SubCommand,
7 command::CommandError,
8 device::{with_device, Device},
9 handle::HandlePattern,
10 job::Job,
11 vtpm::VtpmKey,
12};
13use clap::Args;
14use std::collections::VecDeque;
15use tpm2_protocol::{
16 data::{TpmHt, TpmRh, TpmtPublic},
17 TpmHandle,
18};
19
20#[derive(Args, Debug)]
22pub struct Delete {
23 pub input: String,
25}
26
27impl Delete {
28 fn delete_vtpm_children(
30 job: &mut Job,
31 dev: &mut Device,
32 first_public: &TpmtPublic,
33 deleted: &mut Vec<u32>,
34 ) -> Result<(), CommandError> {
35 let mut ancestor_list = VecDeque::new();
36 ancestor_list.push_back(first_public.clone());
37
38 while let Some(parent_public) = ancestor_list.pop_front() {
39 let children_to_process: Vec<(u32, TpmtPublic)> = job
40 .cache
41 .key_iter()
42 .filter(|(_, key)| key.parent.inner == parent_public)
43 .map(|(vhandle, key)| (*vhandle, key.public.inner.clone()))
44 .collect();
45
46 for (child_vhandle, child_public) in children_to_process {
47 if deleted.contains(&child_vhandle)
48 || !job.cache.contexts.contains_key(&child_vhandle)
49 {
50 continue;
51 }
52 job.cache.remove(dev, child_vhandle)?;
53 deleted.push(child_vhandle);
54 ancestor_list.push_back(child_public);
55 }
56 }
57 Ok(())
58 }
59}
60
61impl SubCommand for Delete {
62 fn run(&self, job: &mut Job) -> Result<(), CommandError> {
63 if let Some(pattern) = self.input.strip_prefix("tpm:") {
64 delete_tpm_handles(job, pattern)
65 } else if let Some(pattern) = self.input.strip_prefix("vtpm:") {
66 delete_vtpm_handles(job, pattern)
67 } else {
68 Err(CommandError::InvalidInput(self.input.to_string()))
69 }
70 }
71}
72
73fn delete_tpm_handles(job: &mut Job, pattern_str: &str) -> Result<(), CommandError> {
75 with_device(job.device.clone(), |dev| {
76 let pattern = HandlePattern::new(pattern_str)?;
77
78 for class in [
79 TpmHt::HmacSession,
80 TpmHt::PolicySession,
81 TpmHt::Transient,
82 TpmHt::Persistent,
83 ] {
84 let handles = dev.fetch_handles((class as u32) << 24)?;
85 for handle in handles.into_iter().filter(|&h| pattern.matches(h.value())) {
86 match class {
87 TpmHt::HmacSession | TpmHt::PolicySession | TpmHt::Transient => {
88 dev.flush_context(TpmHandle(handle.value()))?;
89 if class == TpmHt::Transient {
90 job.cache.untrack(handle.value());
91 }
92 }
93 TpmHt::Persistent => {
94 let persistent_handle = TpmHandle(handle.value());
95 let auth_handle: TpmHandle =
96 if (handle.value() & 0x00FF_FFFF) <= 0x007F_FFFF {
97 (TpmRh::Owner as u32).into()
98 } else {
99 (TpmRh::Platform as u32).into()
100 };
101 let auths = vec![job.auth_list.first().cloned().unwrap_or_default()];
102 job.evict_control(
103 auth_handle,
104 persistent_handle,
105 persistent_handle,
106 &auths,
107 )?;
108 }
109 _ => {}
110 }
111 writeln!(job.writer, "{handle}")?;
112 }
113 }
114 Ok(())
115 })
116}
117
118fn delete_vtpm_handles(job: &mut Job, pattern_str: &str) -> Result<(), CommandError> {
120 let pattern = HandlePattern::new(pattern_str)?;
121
122 let matched_handles: Vec<u32> = job
123 .cache
124 .contexts
125 .keys()
126 .copied()
127 .filter(|&h| pattern.matches(h))
128 .collect();
129
130 if matched_handles.is_empty() {
131 return Ok(());
132 }
133
134 with_device(job.device.clone(), |dev| {
135 let mut deleted_vhandles = Vec::new();
136
137 for vhandle in matched_handles {
138 if deleted_vhandles.contains(&vhandle) || !job.cache.contexts.contains_key(&vhandle) {
139 continue;
140 }
141
142 let maybe_public = if let Some(context) = job.cache.contexts.get(&vhandle) {
143 context
144 .as_any()
145 .downcast_ref::<VtpmKey>()
146 .map(|key| key.public.inner.clone())
147 } else {
148 continue;
149 };
150
151 job.cache.remove(dev, vhandle)?;
152 writeln!(job.writer, "vtpm:{vhandle:08x}")?;
153 deleted_vhandles.push(vhandle);
154
155 if let Some(public_key) = maybe_public {
156 Delete::delete_vtpm_children(job, dev, &public_key, &mut deleted_vhandles)?;
157
158 let deleted_children: Vec<u32> = deleted_vhandles
159 .iter()
160 .filter(|&&h| {
161 let vhandle_pos = deleted_vhandles.iter().position(|&x| x == vhandle);
162 let h_pos = deleted_vhandles.iter().position(|&x| x == h);
163 if let (Some(vp), Some(hp)) = (vhandle_pos, h_pos) {
164 hp > vp
165 } else {
166 false
167 }
168 })
169 .copied()
170 .collect();
171
172 for child_vhandle in deleted_children {
173 writeln!(job.writer, "vtpm:{child_vhandle:08x}")?;
174 }
175 }
176 }
177 Ok(())
178 })
179}