Skip to content

Commit

Permalink
Implement the list_winreg_values action.
Browse files Browse the repository at this point in the history
  • Loading branch information
panhania authored Jun 26, 2024
2 parents 3764915 + a105aeb commit 94083e6
Show file tree
Hide file tree
Showing 7 changed files with 185 additions and 0 deletions.
1 change: 1 addition & 0 deletions crates/rrg-proto/build.rs
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ const PROTOS: &'static [&'static str] = &[
"../../proto/rrg/action/list_connections.proto",
"../../proto/rrg/action/list_interfaces.proto",
"../../proto/rrg/action/list_mounts.proto",
"../../proto/rrg/action/list_winreg_values.proto",
];

fn main() {
Expand Down
2 changes: 2 additions & 0 deletions crates/rrg/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ default = [
"action-list_interfaces",
"action-list_mounts",
"action-get_winreg_value",
"action-list_winreg_values",
]

action-get_system_metadata = []
Expand All @@ -25,6 +26,7 @@ action-list_connections = []
action-list_interfaces = []
action-list_mounts = []
action-get_winreg_value = []
action-list_winreg_values = []

test-setfattr = []
test-chattr = []
Expand Down
7 changes: 7 additions & 0 deletions crates/rrg/src/action.rs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ pub mod list_mounts;
#[cfg(feature = "action-get_winreg_value")]
pub mod get_winreg_value;

#[cfg(feature = "action-list_winreg_values")]
pub mod list_winreg_values;

use log::info;

/// Dispatches the given `request` to an appropriate action handler.
Expand Down Expand Up @@ -97,6 +100,10 @@ where
GetWinregValue => {
handle(session, request, self::get_winreg_value::handle)
}
#[cfg(feature = "action-list_winreg_values")]
ListWinregValues => {
handle(session, request, self::list_winreg_values::handle)
}
// We allow `unreachable_patterns` because otherwise we get a warning if
// we compile with all the actions enabled.
#[allow(unreachable_patterns)]
Expand Down
141 changes: 141 additions & 0 deletions crates/rrg/src/action/list_winreg_values.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
// 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.

/// Arguments of the `list_winreg_values` action.
#[cfg(target_family = "windows")]
pub struct Args {
/// Root predefined key of the key to list values of.
root: winreg::PredefinedKey,
/// Key relative to `root` to list values of.
key: std::ffi::OsString,
}

/// A result of the `list_winreg_values` action.
#[cfg(target_family = "windows")]
struct Item {
/// Root predefined key of the listed value.
root: winreg::PredefinedKey,
/// Key relative to `root` of the listed value.
key: std::ffi::OsString,
/// Listed value.
value: winreg::Value,
}

/// Handles invocations of the `list_winreg_values` action.
#[cfg(target_family = "windows")]
pub fn handle<S>(session: &mut S, args: Args) -> crate::session::Result<()>
where
S: crate::session::Session,
{
let key = args.root.open(&args.key)
.map_err(crate::session::Error::action)?;

let info = key.info()
.map_err(crate::session::Error::action)?;

for value in info.values() {
let value = match value {
Ok(value) => value,
Err(error) => {
log::error! {
"failed to list value for key '{:?}': {}",
args.key, error,
};
continue;
}
};

session.reply(Item {
root: args.root,
// TODO(@panhania): Add support for case-correcting the key.
key: args.key.clone(),
value,
})?;
}

Ok(())
}

/// Handles invocations of the `list_winreg_values` action.
#[cfg(target_family = "unix")]
pub fn handle<S>(_: &mut S, _: ()) -> crate::session::Result<()>
where
S: crate::session::Session,
{
use std::io::{Error, ErrorKind};
Err(crate::session::Error::action(Error::from(ErrorKind::Unsupported)))
}

#[cfg(target_family = "windows")]
impl crate::request::Args for Args {

type Proto = rrg_proto::list_winreg_values::Args;

fn from_proto(mut proto: Self::Proto) -> Result<Args, crate::request::ParseArgsError> {
let root = match proto.root.enum_value() {
Ok(root) => winreg::PredefinedKey::try_from(root),
Err(value) => Err(rrg_proto::ParseWinregPredefinedKeyError { value }),
}.map_err(|error| {
crate::request::ParseArgsError::invalid_field("root", error)
})?;

Ok(Args {
root,
key: std::ffi::OsString::from(proto.take_key()),
})
}
}

#[cfg(target_family = "windows")]
impl crate::response::Item for Item {

type Proto = rrg_proto::list_winreg_values::Result;

fn into_proto(self) -> Self::Proto {
let mut proto = rrg_proto::list_winreg_values::Result::new();
proto.set_root(self.root.into());
proto.set_key(self.key.to_string_lossy().into_owned());
proto.set_value(self.value.into());

proto
}
}

#[cfg(test)]
#[cfg(target_family = "windows")]
mod tests {

use super::*;

#[test]
fn handle_non_existent() {
let args = Args {
root: winreg::PredefinedKey::LocalMachine,
key: std::ffi::OsString::from("FOOWARE"),
};

let mut session = crate::session::FakeSession::new();
assert!(handle(&mut session, args).is_err());
}

#[test]
fn handle_ok() {
let args = Args {
root: winreg::PredefinedKey::LocalMachine,
key: std::ffi::OsString::from("SOFTWARE\\Microsoft\\Windows NT\\CurrentVersion"),
};

let mut session = crate::session::FakeSession::new();
assert!(handle(&mut session, args).is_ok());

let value_names = session.replies::<Item>()
.map(|item| item.value.name.clone())
.collect::<Vec<_>>();

assert!(value_names.contains(&"CurrentBuild".into()));
assert!(value_names.contains(&"CurrentType".into()));
assert!(value_names.contains(&"CurrentVersion".into()));
}
}
4 changes: 4 additions & 0 deletions crates/rrg/src/request.rs
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,8 @@ pub enum Action {
GetFilesystemTimeline,
// Get a value from the Windows Registry (Windows-only).
GetWinregValue,
/// List values of the Windows Registry key (Windows-only).
ListWinregValues,
}

impl std::fmt::Display for Action {
Expand All @@ -60,6 +62,7 @@ impl std::fmt::Display for Action {
Action::ListUsers => write!(fmt, "list_users"),
Action::GetFilesystemTimeline => write!(fmt, "get_filesystem_timeline"),
Action::GetWinregValue => write!(fmt, "get_winreg_value"),
Action::ListWinregValues => write!(fmt, "list_winreg_values"),
}
}
}
Expand Down Expand Up @@ -105,6 +108,7 @@ impl TryFrom<rrg_proto::rrg::Action> for Action {
LIST_USERS => Ok(Action::ListUsers),
GET_FILESYSTEM_TIMELINE => Ok(Action::GetFilesystemTimeline),
GET_WINREG_VALUE => Ok(Action::GetWinregValue),
LIST_WINREG_VALUES => Ok(Action::ListWinregValues),
_ => {
let value = protobuf::Enum::value(&proto);
Err(UnknownAction { value })
Expand Down
2 changes: 2 additions & 0 deletions proto/rrg.proto
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ enum Action {
LIST_MOUNTS = 12;
// Get a value from the Windows Registry (Windows-only).
GET_WINREG_VALUE = 13;
// List values of the Windows Registry key (Windows-only).
LIST_WINREG_VALUES = 14;

// TODO: Define more actions that should be supported.

Expand Down
28 changes: 28 additions & 0 deletions proto/rrg/action/list_winreg_values.proto
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
// 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.
syntax = "proto3";

package rrg.action.list_winreg_values;

import "rrg/winreg.proto";

message Args {
// Root predefined key of the key to list values of.
rrg.winreg.PredefinedKey root = 1;

// Key relative to `root` to list values of.
string key = 2;
}

message Result {
// Root predefined key of the listed value.
rrg.winreg.PredefinedKey root = 1;

// Key relative to `root` of the listed value.
string key = 2;

// Listed value.
rrg.winreg.Value value = 3;
}

0 comments on commit 94083e6

Please sign in to comment.