Blob Blame History Raw
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