Skip to content

Commit

Permalink
Final touches before the 3490 release. (#1125)
Browse files Browse the repository at this point in the history
* Minor fixes in the client builder logic.
* Version bumped to 3490, CHANGELOG updated.
* Further RRG development.
  • Loading branch information
mbushkov authored Feb 27, 2025
1 parent 4b19726 commit 44adfce
Show file tree
Hide file tree
Showing 19 changed files with 65 additions and 64 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,14 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

### Removed

### Changed

## [3.4.9.0] - 2025-02-27

### Added

* Added support for listing `%SystemDrive%\Users` as a supplementary mechanism
for collecting user profiles on Windows (additionally to using data from the
registry).
Expand Down
2 changes: 1 addition & 1 deletion compose.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ services:
- ./docker_config_files/mysql/init.sh:/docker-entrypoint-initdb.d/init.sh
- db_data:/var/lib/mysql:rw
ports:
- "3306:3306"
- "3306:3306"
expose:
- "3306"
networks:
Expand Down
29 changes: 11 additions & 18 deletions grr/client_builder/grr_response_client_builder/client_build.py
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,17 @@ def BuildTemplate(self, context=None, output=None):
# The repacker uses this context to chose the .msi extension for the
# repacked installer.
context.append("Target:WindowsMsi")
if "Target:Darwin" in context:
if not grr_config.CONFIG.Get(
"ClientBuilder.install_dir", context=context
):
raise ValueError("ClientBuilder.install_dir must be set on Darwin.")
if not grr_config.CONFIG.Get(
"ClientBuilder.fleetspeak_plist_path", context=context
):
raise ValueError(
"ClientBuilder.fleetspeak_plist_path must be set on Darwin."
)

template_path = None
# If output is specified, place the built template file there, otherwise
Expand Down Expand Up @@ -422,24 +433,6 @@ def main(args):
logger.handlers = [handler]

if args.subparser_name == "build":
if grr_config.CONFIG.ContextApplied("Platform:Darwin"):
# We know that the client builder is run on Darwin, so we can check that
# the required config options are set. But the builder config options use
# the "Target:Darwin" context, as they care about the target system that
# the template is built for, not the system that the builder is run on.
# The fact that we build macOS templates on Darwin is technically
# an implementation detail even though it is impossible to build macOS
# templates on any other platform.
if not grr_config.CONFIG.Get(
"ClientBuilder.install_dir",
context=[contexts.TARGET_DARWIN],
):
raise RuntimeError("ClientBuilder.install_dir must be set.")
if not grr_config.CONFIG.Get(
"ClientBuilder.fleetspeak_plist_path",
context=[contexts.TARGET_DARWIN],
):
raise RuntimeError("ClientBuilder.fleetspeak_plist_path must be set.")
TemplateBuilder().BuildTemplate(context=context, output=args.output)
elif args.subparser_name == "repack":
if args.debug_build:
Expand Down
2 changes: 1 addition & 1 deletion grr/proto/grr_response_proto/api/signed_commands.proto
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ message ApiCommand {

// Whether the command should allow execution with arbitrary
// standard input without it being pre-signed.
bool unsigned_stdin = 7;
bool unsigned_stdin_allowed = 7;
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ package rrg.action.execute_signed_command;
import "google/protobuf/duration.proto";
import "grr_response_proto/rrg/fs.proto";

message SignedCommand {
message Command {
// Path to the executable file to execute.
rrg.fs.Path path = 1;

Expand All @@ -29,18 +29,18 @@ message SignedCommand {

// Whether the command should allow execution with arbitrary
// standard input without it being pre-signed.
bool unsigned_stdin = 5;
bool unsigned_stdin_allowed = 5;
}
}

message Args {
// Serialized `SignedCommand` message to execute.
// Serialized `Command` message to execute.
bytes command = 1;

// Standard input to pass to the executed command.
//
// For this option to work, the command that has been signed has to allow
// arbitrary standard input by having the `unsigned_stdin` flag set.
// arbitrary standard input by having the `unsigned_stdin_allowed` flag set.
bytes unsigned_stdin = 2;

// An [Ed25519][1] signature of the command.
Expand Down
2 changes: 1 addition & 1 deletion grr/proto/grr_response_proto/signed_commands.proto
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ message Command {
repeated EnvVar env_vars = 3;
oneof stdin {
// Whether the stdin of the command is unsigned.
bool unsigned_stdin = 4;
bool unsigned_stdin_allowed = 4;
// The stdin of the command, if it is signed.
bytes signed_stdin = 5;
}
Expand Down
6 changes: 3 additions & 3 deletions grr/server/grr_response_server/bin/command_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,9 +41,9 @@ def _GetCommandSigner() -> command_signer.AbstractCommandSigner:

def _ConvertToRrgCommand(
command: api_signed_commands_pb2.ApiCommand,
) -> execute_signed_command_pb2.SignedCommand:
) -> execute_signed_command_pb2.Command:
"""Converts a GRR command to a RRG command."""
rrg_command = execute_signed_command_pb2.SignedCommand()
rrg_command = execute_signed_command_pb2.Command()

rrg_command.path.raw_bytes = command.path.encode("utf-8")
rrg_command.args.extend(command.args)
Expand All @@ -52,7 +52,7 @@ def _ConvertToRrgCommand(
if command.HasField("signed_stdin"):
rrg_command.signed_stdin = command.signed_stdin
else:
rrg_command.unsigned_stdin = command.unsigned_stdin
rrg_command.unsigned_stdin_allowed = command.unsigned_stdin_allowed
return rrg_command


Expand Down
2 changes: 1 addition & 1 deletion grr/server/grr_response_server/bin/command_signer_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ def testConvertToRrgCommand(self):

rrg_command = command_signer._ConvertToRrgCommand(command)

expected = execute_signed_command_pb2.SignedCommand()
expected = execute_signed_command_pb2.Command()
expected.path.raw_bytes = b"foo"
expected.args.extend(["bar", "baz"])
expected.env["FOO"] = "bar"
Expand Down
4 changes: 2 additions & 2 deletions grr/server/grr_response_server/command_signer.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,14 @@ class AbstractCommandSigner(metaclass=abc.ABCMeta):
"""A base class for command signers."""

@abc.abstractmethod
def Sign(self, command: execute_signed_command_pb2.SignedCommand) -> bytes:
def Sign(self, command: execute_signed_command_pb2.Command) -> bytes:
"""Signs a command and returns the signature."""

@abc.abstractmethod
def Verify(
self,
signature: bytes,
command: execute_signed_command_pb2.SignedCommand,
command: execute_signed_command_pb2.Command,
) -> None:
"""Validates a signature for given data with a verification key.
Expand Down
6 changes: 3 additions & 3 deletions grr/server/grr_response_server/command_signer_test_mixin.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,19 +11,19 @@ class CommandSignerTestMixin:
signer: command_signer.AbstractCommandSigner

def testVerifySignatureCanSignAndVerify(self): # pylint: disable=invalid-name
command = execute_signed_command_pb2.SignedCommand()
command = execute_signed_command_pb2.Command()
command.path.raw_bytes = b"/bin/ls"
command.args.append("-l")
command.env["PATH"] = "/usr/bin"
command.unsigned_stdin = True
command.unsigned_stdin_allowed = True

signature = self.signer.Sign(command)
self.assertLen(signature, 64)

self.signer.Verify(signature, command)

def testVerifySignatureRaisesWhenSignatureIsInvalid(self): # pylint: disable=invalid-name
command = execute_signed_command_pb2.SignedCommand()
command = execute_signed_command_pb2.Command()
command.path.raw_bytes = b"/bin/ls"

signature = b"invalid signature"
Expand Down
2 changes: 1 addition & 1 deletion grr/server/grr_response_server/databases/db.py
Original file line number Diff line number Diff line change
Expand Up @@ -4989,7 +4989,7 @@ def WriteSignedCommands(
signed_commands: Sequence[signed_commands_pb2.SignedCommand],
) -> None:
for signed_command in signed_commands:
command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.ParseFromString(signed_command.command)

_ValidateSignedCommandId(signed_command.id)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ def create_signed_command(
path: str = "test_path",
signature: bytes = None,
args: Optional[list[str]] = None,
unsigned_stdin: bool = False,
unsigned_stdin_allowed: bool = False,
signed_stdin: Optional[bytes] = None,
env_vars: Optional[list[signed_commands_pb2.Command.EnvVar]] = None,
) -> signed_commands_pb2.SignedCommand:
Expand All @@ -22,7 +22,7 @@ def create_signed_command(
signed_command.id = id_
signed_command.operating_system = operating_system

command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.path.raw_bytes = path.encode("utf-8")

if not signature:
Expand All @@ -35,7 +35,7 @@ def create_signed_command(
for env_var in env_vars:
command.env[env_var.name] = env_var.value

command.unsigned_stdin = unsigned_stdin
command.unsigned_stdin_allowed = unsigned_stdin_allowed
if signed_stdin:
command.signed_stdin = signed_stdin

Expand All @@ -52,13 +52,13 @@ def testWriteReadSignedCommands_allFields(self):
signed_command.operating_system = signed_commands_pb2.SignedCommand.OS.MACOS
signed_command.ed25519_signature = b"test_signature" + 50 * b"-" # 64 bytes

command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.path.raw_bytes = "test_path".encode("utf-8")
command.args.extend(["args1", "args2"])
command.env["env_var_1"] = "env_var_1_value"
command.env["env_var_2"] = "env_var_2_value"
command.signed_stdin = b"signed_stdin"
command.unsigned_stdin = False
command.unsigned_stdin_allowed = False

signed_command.command = command.SerializeToString()

Expand Down Expand Up @@ -104,7 +104,7 @@ def testWriteReadSignedCommands_testPositionalArgsKeepOrder(self):
read_command = self.db.ReadSignedCommand(
"command", signed_commands_pb2.SignedCommand.OS.LINUX
)
command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.ParseFromString(read_command.command)
self.assertEqual(command.args, ["arg1", "arg2", "arg3"])

Expand All @@ -122,7 +122,7 @@ def testWriteReadSignedCommands_testEnvVars(self):
read_command = self.db.ReadSignedCommand(
"command", signed_commands_pb2.SignedCommand.OS.LINUX
)
command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.ParseFromString(read_command.command)
self.assertEqual(
command.env,
Expand Down
8 changes: 4 additions & 4 deletions grr/server/grr_response_server/flows/general/cloud_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ def testRRGGoogleLinux(
) -> None:
# TODO: Load signed commands from the `.textproto` file to
# ensure integrity.
command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.path.raw_bytes = "/usr/sbin/dmidecode".encode("utf-8")
command.args.append("--string")
command.args.append("bios-version")
Expand All @@ -63,7 +63,7 @@ def ExecuteSignedCommandHandler(session: rrg_test_lib.Session) -> None:
args = rrg_execute_signed_command_pb2.Args()
assert session.args.Unpack(args)

command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.ParseFromString(args.command)

if command.path.raw_bytes != "/usr/sbin/dmidecode".encode("utf-8"):
Expand Down Expand Up @@ -220,7 +220,7 @@ def testRRGAmazonLinux(
) -> None:
# TODO: Load signed commands from the `.textproto` file to
# ensure integrity.
command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.path.raw_bytes = "/usr/sbin/dmidecode".encode("utf-8")
command.args.append("--string")
command.args.append("bios-version")
Expand All @@ -242,7 +242,7 @@ def ExecuteSignedCommandHandler(session: rrg_test_lib.Session) -> None:
args = rrg_execute_signed_command_pb2.Args()
assert session.args.Unpack(args)

command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.ParseFromString(args.command)

if command.path.raw_bytes != "/usr/sbin/dmidecode".encode("utf-8"):
Expand Down
8 changes: 4 additions & 4 deletions grr/server/grr_response_server/flows/general/hardware_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ def setUpClass(cls):
def testRRGLinux(self, db: abstract_db.Database) -> None:
# TODO: Load signed commands from the `.textproto` file to
# ensure integrity.
command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.path.raw_bytes = "/usr/sbin/dmidecode".encode("utf-8")
command.args.append("-q")
signed_command = signed_commands_pb2.SignedCommand()
Expand All @@ -57,7 +57,7 @@ def ExecuteSignedCommandHandler(session: rrg_test_lib.Session) -> None:
args = rrg_execute_signed_command_pb2.Args()
assert session.args.Unpack(args)

command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.ParseFromString(args.command)

if command.path.raw_bytes != "/usr/sbin/dmidecode".encode("utf-8"):
Expand Down Expand Up @@ -227,7 +227,7 @@ def testLinux(self):
def testRRGMacos(self, db: abstract_db.Database) -> None:
# TODO: Load signed commands from the `.textproto` file to
# ensure integrity.
command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.path.raw_bytes = "/usr/sbin/system_profiler".encode("utf-8")
command.args.append("-xml")
command.args.append("SPHardwareDataType")
Expand All @@ -249,7 +249,7 @@ def ExecuteSignedCommandHandler(session: rrg_test_lib.Session) -> None:
args = rrg_execute_signed_command_pb2.Args()
assert session.args.Unpack(args)

command = rrg_execute_signed_command_pb2.SignedCommand()
command = rrg_execute_signed_command_pb2.Command()
command.ParseFromString(args.command)

if command.path.raw_bytes != "/usr/sbin/system_profiler".encode("utf-8"):
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -70,12 +70,12 @@ def Handle(
# TODO: Add signature verification.
raise ValueError("Command signature is required.")

rrg_command = rrg_execute_signed_command_pb2.SignedCommand()
rrg_command = rrg_execute_signed_command_pb2.Command()
rrg_command.ParseFromString(args_signed_command.command)
if not rrg_command.path.raw_bytes:
raise ValueError("Command path is required.")
if not rrg_command.HasField(
"unsigned_stdin"
"unsigned_stdin_allowed"
) and not rrg_command.HasField("signed_stdin"):
raise ValueError("Command stdin is required.")

Expand Down
Loading

0 comments on commit 44adfce

Please sign in to comment.