From dac5b489752846a2ed4d1d3b4cb9cecaa20a9734 Mon Sep 17 00:00:00 2001 From: Philip Tricca Date: Thu, 10 Oct 2024 16:05:04 -0700 Subject: [PATCH] yhsm: Add command for dealing with the audit log. YubiCo docs state that only 1-60 are valid index values. I've set this value to well beyond 60 in testing. These docs don't say what happens when the index (u16) rolls over. https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-cmd-reference.html#set-log-index-command --- src/bin/yhsm.rs | 72 ++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 1 deletion(-) diff --git a/src/bin/yhsm.rs b/src/bin/yhsm.rs index 3b168be..b14b764 100644 --- a/src/bin/yhsm.rs +++ b/src/bin/yhsm.rs @@ -9,7 +9,7 @@ use log::LevelFilter; use std::{path::PathBuf, str::FromStr}; use yubihsm::{ object::{Id, Type}, - Client, Connector, Credentials, UsbConfig, + AuditOption, Client, Connector, Credentials, UsbConfig, }; #[derive(Parser, Debug)] @@ -29,8 +29,48 @@ struct Args { verbose: bool, } +#[derive(Subcommand, Clone, Debug, PartialEq)] +enum LogCommand { + /// dump log serialized to JSON + Json, + + /// Set the index of the last entry consumed from the HSM audit log. + /// This causes entries with a lower index to be deleted. + SetIndex { + /// Last entry consumed. + index: u16, + }, +} + +#[derive(Subcommand, Clone, Debug, PartialEq)] +enum AuditCommand { + /// Set the `force-audit` option to the disabled state. + Disable, + + /// Set the `force-audit` option to the enabled state. + Enable, + + /// Set the `force-audit` option to the locked state. + Lock, + + /// Query the state of the `force-audit` option. + Query, + + /// Manage the audit log. + Log { + #[command(subcommand)] + command: Option, + }, +} + #[derive(Subcommand, Debug, PartialEq)] enum Command { + /// Get / Set the state of the `force-audit` option. + Audit { + #[command(subcommand)] + command: AuditCommand, + }, + /// Export an object identified under wrap. Backup { /// Object ID: https://developers.yubico.com/YubiHSM2/Concepts/Object_ID.html @@ -98,6 +138,36 @@ fn main() -> Result<()> { let client = Client::open(connector, credentials, true)?; match args.command { + Command::Audit { command } => match command { + AuditCommand::Disable => { + Ok(client.set_force_audit_option(AuditOption::Off)?) + } + AuditCommand::Enable => { + Ok(client.set_force_audit_option(AuditOption::On)?) + } + AuditCommand::Lock => { + todo!("implement Command::Audit::{:?}", command); + } + AuditCommand::Query => { + let state = client.get_force_audit_option()?; + println!("{:?}", state); + Ok(()) + } + AuditCommand::Log { command } => match command { + None | Some(LogCommand::Json) => { + let entries = client.get_log_entries()?; + if entries.entries.last().is_some() { + println!("{}", serde_json::to_string_pretty(&entries)?); + Ok(()) + } else { + Err(anyhow::anyhow!("audit log contains no entries")) + } + } + Some(LogCommand::SetIndex { index }) => { + Ok(client.set_log_index(index)?) + } + }, + }, Command::Backup { id, kind, file } => { // this is a bit weird but necessary because the Type type // returns () on error, not a type implementing std::Error