cli/command/
delete.rs

1// SPDX-License-Identifier: GPL-3-0-or-later
2// Copyright (c) 2024-2025 Jarkko Sakkinen
3// Copyright (c) 2025 Opinsys Oy
4
5use 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/// Deletes TPM objects, and cached keys and sessions.
17#[derive(FromArgs, Debug)]
18#[argh(subcommand, name = "delete")]
19pub struct Delete {
20    /// inputs: 'tpm://<handle>', 'key://<name grip>', or 'session://<handle>'
21    #[argh(positional)]
22    pub inputs: Vec<String>,
23
24    /// auth for the object: 'password://<hex>' or 'session://<handle>'
25    /// Uses TPM2SH_AUTH environment variable if not set.
26    #[argh(option, arg_name = "auth", short = 'a')]
27    pub auth: Option<String>,
28
29    /// hmac auth: 'password://<hex>' or 'session://<handle>'
30    /// Uses TPM2SH_HMAC_AUTH environment variable if not set.
31    #[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}