From cb78527176c2c13cf6123304bcb96459414f4244 Mon Sep 17 00:00:00 2001 From: Susanne Westphal Date: Wed, 18 Dec 2024 13:21:27 +0000 Subject: [PATCH] Add empty action for execute signed command --- crates/rrg/Cargo.toml | 2 + crates/rrg/src/action.rs | 7 ++ .../rrg/src/action/execute_signed_command.rs | 115 ++++++++++++++++++ proto/rrg.proto | 2 + 4 files changed, 126 insertions(+) create mode 100644 crates/rrg/src/action/execute_signed_command.rs diff --git a/crates/rrg/Cargo.toml b/crates/rrg/Cargo.toml index 5de54742..c0e4c582 100644 --- a/crates/rrg/Cargo.toml +++ b/crates/rrg/Cargo.toml @@ -22,6 +22,7 @@ default = [ "action-list_winreg_values", "action-list_winreg_keys", "action-query_wmi", + "action-execute_signed_command", ] action-get_system_metadata = [] @@ -39,6 +40,7 @@ action-get_winreg_value = [] action-list_winreg_values = [] action-list_winreg_keys = [] action-query_wmi = [] +action-execute_signed_command = [] test-setfattr = [] test-chattr = [] diff --git a/crates/rrg/src/action.rs b/crates/rrg/src/action.rs index 23db0798..16e0fd95 100644 --- a/crates/rrg/src/action.rs +++ b/crates/rrg/src/action.rs @@ -51,6 +51,9 @@ pub mod list_winreg_keys; #[cfg(feature = "action-query_wmi")] pub mod query_wmi; +#[cfg(feature = "action-execute_signed_command")] +pub mod execute_signed_command; + use log::info; /// Dispatches the given `request` to an appropriate action handler. @@ -125,6 +128,10 @@ where QueryWmi => { handle(session, request, self::query_wmi::handle) } + #[cfg(feature = "action-execute_signed_command")] + ExecuteSignedCommand => { + handle(session, request, self::execute_signed_command::handle) + } // We allow `unreachable_patterns` because otherwise we get a warning if // we compile with all the actions enabled. #[allow(unreachable_patterns)] diff --git a/crates/rrg/src/action/execute_signed_command.rs b/crates/rrg/src/action/execute_signed_command.rs new file mode 100644 index 00000000..37bcd0e7 --- /dev/null +++ b/crates/rrg/src/action/execute_signed_command.rs @@ -0,0 +1,115 @@ +// Copyright 2024 Google LLC +// +// Use of this source code is governed by an MIT-style license that can be found +// in the LICENSE file or at https://opensource.org/licenses/MIT. + +use std::{io::{Read, Write}, os::unix::process::ExitStatusExt, process::{Command, ExitStatus}}; + +use protobuf::Message; + +/// Arguments of the `execute_signed_command` action. +pub struct Args { + raw_command: Vec, + command: rrg_proto::execute_signed_command::SignedCommand, + stdin: Stdin, + ed25519_signature: ed25519_dalek::Signature, + timeout: std::time::Duration, +} + +/// Result of the `execute_signed_command` action. +pub struct Item { + /// Exit status of the command subprocess. + exit_status: ExitStatus, + /// Standard output of the command executiom. + stdout: Vec, + /// Wheather standard output is truncated. + truncated_stdout: bool, + /// Standard error of the command executiom. + stderr: Vec, + /// Wheather stderr is truncated. + truncated_stderr: bool, +} + +enum Stdin { + NONE, + UNSIGNED(Vec), + SIGNED(Vec), +} + + +/// Handles invocations of the `execute_signed_command` action. +pub fn handle(session: &mut S, mut args: Args) -> crate::session::Result<()> +where + S: crate::session::Session, +{ + // TODO(s-westphal): Add implementation. + Ok(()) +} + +impl crate::request::Args for Args { + + type Proto = rrg_proto::execute_signed_command::Args; + + fn from_proto(mut proto: Self::Proto) -> Result { + use crate::request::ParseArgsError; + + let raw_signature= proto.take_command_ed25519_signature(); + + let ed25519_signature = ed25519_dalek::Signature::try_from(&raw_signature[..]) + .map_err(|error| ParseArgsError::invalid_field("command_ed25519_signature", error))?; + + let raw_command = proto.take_command(); + let mut command = rrg_proto::execute_signed_command::SignedCommand::parse_from_bytes(&raw_command) + .map_err(|error| ParseArgsError::invalid_field("command", error))?; + + + let stdin: Stdin; + if command.has_signed_stdin() { + stdin = Stdin::SIGNED(command.take_signed_stdin()); + } else if command.unsigned_stdin() && !proto.unsigned_stdin.is_empty() { + stdin = Stdin::UNSIGNED(proto.take_unsigned_stdin()); + } else { + stdin = Stdin::NONE + } + + let timeout = std::time::Duration::try_from(proto.take_timeout()) + .map_err(|error| ParseArgsError::invalid_field("command", error))?; + + Ok(Args { + raw_command, + command, + ed25519_signature, + stdin, + timeout, + }) + } +} + +impl crate::response::Item for Item { + + type Proto = rrg_proto::execute_signed_command::Result; + + fn into_proto(self) -> Self::Proto { + + let mut proto = rrg_proto::execute_signed_command::Result::new(); + + if let Some(exit_code) = self.exit_status.code() { + proto.set_exit_code(exit_code); + } + + #[cfg(target_family = "unix")] + { + if let Some(exit_signal) = self.exit_status.signal() { + proto.set_exit_signal(exit_signal); + } + } + + proto.set_stdout(self.stdout); + proto.set_stdout_truncated(self.truncated_stdout); + + proto.set_stderr(self.stderr); + proto.set_stderr_truncated(self.truncated_stderr); + + proto + } +} \ No newline at end of file diff --git a/proto/rrg.proto b/proto/rrg.proto index ded2ffde..4f40993f 100644 --- a/proto/rrg.proto +++ b/proto/rrg.proto @@ -48,6 +48,8 @@ enum Action { QUERY_WMI = 16; /// Grep the specified file for a pattern. GREP_FILE_CONTENTS = 17; + /// Execute a signed command. + EXECUTE_SIGNED_COMMAND = 18; // TODO: Define more actions that should be supported.