cli/command/
reset_lock.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, ContextError, DeviceError};
6use crate::{
7    cli::{get_auth, SubCommand},
8    context::ContextCache,
9    device::{self, Auth, Device},
10};
11use argh::FromArgs;
12use std::{cell::RefCell, rc::Rc};
13use tpm2_protocol::{
14    data::{TpmCc, TpmRcBase, TpmRh, TpmSe},
15    message::TpmDictionaryAttackLockResetCommand,
16};
17
18/// Resets the dictionary attack lockout counter.
19#[derive(FromArgs, Debug)]
20#[argh(subcommand, name = "reset-lock")]
21pub struct ResetLock {
22    /// auth for the lockout hierarchy: 'password://<hex>' or 'session://<handle>'
23    /// Uses TPM2SH_AUTH environment variable if not set.
24    #[argh(option, arg_name = "auth", short = 'a')]
25    pub auth: Option<String>,
26
27    /// hmac auth: 'password://<hex>' or 'session://<handle>'
28    /// Uses TPM2SH_HMAC_AUTH environment variable if not set.
29    #[argh(option, arg_name = "auth", short = 'm', long = "hmac-auth")]
30    pub hmac_auth: Option<String>,
31}
32
33impl SubCommand for ResetLock {
34    fn run(
35        &self,
36        device: Option<Rc<RefCell<Device>>>,
37        context: &mut ContextCache,
38        _plain: bool,
39    ) -> Result<(), CommandError> {
40        let auth = match (self.auth.as_ref(), self.hmac_auth.as_ref()) {
41            (Some(_), Some(_)) => {
42                return Err(CommandError::InvalidInput(
43                    "Cannot use --auth and --hmac-auth at the same time".to_string(),
44                ));
45            }
46            (Some(auth_str), None) => get_auth(
47                Some(auth_str),
48                "TPM2SH_AUTH",
49                &context.session_map,
50                &[TpmSe::Policy],
51            )?,
52            (None, Some(hmac_auth_str)) => get_auth(
53                Some(hmac_auth_str),
54                "TPM2SH_HMAC_AUTH",
55                &context.session_map,
56                &[TpmSe::Hmac],
57            )?,
58            (None, None) => {
59                let auth = get_auth(None, "TPM2SH_AUTH", &context.session_map, &[TpmSe::Policy])?;
60                if matches!(&auth, Auth::Password(p) if p.is_empty()) {
61                    get_auth(
62                        None,
63                        "TPM2SH_HMAC_AUTH",
64                        &context.session_map,
65                        &[TpmSe::Hmac],
66                    )?
67                } else {
68                    auth
69                }
70            }
71        };
72        device::with_device(device, |device| {
73            let command = TpmDictionaryAttackLockResetCommand {
74                lock_handle: (TpmRh::Lockout as u32).into(),
75            };
76            let handles = [TpmRh::Lockout as u32];
77            let auths = &[auth];
78
79            let (resp, _) = match context.execute(device, &command, &handles, auths) {
80                Ok(result) => result,
81                Err(ContextError::Device(DeviceError::TpmRc(rc)))
82                    if rc.base() == TpmRcBase::Lockout =>
83                {
84                    return Err(CommandError::DictionaryAttackLocked);
85                }
86                Err(e) => return Err(e.into()),
87            };
88
89            resp.DictionaryAttackLockReset()
90                .map_err(|_| CommandError::ResponseMismatch(TpmCc::DictionaryAttackLockReset))?;
91
92            writeln!(context.writer, "done")?;
93
94            Ok(())
95        })
96    }
97}