Skip to content

Commit d8519fe

Browse files
committed
Multiple changes.
* Add a configuration option for specifying allowed commands. * Windows: move interpolation of fleetspeak service config from repacker to installer.
1 parent 8801003 commit d8519fe

File tree

5 files changed

+76
-15
lines changed

5 files changed

+76
-15
lines changed

grr/client/grr_response_client/client_utils_common.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
import logging
1010
import os
1111
import platform
12+
import shlex
1213
import subprocess
1314
import threading
1415
import time
@@ -146,6 +147,11 @@ def IsExecutionAllowed(cmd, args):
146147
the list).
147148
A deployment-specific list is also checked (see local/binary_whitelist.py).
148149
"""
150+
allowed_commands = map(shlex.split, config.CONFIG["Client.allowed_commands"])
151+
allowed_commands = list(allowed_commands)
152+
if [cmd] + list(args) in allowed_commands:
153+
return True
154+
149155
if platform.system() == "Windows":
150156
allowlist = [
151157
("arp.exe", ["-a"]),

grr/client/grr_response_client/client_utils_test.py

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,50 @@
2424
from grr.test_lib import test_lib
2525

2626

27+
class IsExecutionAllowedTest(absltest.TestCase):
28+
29+
def setUp(self):
30+
super().setUp()
31+
self.is_execution_allowed = client_utils_common.IsExecutionAllowed
32+
33+
def testAllowsOnlyConfiguredCommands(self):
34+
with test_lib.ConfigOverrider({
35+
"Client.allowed_commands": ["/usr/bin/foo"],
36+
}):
37+
self.assertTrue(self.is_execution_allowed("/usr/bin/foo", []))
38+
self.assertFalse(self.is_execution_allowed("/usr/bin/bar", []))
39+
40+
def testAllowsOnlyConfiguredCommandsWithArgs(self):
41+
with test_lib.ConfigOverrider({
42+
"Client.allowed_commands": [
43+
"/bin/foo --bar --baz",
44+
"/bin/foo --quux",
45+
],
46+
}):
47+
self.assertTrue(self.is_execution_allowed("/bin/foo", ["--bar", "--baz"]))
48+
self.assertTrue(self.is_execution_allowed("/bin/foo", ["--quux"]))
49+
self.assertFalse(self.is_execution_allowed("/bin/foo", ["--norf"]))
50+
51+
def testAllowsOnlyConfiguredCommandsWithSimpleQuotes(self):
52+
with test_lib.ConfigOverrider({
53+
"Client.allowed_commands": ["'foo bar' 'baz quux'"],
54+
}):
55+
self.assertTrue(self.is_execution_allowed("foo bar", ["baz quux"]))
56+
self.assertFalse(self.is_execution_allowed("foo bar", ["baz", "quux"]))
57+
self.assertFalse(self.is_execution_allowed("foo", ["bar", "baz quux"]))
58+
self.assertFalse(self.is_execution_allowed("foo", ["bar", "baz", "quux"]))
59+
60+
def testAllowsOnlyConfiguredCommandsWithComplexQuotes(self):
61+
with test_lib.ConfigOverrider({
62+
"Client.allowed_commands": [
63+
"'/foo bar/\"quux norf\"/thud' -x '1 3 3 7' -y \"42\"",
64+
],
65+
}):
66+
command = "/foo bar/\"quux norf\"/thud"
67+
args = ["-x", "1 3 3 7", "-y", "42"]
68+
self.assertTrue(self.is_execution_allowed(command, args))
69+
70+
2771
class ClientUtilsTest(test_lib.GRRBaseTest):
2872
"""Test the client utils."""
2973

grr/client/grr_response_client/windows/installers.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -336,11 +336,26 @@ def _InstallBundledFleetspeak():
336336
command_line=f"\"{fleetspeak_client}\" -config \"{fleetspeak_config}\"")
337337

338338

339+
def _MaybeInterpolateFleetspeakServiceConfig():
340+
"""Interpolates the fleetspeak service config if present."""
341+
fleetspeak_unsigned_config_path = os.path.join(
342+
config.CONFIG["Client.install_path"],
343+
config.CONFIG["Client.fleetspeak_unsigned_config_fname"])
344+
template_path = f"{fleetspeak_unsigned_config_path}.in"
345+
if not os.path.exists(template_path):
346+
return
347+
with open(template_path, "r") as source:
348+
with open(fleetspeak_unsigned_config_path, "w") as dest:
349+
interpolated = config.CONFIG.InterpolateValue(source.read())
350+
dest.write(interpolated.replace("\\", "\\\\"))
351+
352+
339353
def _Run():
340354
"""Installs the windows client binary."""
341355
_CheckForWow64()
342356
_StopPreviousService()
343357
_CopyToSystemDir()
358+
_MaybeInterpolateFleetspeakServiceConfig()
344359

345360
if not config.CONFIG["Client.fleetspeak_enabled"]:
346361
_InstallNanny()

grr/client_builder/grr_response_client_builder/repackers/windows.py

Lines changed: 5 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@
88
import io
99
import logging
1010
import os
11-
import re
1211
import struct
1312
import zipfile
1413

@@ -86,22 +85,13 @@ def _GenerateFleetspeakServiceConfig(self, zip_file):
8685
final_fs_config_fname = config.CONFIG.Get(
8786
"Client.fleetspeak_unsigned_config_fname", context=self.context)
8887
if orig_fs_config_path.endswith(".in"):
89-
logging.info("Interpolating %s", orig_fs_config_path)
90-
logging.warning("Backslashes will be naively re-escaped after "
91-
"interpolation. If this is not desired, use a Fleetspeak "
92-
"config file without the '.in' extension.")
93-
with utils.TempDirectory() as temp_dir:
94-
temp_fs_config_path = os.path.join(temp_dir, final_fs_config_fname)
95-
with io.open(orig_fs_config_path, "r") as source:
96-
with io.open(temp_fs_config_path, "w") as dest:
97-
interpolated = config.CONFIG.InterpolateValue(
98-
source.read(), context=self.context)
99-
dest.write(re.sub(r"\\", r"\\\\", interpolated))
100-
self._ValidateFleetspeakServiceConfig(temp_fs_config_path)
101-
zip_file.write(temp_fs_config_path, final_fs_config_fname)
88+
# The interpolation has to be performed on the client, since it depends
89+
# on values of environment variable.
90+
final_fs_config_fname = f"{final_fs_config_fname}.in"
10291
else:
10392
self._ValidateFleetspeakServiceConfig(orig_fs_config_path)
104-
zip_file.write(orig_fs_config_path, final_fs_config_fname)
93+
94+
zip_file.write(orig_fs_config_path, final_fs_config_fname)
10595

10696
def _AddFleetspeakConfig(self, zip_file):
10797
zip_file.write(

grr/core/grr_response_core/config/client.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -249,6 +249,12 @@
249249
"File-name for the Fleetspeak service config generated "
250250
"when repacking templates.")
251251

252+
config_lib.DEFINE_list(
253+
"Client.allowed_commands",
254+
default=[],
255+
help="Commands that the client is allowed to execute. Each command must be "
256+
"specified in the format that is supported by the Python's `shlex` module.")
257+
252258
# osquery options.
253259

254260
config_lib.DEFINE_string(

0 commit comments

Comments
 (0)