1use super::CommandError;
6use crate::{
7 cli::{get_auth, SubCommand},
8 context::ContextCache,
9 device::{self, Auth, Device},
10 uri::Uri,
11};
12use argh::FromArgs;
13use std::{cell::RefCell, rc::Rc, str::FromStr};
14use tpm2_protocol::data::TpmSe;
15
16#[derive(FromArgs, Debug)]
18#[argh(subcommand, name = "delete")]
19pub struct Delete {
20 #[argh(positional)]
22 pub inputs: Vec<String>,
23
24 #[argh(option, arg_name = "auth", short = 'a')]
27 pub auth: Option<String>,
28
29 #[argh(option, arg_name = "auth", short = 'm', long = "hmac-auth")]
32 pub hmac_auth: Option<String>,
33}
34
35impl SubCommand for Delete {
36 fn run(
37 &self,
38 device: Option<Rc<RefCell<Device>>>,
39 context: &mut ContextCache,
40 _plain: bool,
41 ) -> Result<(), CommandError> {
42 let auth = match (self.auth.as_ref(), self.hmac_auth.as_ref()) {
43 (Some(_), Some(_)) => {
44 return Err(CommandError::InvalidInput(
45 "Cannot use --auth and --hmac-auth at the same time".to_string(),
46 ));
47 }
48 (Some(auth_str), None) => get_auth(
49 Some(auth_str),
50 "TPM2SH_AUTH",
51 &context.session_map,
52 &[TpmSe::Policy],
53 )?,
54 (None, Some(hmac_auth_str)) => get_auth(
55 Some(hmac_auth_str),
56 "TPM2SH_HMAC_AUTH",
57 &context.session_map,
58 &[TpmSe::Hmac],
59 )?,
60 (None, None) => {
61 let auth = get_auth(None, "TPM2SH_AUTH", &context.session_map, &[TpmSe::Policy])?;
62 if matches!(&auth, Auth::Password(p) if p.is_empty()) {
63 get_auth(
64 None,
65 "TPM2SH_HMAC_AUTH",
66 &context.session_map,
67 &[TpmSe::Hmac],
68 )?
69 } else {
70 auth
71 }
72 }
73 };
74
75 let uris: Vec<Uri> = self
76 .inputs
77 .iter()
78 .map(|s| Uri::from_str(s))
79 .collect::<Result<_, _>>()?;
80
81 let (device_ops, local_ops): (Vec<_>, Vec<_>) = uris
82 .into_iter()
83 .partition(|uri| matches!(uri, Uri::Tpm(_) | Uri::Session(_)));
84
85 for uri in local_ops {
86 match uri {
87 Uri::Context(ref grip) => {
88 context.remove_context(grip)?;
89 writeln!(context.writer, "{uri}")?;
90 }
91 Uri::Path(_) | Uri::Password(_) => {
92 return Err(CommandError::InvalidInput(uri.to_string()));
93 }
94 Uri::Tpm(_) | Uri::Session(_) => unreachable!(),
95 }
96 }
97
98 if !device_ops.is_empty() {
99 device::with_device(device, |dev| -> Result<(), CommandError> {
100 for uri in device_ops {
101 match uri {
102 Uri::Session(_) => {
103 let uri_str = uri.to_string();
104 if let Some(session) = context.session_map.remove(&uri_str)? {
105 if let Err(err) = dev.flush_session(session.context) {
106 log::warn!("{uri}: {err}");
107 }
108 }
109 writeln!(context.writer, "{uri}")?;
110 }
111 Uri::Tpm(_) => {
112 let handle = context.delete(dev, &uri, std::slice::from_ref(&auth))?;
113 writeln!(context.writer, "tpm://{handle:08x}")?;
114 }
115 Uri::Context(_) | Uri::Path(_) | Uri::Password(_) => unreachable!(),
116 }
117 }
118 Ok(())
119 })?;
120 }
121
122 Ok(())
123 }
124
125 fn is_local(&self) -> bool {
126 !self
127 .inputs
128 .iter()
129 .any(|s| s.starts_with("tpm://") || s.starts_with("session://"))
130 }
131}