From e826795667e319a336ccbfe0919c044766801cb8 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Fri, 17 Jan 2020 10:49:36 +0100
Subject: [PATCH 1/7] Added lineinfile shell assignment support to our macros.
---
shared/macros-ansible.jinja | 20 +++++++++++++++++++
shared/macros-bash.jinja | 26 +++++++++++++++++++++++++
shared/macros-oval.jinja | 39 ++++++++++++++++++++++++++++++++-----
3 files changed, 80 insertions(+), 5 deletions(-)
diff --git a/shared/macros-ansible.jinja b/shared/macros-ansible.jinja
index 3e4a441225..c42a5156ce 100644
--- a/shared/macros-ansible.jinja
+++ b/shared/macros-ansible.jinja
@@ -141,6 +141,26 @@
{{{ ansible_set_config_file(msg, "/etc/ssh/sshd_config", parameter, value=value, create="yes", prefix_regex='(?i)^\s*', validate="/usr/sbin/sshd -t -f %s", insert_before="^[#\s]*Match") }}}
{{%- endmacro %}}
+{{#
+ High level macro to set a value in a shell-related file that contains var assignments. This
+ takes these values: msg (the name for the Ansible task), path to the file, a parameter to set
+ in the configuration file, and the value to set it to. We specify a case
+ sensitive comparison in the prefix since this is used to deduplicate since
+ We also specify the validation program here; see 'bash -c "help set" | grep -e -n'
+#}}
+{{%- macro ansible_shell_set(msg, path, parameter, value='', no_quotes=false) %}}
+{{% if no_quotes -%}}
+{{%- else -%}}
+{{%- set quotes = "\"'" -%}}
+ {{% if "$" in value %}}
+ {{% set value = '"%s"' % value %}}
+ {{% else %}}
+ {{% set value = "'%s'" % value %}}
+ {{% endif %}}
+{{%- endif -%}}
+{{{ ansible_set_config_file(msg, path, parameter, value=value, create="yes", prefix_regex='^\s*', validate="/usr/bin/bash -n %s", insert_before="^[#\s]*Match") }}}
+{{%- endmacro %}}
+
{{#
High level macro to set a command in tmux configuration file /etc/tmux.conf.
Parameters:
diff --git a/shared/macros-bash.jinja b/shared/macros-bash.jinja
index 43200bdd8a..6c0bb2facc 100644
--- a/shared/macros-bash.jinja
+++ b/shared/macros-bash.jinja
@@ -1,5 +1,31 @@
{{# ##### High level macros ##### #}}
+{{%- macro bash_shell_file_set(path, parameter, value, no_quotes=false) -%}}
+{{% if no_quotes -%}}
+ {{% if "$" in value %}}
+ {{% set value = '%s' % value.replace("$", "\\$") %}}
+ {{% endif %}}
+{{%- else -%}}
+ {{% if "$" in value %}}
+ {{% set value = '\\"%s\\"' % value.replace("$", "\\$") %}}
+ {{% else %}}
+ {{% set value = "'%s'" % value %}}
+ {{% endif %}}
+{{%- endif -%}}
+{{{ set_config_file(
+ path=path,
+ parameter=parameter,
+ value=value,
+ create=true,
+ insert_after="",
+ insert_before="^Match",
+ insensitive=false,
+ separator="=",
+ separator_regex="=",
+ prefix_regex="^\s*")
+ }}}
+{{%- endmacro -%}}
+
{{%- macro bash_sshd_config_set(parameter, value) -%}}
{{{ set_config_file(
path="/etc/ssh/sshd_config",
diff --git a/shared/macros-oval.jinja b/shared/macros-oval.jinja
index 2049a24d6e..696cf36db0 100644
--- a/shared/macros-oval.jinja
+++ b/shared/macros-oval.jinja
@@ -17,8 +17,9 @@
- multi_value (boolean): If set, it means that the parameter can accept multiple values and the expected value must be present in the current list of values.
- missing_config_file_fail (boolean): If set, the check will fail if the configuration is not existent in the system.
- section (String): If set, the parameter will be checked only within the given section defined by [section].
+ - quotes (String): If non-empty, one level of matching quotes is considered when checking the value. See comment of oval_line_in_file_state for more info.
#}}
-{{%- macro oval_check_config_file(path='', prefix_regex='^[ \\t]*', parameter='', separator_regex='[ \\t]+', value='', missing_parameter_pass=false, application='', multi_value=false, missing_config_file_fail=false, section='') -%}}
+{{%- macro oval_check_config_file(path='', prefix_regex='^[ \\t]*', parameter='', separator_regex='[ \\t]+', value='', missing_parameter_pass=false, application='', multi_value=false, missing_config_file_fail=false, section='', quotes='') -%}}
<def-group>
<definition class="compliance" id="{{{ rule_id }}}" version="1">
<metadata>
@@ -60,7 +61,7 @@
</definition>
{{{ oval_line_in_file_test(path, parameter) }}}
{{{ oval_line_in_file_object(path, section, prefix_regex, parameter, separator_regex, false, multi_value) }}}
- {{{ oval_line_in_file_state(value, multi_value) }}}
+ {{{ oval_line_in_file_state(value, multi_value, quotes) }}}
{{%- if missing_parameter_pass %}}
{{{ oval_line_in_file_test(path, parameter, missing_parameter_pass) }}}
{{{ oval_line_in_file_object(path, section, prefix_regex, parameter, separator_regex, missing_parameter_pass, multi_value) }}}
@@ -173,12 +174,21 @@
This macro can take two parameters:
- value (String): The value to be checked. This can also be a regular expression (e.g: value1|value2 can match both values).
- multi_value (boolean): If set, it means that the parameter can accept multiple values and the expected value must be present in the current list of values.
+ - quotes (String): If non-empty, one level of matching quotes is considered when checking the value. Specify one or more quote types as a string.
+ For example, for shell quoting, specify quotes="'\""), which will make sure that value, 'value' and "value" are matched, but 'value" or '"value"' won't be.
#}}
-{{%- macro oval_line_in_file_state(value='', multi_value='') -%}}
+{{%- macro oval_line_in_file_state(value='', multi_value='', quotes='') -%}}
+{{%- set regex = value -%}}
+{{%- if quotes != "" %}}
+{{%- if "\\1" in value > 0 %}}
+{{{ raise("The regex for matching '%s' already references capturing groups, which doesn't go well with quoting that adds a capturing group to the beginning." % value) }}}
+{{%- endif %}}
+{{%- set regex = "((?:%s)?)%s\\1" % ("|".join(quotes), regex) -%}}
+{{%- endif %}}
{{%- if multi_value %}}
-{{%- set regex = "^.*\\b"+value+"\\b.*$" -%}}
+{{%- set regex = "^.*\\b"+regex+"\\b.*$" -%}}
{{%- else %}}
-{{%- set regex = "^"+value+"$" -%}}
+{{%- set regex = "^"+regex+"$" -%}}
{{%- endif %}}
<ind:textfilecontent54_state id="state_{{{ rule_id }}}" version="1">
<ind:subexpression datatype="string" operation="pattern match">{{{ regex }}}</ind:subexpression>
@@ -232,6 +242,25 @@
{{{ oval_check_config_file("/etc/ssh/sshd_config", prefix_regex="^[ \\t]*(?i)", parameter=parameter, separator_regex='(?-i)[ \\t]+', value=value, missing_parameter_pass=missing_parameter_pass, application="sshd", multi_value=multi_value, missing_config_file_fail=missing_config_file_fail) }}}
{{%- endmacro %}}
+{{#
+ High level macro to check if a particular shell variable is set.
+ This macro can take five parameters:
+ - path (String): Path to the file.
+ - parameter (String): The shell variable name.
+ - value (String): The variable value WITHOUT QUOTES.
+ - missing_parameter_pass (boolean): If set, the check will also pass if the parameter is not present in the configuration file (default is applied).
+ - multi_value (boolean): If set, it means that the parameter can accept multiple values and the expected value must be present in the current list of values.
+ - missing_config_file_fail (boolean): If set, the check will fail if the configuration is not existent in the system.
+#}}
+{{%- macro oval_check_shell_file(path, parameter='', value='', application='', no_quotes=false, missing_parameter_pass=false, multi_value=false, missing_config_file_fail=false) %}}
+{{% if no_quotes -%}}
+{{%- set quotes = "" -%}}
+{{%- else -%}}
+{{%- set quotes = "\"'" -%}}
+{{%- endif -%}}
+{{{ oval_check_config_file(path, prefix_regex="^[ \\t]*", parameter=parameter, separator_regex='=', value=value, missing_parameter_pass=missing_parameter_pass, application=application, multi_value=multi_value, missing_config_file_fail=missing_config_file_fail, quotes=quotes) }}}
+{{%- endmacro %}}
+
{{#
High level macro to check if a particular combination of parameter and value in the Audit daemon configuration file is set.
This function can take five parameters:
From a7281779e424a0b481e1b08ca01d2ebd1af2e834 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Fri, 17 Jan 2020 10:50:16 +0100
Subject: [PATCH 2/7] Added tests for shell lineinfile.
---
tests/test_macros_oval.py | 142 ++++++++++++++++++
.../unit/bash/test_set_config_file.bats.jinja | 56 +++++++
2 files changed, 198 insertions(+)
diff --git a/tests/test_macros_oval.py b/tests/test_macros_oval.py
index 65a88ba7b4..8acae8548b 100755
--- a/tests/test_macros_oval.py
+++ b/tests/test_macros_oval.py
@@ -896,6 +896,148 @@ def main():
"[vehicle]\nspeed =\n100",
"false"
)
+ tester.test(
+ "SHELL commented out",
+ r"""{{{ oval_check_shell_file(
+ path='CONFIG_FILE',
+ parameter='SHELL',
+ value='/bin/bash',
+ missing_parameter_pass=false,
+ application='',
+ multi_value=false,
+ missing_config_file_fail=false,
+ ) }}}""",
+ "# SHELL=/bin/bash\n",
+ "false"
+ )
+ tester.test(
+ "SHELL correct",
+ r"""{{{ oval_check_shell_file(
+ path='CONFIG_FILE',
+ parameter='SHELL',
+ value='/bin/bash',
+ missing_parameter_pass=false,
+ application='',
+ multi_value=false,
+ missing_config_file_fail=false,
+ ) }}}""",
+ " SHELL=/bin/bash\n",
+ "true"
+ )
+ tester.test(
+ "SHELL single-quoted",
+ r"""{{{ oval_check_shell_file(
+ path='CONFIG_FILE',
+ parameter='SHELL',
+ value='/bin"/bash',
+ missing_parameter_pass=false,
+ application='',
+ multi_value=false,
+ missing_config_file_fail=false,
+ ) }}}""",
+ " SHELL='/bin\"/bash'\n",
+ "true"
+ )
+ tester.test(
+ "SHELL double-quoted",
+ r"""{{{ oval_check_shell_file(
+ path='CONFIG_FILE',
+ parameter='SHELL',
+ value=' /bin/bash',
+ missing_parameter_pass=false,
+ application='',
+ multi_value=false,
+ missing_config_file_fail=false,
+ ) }}}""",
+ """ SHELL=" /bin/bash"\n""",
+ "true"
+ )
+ tester.test(
+ "SHELL unwanted double-quoted",
+ r"""{{{ oval_check_shell_file(
+ path='CONFIG_FILE',
+ parameter='SHELL',
+ value=' /bin/bash',
+ no_quotes=true,
+ missing_parameter_pass=false,
+ application='',
+ multi_value=false,
+ missing_config_file_fail=false,
+ ) }}}""",
+ """ SHELL=" /bin/bash"\n""",
+ "false"
+ )
+ tester.test(
+ "SHELL unwanted single-quoted",
+ r"""{{{ oval_check_shell_file(
+ path='CONFIG_FILE',
+ parameter='SHELL',
+ value='/bin"/bash',
+ no_quotes=true,
+ missing_parameter_pass=false,
+ application='',
+ multi_value=false,
+ missing_config_file_fail=false,
+ ) }}}""",
+ " SHELL='/bin\"/bash'\n",
+ "false"
+ )
+ tester.test(
+ "SHELL double-quoted spaced",
+ r"""{{{ oval_check_shell_file(
+ path='CONFIG_FILE',
+ parameter='SHELL',
+ value='/bin/bash',
+ missing_parameter_pass=false,
+ application='',
+ multi_value=false,
+ missing_config_file_fail=false,
+ ) }}}""",
+ """ SHELL= "/bin/bash"\n""",
+ "false"
+ )
+ tester.test(
+ "SHELL bad_var_case",
+ r"""{{{ oval_check_shell_file(
+ path='CONFIG_FILE',
+ parameter='SHELL',
+ value='/bin/bash',
+ missing_parameter_pass=false,
+ application='',
+ multi_value=false,
+ missing_config_file_fail=false,
+ ) }}}""",
+ """ Shell="/bin/bash"\n""",
+ "false"
+ )
+ tester.test(
+ "SHELL bad_value_case",
+ r"""{{{ oval_check_shell_file(
+ path='CONFIG_FILE',
+ parameter='SHELL',
+ value='/bin/bash',
+ missing_parameter_pass=false,
+ application='',
+ multi_value=false,
+ missing_config_file_fail=false,
+ ) }}}""",
+ """ SHELL="/bin/Bash"\n""",
+ "false"
+ )
+ tester.test(
+ "SHELL badly quoted",
+ r"""{{{ oval_check_shell_file(
+ path='CONFIG_FILE',
+ parameter='SHELL',
+ value='/bin/bash',
+ missing_parameter_pass=false,
+ application='',
+ multi_value=false,
+ missing_config_file_fail=false,
+ ) }}}""",
+ """ SHELL="/bin/bash'\n""",
+ "false"
+ )
tester.finish()
diff --git a/tests/unit/bash/test_set_config_file.bats.jinja b/tests/unit/bash/test_set_config_file.bats.jinja
index 3dc2c721d4..4126d0440e 100644
--- a/tests/unit/bash/test_set_config_file.bats.jinja
+++ b/tests/unit/bash/test_set_config_file.bats.jinja
@@ -126,3 +126,59 @@ function call_set_config_file {
rm "$tmp_file"
}
+
+@test "Basic Bash remediation" {
+ tmp_file="$(mktemp)"
+ printf "%s\n" "something=foo" > "$tmp_file"
+ expected_output="something='va lue'\n"
+
+ {{{ bash_shell_file_set("$tmp_file", "something", "va lue") | indent(4) }}}
+
+ run diff -U2 "$tmp_file" <(printf "$expected_output")
+ echo "$output"
+ [ "$status" -eq 0 ]
+
+ rm "$tmp_file"
+}
+
+@test "Variable remediation - preserve dollar and use double quotes" {
+ tmp_file="$(mktemp)"
+ printf "%s\n" "something=bar" > "$tmp_file"
+ expected_output='something="$value"'"\n"
+
+ {{{ bash_shell_file_set("$tmp_file", "something", '$value') | indent(4) }}}
+
+ run diff -U2 "$tmp_file" <(printf "$expected_output")
+ echo "$output"
+ [ "$status" -eq 0 ]
+
+ rm "$tmp_file"
+}
+
+@test "Basic Bash remediation - don't quote" {
+ tmp_file="$(mktemp)"
+ printf "%s\n" "something=foo" > "$tmp_file"
+ expected_output="something=va lue\n"
+
+ {{{ bash_shell_file_set("$tmp_file", "something", "va lue", no_quotes=true) | indent(4) }}}
+
+ run diff -U2 "$tmp_file" <(printf "$expected_output")
+ echo "$output"
+ [ "$status" -eq 0 ]
+
+ rm "$tmp_file"
+}
+
+@test "Variable remediation - don't quote" {
+ tmp_file="$(mktemp)"
+ printf "%s\n" "something=bar" > "$tmp_file"
+ expected_output='something=$value'"\n"
+
+ {{{ bash_shell_file_set("$tmp_file", "something", '$value', no_quotes=true) | indent(4) }}}
+
+ run diff -U2 "$tmp_file" <(printf "$expected_output")
+ echo "$output"
+ [ "$status" -eq 0 ]
+
+ rm "$tmp_file"
+}
From 347e7ab345a35fc3045a886d883d8efe7d9820b2 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Fri, 17 Jan 2020 10:51:02 +0100
Subject: [PATCH 3/7] Added the shell lineinfile template.
---
docs/manual/developer_guide.adoc | 21 +++++++++++++++++
.../template_ANSIBLE_shell_lineinfile | 21 +++++++++++++++++
.../templates/template_BASH_shell_lineinfile | 6 +++++
.../templates/template_OVAL_shell_lineinfile | 10 ++++++++
ssg/templates.py | 23 +++++++++++++++++++
5 files changed, 81 insertions(+)
create mode 100644 shared/templates/template_ANSIBLE_shell_lineinfile
create mode 100644 shared/templates/template_BASH_shell_lineinfile
create mode 100644 shared/templates/template_OVAL_shell_lineinfile
diff --git a/docs/manual/developer_guide.adoc b/docs/manual/developer_guide.adoc
index aa0a7491c3..b5d22213b7 100644
--- a/docs/manual/developer_guide.adoc
+++ b/docs/manual/developer_guide.adoc
@@ -1591,6 +1591,27 @@ service_enabled::
** *daemonname* - name of the daemon. This argument is optional. If *daemonname* is not specified it means the name of the daemon is the same as the name of service.
* Languages: Ansible, Bash, OVAL, Puppet
+shell_lineinfile::
+* Checks shell variable assignments in files.
+Remediations will paste assignments with single shell quotes unless there is the dollar sign in the value string, in which case double quotes are administered.
+The OVAL checks for a match with either of no quotes, single quoted string, or double quoted string.
+* Parameters:
+** *path* - What file to check.
+** *parameter* - name of the shell variable, eg. `SHELL`.
+** *value* - value of the SSH configuration option specified by *parameter*, eg. `"/bin/bash"`. Don't pass extra shell quoting - that will be handled on the lower level.
+** *no_quotes* - If set to `"true"`, the assigned value has to be without quotes during the check and remediation doesn't quote assignments either.
+** *missing_parameter_pass* - If set to `"true"` the OVAL check will pass if the parameter is not present in the target file.
+* Languages: Ansible, Bash, OVAL
+* Example:
+A template invocation specifying that parameter `HISTSIZE` should be set to value `500` in `/etc/profile` will produce a check that passes if any of the following lines are present in `/etc/profile`:
+** `HISTSIZE=500`
+** `HISTSIZE="500"`
+** `HISTSIZE='500'`
++
+The remediation would insert one of the quoted forms if the line was not present.
++
+If the `no_quotes` would be set in the template, only the first form would be checked for, and the unquoted assignment would be inserted to the file by the remediation if not present.
+
sshd_lineinfile::
* Checks SSH server configuration items in `/etc/ssh/sshd_config`.
* Parameters:
diff --git a/shared/templates/template_ANSIBLE_shell_lineinfile b/shared/templates/template_ANSIBLE_shell_lineinfile
new file mode 100644
index 0000000000..7d0a3ebcbd
--- /dev/null
+++ b/shared/templates/template_ANSIBLE_shell_lineinfile
@@ -0,0 +1,21 @@
+# platform = multi_platform_all
+# reboot = false
+# strategy = restrict
+# complexity = low
+# disruption = low
+{{% set msg = "shell-style assignment of '" ~ PARAMETER ~ "' to '" ~ VALUE ~ "' in '" ~ PATH ~ "'." -%}}
+{{%- if NO_QUOTES -%}}
+ {{% set msg = "Setting unquoted " ~ msg %}}
+{{%- else -%}}
+ {{% set msg = "Setting shell-quoted " ~ msg %}}
+{{%- endif -%}}
+{{{
+ ansible_shell_set(
+ msg=msg,
+ path=PATH,
+ parameter=PARAMETER,
+ value=VALUE,
+ no_quotes=NO_QUOTES
+ )
+}}}
+
diff --git a/shared/templates/template_BASH_shell_lineinfile b/shared/templates/template_BASH_shell_lineinfile
new file mode 100644
index 0000000000..6bf869d62b
--- /dev/null
+++ b/shared/templates/template_BASH_shell_lineinfile
@@ -0,0 +1,6 @@
+# platform = multi_platform_all
+# reboot = false
+# strategy = restrict
+# complexity = low
+# disruption = low
+{{{ bash_shell_file_set(path=PATH, parameter=PARAMETER, value=VALUE, no_quotes=NO_QUOTES) }}}
diff --git a/shared/templates/template_OVAL_shell_lineinfile b/shared/templates/template_OVAL_shell_lineinfile
new file mode 100644
index 0000000000..fd05b6b568
--- /dev/null
+++ b/shared/templates/template_OVAL_shell_lineinfile
@@ -0,0 +1,10 @@
+{{{
+oval_check_shell_file(
+ path=PATH,
+ parameter=PARAMETER,
+ value=VALUE,
+ no_quotes=NO_QUOTES,
+ missing_parameter_pass=MISSING_PARAMETER_PASS
+)
+}}}
+
diff --git a/ssg/templates.py b/ssg/templates.py
index f4f56c94e6..c2c82e6c29 100644
--- a/ssg/templates.py
+++ b/ssg/templates.py
@@ -290,6 +290,29 @@ def sshd_lineinfile(data, lang):
return data
+@template(["ansible", "bash", "oval"])
+def shell_lineinfile(data, lang):
+ value = data["value"]
+ if value[0] in ("'", '"') and value[0] == value[1]:
+ msg = (
+ "Value >>{value}<< of shell variable '{varname}' "
+ "has been supplied with quotes, please fix the content - "
+ "shell quoting is handled by the check/remediation code."
+ .format(value=value, varname=data["parameter"]))
+ raise Exception(msg)
+ missing_parameter_pass = data.get("missing_parameter_pass", "false")
+ if missing_parameter_pass == "true":
+ missing_parameter_pass = True
+ elif missing_parameter_pass == "false":
+ missing_parameter_pass = False
+ data["missing_parameter_pass"] = missing_parameter_pass
+ no_quotes = False
+ if data["no_quotes"] == "true":
+ no_quotes = True
+ data["no_quotes"] = no_quotes
+ return data
+
+
@template(["ansible", "bash", "oval"])
def timer_enabled(data, lang):
if "packagename" not in data:
From ac5d1a8ad511e828e652ce1ca58b06c18f8c083b Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Tue, 21 Jan 2020 14:13:01 +0100
Subject: [PATCH 4/7] Fixed the templated string evaluation.
---
ssg/templates.py | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/ssg/templates.py b/ssg/templates.py
index c2c82e6c29..873f543f41 100644
--- a/ssg/templates.py
+++ b/ssg/templates.py
@@ -293,7 +293,7 @@ def sshd_lineinfile(data, lang):
@template(["ansible", "bash", "oval"])
def shell_lineinfile(data, lang):
value = data["value"]
- if value[0] in ("'", '"') and value[0] == value[1]:
+ if value[0] in ("'", '"') and value[0] == value[-1]:
msg = (
"Value >>{value}<< of shell variable '{varname}' "
"has been supplied with quotes, please fix the content - "
From 8589574707c63eb3ac4c56674326b70dacfd2ee4 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Tue, 21 Jan 2020 14:46:39 +0100
Subject: [PATCH 5/7] Fixed jinja macros
- Fixed macro descriptions.
- Fixed Ansible insert_after.
---
shared/macros-ansible.jinja | 18 ++++++++----------
shared/macros-bash.jinja | 2 +-
shared/macros-oval.jinja | 7 +++----
3 files changed, 12 insertions(+), 15 deletions(-)
diff --git a/shared/macros-ansible.jinja b/shared/macros-ansible.jinja
index c42a5156ce..81e18e2d5c 100644
--- a/shared/macros-ansible.jinja
+++ b/shared/macros-ansible.jinja
@@ -143,22 +143,20 @@
{{#
High level macro to set a value in a shell-related file that contains var assignments. This
- takes these values: msg (the name for the Ansible task), path to the file, a parameter to set
- in the configuration file, and the value to set it to. We specify a case
- sensitive comparison in the prefix since this is used to deduplicate since
+ takes these values:
+ - msg (the name for the Ansible task),
+ - path to the file,
+ - parameter to set in the configuration file, and
+ - value to set it to.
We also specify the validation program here; see 'bash -c "help set" | grep -e -n'
#}}
{{%- macro ansible_shell_set(msg, path, parameter, value='', no_quotes=false) %}}
{{% if no_quotes -%}}
{{%- else -%}}
-{{%- set quotes = "\"'" -%}}
- {{% if "$" in value %}}
- {{% set value = '"%s"' % value %}}
- {{% else %}}
- {{% set value = "'%s'" % value %}}
- {{% endif %}}
+{{# Use the double quotes in all cases, as the underlying macro single-quotes the assignment line. #}}
+{{% set value = '"%s"' % value %}}
{{%- endif -%}}
-{{{ ansible_set_config_file(msg, path, parameter, value=value, create="yes", prefix_regex='^\s*', validate="/usr/bin/bash -n %s", insert_before="^[#\s]*Match") }}}
+{{{ ansible_set_config_file(msg, path, parameter, separator="=", separator_regex="=", value=value, create="yes", prefix_regex='^\s*', validate="/usr/bin/bash -n %s", insert_before="^# " ~ parameter) }}}
{{%- endmacro %}}
{{#
diff --git a/shared/macros-bash.jinja b/shared/macros-bash.jinja
index 6c0bb2facc..dc7fd25588 100644
--- a/shared/macros-bash.jinja
+++ b/shared/macros-bash.jinja
@@ -18,7 +18,7 @@
value=value,
create=true,
insert_after="",
- insert_before="^Match",
+ insert_before="^#\s*" ~ parameter,
insensitive=false,
separator="=",
separator_regex="=",
diff --git a/shared/macros-oval.jinja b/shared/macros-oval.jinja
index 696cf36db0..cfa9de9d2d 100644
--- a/shared/macros-oval.jinja
+++ b/shared/macros-oval.jinja
@@ -233,7 +233,7 @@
- value (String): The value to be checked. This can also be a regular expression (e.g: value1|value2 can match both values).
- missing_parameter_pass (boolean): If set, the check will also pass if the parameter is not present in the configuration file (default is applied).
- multi_value (boolean): If set, it means that the parameter can accept multiple values and the expected value must be present in the current list of values.
- - missing_config_file_fail (boolean): If set, the check will fail if the configuration is not existent in the system.
+ - missing_config_file_fail (boolean): If set, the check will fail if the configuration file doesn't exist in the system.
We specify a case insensitive comparison in the prefix because
sshd_config has case-insensitive parameters (but case-sensitive values).
@@ -250,7 +250,7 @@
- value (String): The variable value WITHOUT QUOTES.
- missing_parameter_pass (boolean): If set, the check will also pass if the parameter is not present in the configuration file (default is applied).
- multi_value (boolean): If set, it means that the parameter can accept multiple values and the expected value must be present in the current list of values.
- - missing_config_file_fail (boolean): If set, the check will fail if the configuration is not existent in the system.
+ - missing_config_file_fail (boolean): If set, the check will fail if the configuration file doesn't exist in the system.
#}}
{{%- macro oval_check_shell_file(path, parameter='', value='', application='', no_quotes=false, missing_parameter_pass=false, multi_value=false, missing_config_file_fail=false) %}}
{{% if no_quotes -%}}
@@ -268,8 +268,7 @@
- value (String): The value to be checked. This can also be a regular expression (e.g: value1|value2 can match both values).
- missing_parameter_pass (boolean): If set, the check will also pass if the parameter is not present in the configuration file (default is applied).
- multi_value (boolean): If set, it means that the parameter can accept multiple values and the expected value must be present in the current list of values.
- - missing_config_file_fail (boolean): If set, the check will fail if the configuration is not existent in the system.
-
+ - missing_config_file_fail (boolean): If set, the check will fail if the configuration file doesn't exist in the system.
#}}
{{%- macro oval_auditd_config(parameter='', value='', missing_parameter_pass=false, multi_value=false, missing_config_file_fail=false) %}}
{{{ oval_check_config_file("/etc/audit/auditd.conf", prefix_regex="^[ \\t]*(?i)", parameter=parameter, separator_regex='(?-i)[ \\t]*=[ \\t]*', value="(?i)"+value+"(?-i)", missing_parameter_pass=missing_parameter_pass, application="auditd", multi_value=multi_value, missing_config_file_fail=missing_config_file_fail) }}}
From af0e3ba8ef2d5b53dcffed4432ec0415a81ab2bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Wed, 22 Jan 2020 11:37:39 +0100
Subject: [PATCH 6/7] Shell lineinfile macros and templates style fixes.
---
shared/macros-ansible.jinja | 2 +-
shared/macros-oval.jinja | 10 ++++++++--
shared/templates/template_ANSIBLE_shell_lineinfile | 4 ++--
3 files changed, 11 insertions(+), 5 deletions(-)
diff --git a/shared/macros-ansible.jinja b/shared/macros-ansible.jinja
index 81e18e2d5c..f752e7a2be 100644
--- a/shared/macros-ansible.jinja
+++ b/shared/macros-ansible.jinja
@@ -25,7 +25,7 @@
{{%- elif insert_before %}}
insertbefore: '{{{ insert_before }}}'
{{%- endif %}}
- {{% else %}}
+ {{%- else %}}
state: '{{{ state }}}'
{{%- endif %}}
{{%- if validate %}}
diff --git a/shared/macros-oval.jinja b/shared/macros-oval.jinja
index cfa9de9d2d..5f391efdcb 100644
--- a/shared/macros-oval.jinja
+++ b/shared/macros-oval.jinja
@@ -13,13 +13,16 @@
- value (String): The value to be checked. This can also be a regular expression (e.g: value1|value2 can match both values).
- separator_regex (String): Regular expression to be used as the separator of parameter and value in a configuration file. If spaces are allowed, this should be included in the regular expression.
- missing_parameter_pass (boolean): If set, the check will also pass if the parameter is not present in the configuration file (default is applied).
- - application (String): The application which the configuration file is being checked. Can be any value and does not affect the OVAL check.
+ - application (String): The application which the configuration file is being checked. Can be any value and does not affect the actual OVAL check.
- multi_value (boolean): If set, it means that the parameter can accept multiple values and the expected value must be present in the current list of values.
- missing_config_file_fail (boolean): If set, the check will fail if the configuration is not existent in the system.
- section (String): If set, the parameter will be checked only within the given section defined by [section].
- quotes (String): If non-empty, one level of matching quotes is considered when checking the value. See comment of oval_line_in_file_state for more info.
#}}
{{%- macro oval_check_config_file(path='', prefix_regex='^[ \\t]*', parameter='', separator_regex='[ \\t]+', value='', missing_parameter_pass=false, application='', multi_value=false, missing_config_file_fail=false, section='', quotes='') -%}}
+{{%- if application == '' -%}}
+ {{%- set application = "The respective application or service" -%}}
+{{%- endif -%}}
<def-group>
<definition class="compliance" id="{{{ rule_id }}}" version="1">
<metadata>
@@ -248,6 +251,9 @@
- path (String): Path to the file.
- parameter (String): The shell variable name.
- value (String): The variable value WITHOUT QUOTES.
+ - application (String): The application which the configuration file is being checked. Can be any value and does not affect the actual OVAL check.
+ - no_quotes (boolean): If set, the check will require that the RHS of the assignment is the literal value, without quotes.
+ If no_quotes is false, then one level of single or double quotes won't be regarded as part of the value by the check.
- missing_parameter_pass (boolean): If set, the check will also pass if the parameter is not present in the configuration file (default is applied).
- multi_value (boolean): If set, it means that the parameter can accept multiple values and the expected value must be present in the current list of values.
- missing_config_file_fail (boolean): If set, the check will fail if the configuration file doesn't exist in the system.
@@ -342,7 +348,7 @@
- parameter (String): The parameter to be checked in the configuration file.
- value (String): The value to be checked. This can also be a regular expression (e.g: value1|value2 can match both values).
- missing_parameter_pass (boolean): If set, the check will also pass if the parameter is not present in the configuration file (default is applied).
- - application (String): The application which the configuration file is being checked. Can be any value and does not affect the OVAL check.
+ - application (String): The application which the configuration file is being checked. Can be any value and does not affect the actual OVAL check.
- multi_value (boolean): If set, it means that the parameter can accept multiple values and the expected value must be present in the current list of values.
- missing_config_file_fail (boolean): If set, the check will fail if the configuration is not existent in the system.
#}}
diff --git a/shared/templates/template_ANSIBLE_shell_lineinfile b/shared/templates/template_ANSIBLE_shell_lineinfile
index 7d0a3ebcbd..3e6c5619ea 100644
--- a/shared/templates/template_ANSIBLE_shell_lineinfile
+++ b/shared/templates/template_ANSIBLE_shell_lineinfile
@@ -3,7 +3,7 @@
# strategy = restrict
# complexity = low
# disruption = low
-{{% set msg = "shell-style assignment of '" ~ PARAMETER ~ "' to '" ~ VALUE ~ "' in '" ~ PATH ~ "'." -%}}
+{{% set msg = "shell-style assignment of '" ~ PARAMETER ~ "' to '" ~ VALUE ~ "' in '" ~ PATH ~ "'" -%}}
{{%- if NO_QUOTES -%}}
{{% set msg = "Setting unquoted " ~ msg %}}
{{%- else -%}}
@@ -15,7 +15,7 @@
path=PATH,
parameter=PARAMETER,
value=VALUE,
- no_quotes=NO_QUOTES
+ no_quotes=NO_QUOTES
)
}}}
From a7779d2fae1086838daa1ded483decd499e8749f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= <matyc@redhat.com>
Date: Tue, 21 Jan 2020 16:43:23 +0100
Subject: [PATCH 7/7] Add a shell_lineinfile template exemplary rule.
---
.../ssh_server/sshd_use_strong_rng/rule.yml | 47 +++++++++++++++++++
.../tests/bad_config.fail.sh | 3 ++
.../tests/good_config.pass.sh | 3 ++
.../tests/no_config.fail.sh | 3 ++
.../sshd_use_strong_rng/tests/quoted.fail.sh | 3 ++
rhel8/profiles/ospp.profile | 1 +
shared/references/cce-redhat-avail.txt | 1 -
7 files changed, 60 insertions(+), 1 deletion(-)
create mode 100644 linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/rule.yml
create mode 100644 linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/bad_config.fail.sh
create mode 100644 linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/good_config.pass.sh
create mode 100644 linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/no_config.fail.sh
create mode 100644 linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/quoted.fail.sh
diff --git a/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/rule.yml b/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/rule.yml
new file mode 100644
index 0000000000..4bfb72702b
--- /dev/null
+++ b/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/rule.yml
@@ -0,0 +1,47 @@
+documentation_complete: true
+
+# TODO: The plan is not to need this for RHEL>=8.4
+# TODO: Compliant setting is SSH_USE_STRONG_RNG set to 32 or more
+prodtype: rhel8
+
+title: 'SSH server uses strong entropy to seed'
+
+description: |-
+ To set up SSH server to use entropy from a high-quality source, edit the <tt>/etc/sysconfig/sshd</tt> file.
+ The <tt>SSH_USE_STRONG_RNG</tt> configuration value determines how many bytes of entropy to use, so
+ make sure that the file contains line
+ <pre>SSH_USE_STRONG_RNG=32</pre>
+
+rationale: |-
+ SSH implementation in RHEL8 uses the openssl library, which doesn't use high-entropy sources by default.
+ Randomness is needed to generate data-encryption keys, and as plaintext padding and initialization vectors
+ in encryption algorithms, and high-quality entropy elliminates the possibility that the output of
+ the random number generator used by SSH would be known to potential attackers.
+
+severity: medium
+
+identifiers:
+ cce@rhel8: 82462-3
+
+references:
+ ospp: FIA_AFL.1
+
+ocil: |-
+ To determine whether the SSH service is configured to use strong entropy seed,
+ run <pre>$ sudo grep SSH_USE_STRONG_RNG /etc/sysconfig/sshd</pre>
+ If a line indicating that SSH_USE_STRONG_RNG is set to 32 is returned,
+ then the option is set correctly.
+
+ocil_clause: |-
+ The SSH_USE_STRONG_RNG is not set to 32 in /etc/sysconfig/sshd
+
+warnings:
+ - general: "This setting can cause problems on computers without the hardware random generator, because insufficient entropy causes the connection to be blocked until enough entropy is available."
+
+template:
+ name: shell_lineinfile
+ vars:
+ path: '/etc/sysconfig/sshd'
+ parameter: 'SSH_USE_STRONG_RNG'
+ value: '32'
+ no_quotes: 'true'
diff --git a/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/bad_config.fail.sh b/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/bad_config.fail.sh
new file mode 100644
index 0000000000..f4f8c22f64
--- /dev/null
+++ b/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/bad_config.fail.sh
@@ -0,0 +1,3 @@
+# platform = multi_platform_rhel
+
+echo 'SSH_USE_STRONG_RNG=1' > /etc/sysconfig/sshd
diff --git a/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/good_config.pass.sh b/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/good_config.pass.sh
new file mode 100644
index 0000000000..70f53ac22b
--- /dev/null
+++ b/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/good_config.pass.sh
@@ -0,0 +1,3 @@
+# platform = multi_platform_rhel
+
+echo 'SSH_USE_STRONG_RNG=32' > /etc/sysconfig/sshd
diff --git a/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/no_config.fail.sh b/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/no_config.fail.sh
new file mode 100644
index 0000000000..1e5f0b2998
--- /dev/null
+++ b/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/no_config.fail.sh
@@ -0,0 +1,3 @@
+# platform = multi_platform_rhel
+
+rm -f /etc/sysconfig/sshd
diff --git a/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/quoted.fail.sh b/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/quoted.fail.sh
new file mode 100644
index 0000000000..a10d24a73b
--- /dev/null
+++ b/linux_os/guide/services/ssh/ssh_server/sshd_use_strong_rng/tests/quoted.fail.sh
@@ -0,0 +1,3 @@
+# platform = multi_platform_rhel
+
+echo 'SSH_USE_STRONG_RNG="32"' > /etc/sysconfig/sshd
diff --git a/rhel8/profiles/ospp.profile b/rhel8/profiles/ospp.profile
index f97527a914..63aea526b7 100644
--- a/rhel8/profiles/ospp.profile
+++ b/rhel8/profiles/ospp.profile
@@ -58,6 +58,7 @@ selections:
- sshd_set_keepalive
- sshd_enable_warning_banner
- sshd_rekey_limit
+ - sshd_use_strong_rng
# Time Server
- chronyd_client_only
diff --git a/shared/references/cce-redhat-avail.txt b/shared/references/cce-redhat-avail.txt
index b665fa1cea..1ff291c7df 100644
--- a/shared/references/cce-redhat-avail.txt
+++ b/shared/references/cce-redhat-avail.txt
@@ -1,4 +1,3 @@
-CCE-82462-3
CCE-82463-1
CCE-82464-9
CCE-82465-6