From e826795667e319a336ccbfe0919c044766801cb8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mat=C4=9Bj=20T=C3=BD=C4=8D?= 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='') -%}} @@ -60,7 +61,7 @@ {{{ 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 %}} {{{ regex }}} @@ -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?= 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?= 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?= 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?= 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?= 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 -%}} @@ -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?= 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 /etc/sysconfig/sshd file. + The SSH_USE_STRONG_RNG configuration value determines how many bytes of entropy to use, so + make sure that the file contains line +
SSH_USE_STRONG_RNG=32
+ +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
$ sudo grep SSH_USE_STRONG_RNG /etc/sysconfig/sshd
+ 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