Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Final touches before the 3490 release. #1125

Merged
merged 1 commit into from
Feb 27, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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