diff --git a/linux_os/guide/auditing/auditd_configure_rules/audit_rules_sudoers_d/rule.yml b/linux_os/guide/auditing/auditd_configure_rules/audit_rules_sudoers_d/rule.yml index 5a91e876a445..94c775e562e0 100644 --- a/linux_os/guide/auditing/auditd_configure_rules/audit_rules_sudoers_d/rule.yml +++ b/linux_os/guide/auditing/auditd_configure_rules/audit_rules_sudoers_d/rule.yml @@ -6,7 +6,7 @@ title: 'Ensure auditd Collects System Administrator Actions - /etc/sudoers.d/' description: |- At a minimum, the audit system should collect administrator actions for all users and root. - {{{ describe_audit_rules_watch("/etc/sudoers.d/", "actions") }}} + {{{ describe_audit_rules_watch("/etc/sudoers.d", "actions") }}} rationale: |- The actions taken by system administrators should be audited to keep a record @@ -30,14 +30,14 @@ references: ocil_clause: 'the command does not return a line, or the line is commented out' ocil: |- - {{{ ocil_audit_rules_watch("/etc/sudoers.d/", "actions") }}} + {{{ ocil_audit_rules_watch("/etc/sudoers.d", "actions") }}} -fixtext: '{{{ fixtext_audit_file_watch_rule("/etc/sudoers.d/", "identity", "/etc/audit/rules.d/audit.rules") }}}' +fixtext: '{{{ fixtext_audit_file_watch_rule("/etc/sudoers.d", "identity", "/etc/audit/rules.d/audit.rules") }}}' -srg_requirement: '{{{ srg_requirement_audit_file_watch_rule("/etc/sudoers.d/") }}}' +srg_requirement: '{{{ srg_requirement_audit_file_watch_rule("/etc/sudoers.d") }}}' template: name: audit_rules_watch vars: - path: /etc/sudoers.d/ + path: /etc/sudoers.d key: actions diff --git a/shared/templates/audit_rules_watch/oval.template b/shared/templates/audit_rules_watch/oval.template index f610dc6db7e4..ee4fbda2de1c 100644 --- a/shared/templates/audit_rules_watch/oval.template +++ b/shared/templates/audit_rules_watch/oval.template @@ -1,13 +1,13 @@ {{% macro local_variable_modern_style(lvarid, arch) %}} - ^\-a\s+always,exit\s+\-F\s+arch={{{ arch }}}\s+\-F\s+{{{ FILTER_TYPE }}}= + ^\-a\s+always,exit\s+\-F\s+arch={{{ arch }}}\s+\-F\s+(dir|path)= {{% if PATH_IS_VARIABLE %}} {{% else %}} {{{ PATH_ESCAPED }}} {{% endif %}} - \s+\-F\s+perm=\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + /?\s+\-F\s+perm=\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ {{% endmacro %}} @@ -109,7 +109,7 @@ {{% else %}} {{{ PATH_ESCAPED }}} {{% endif %}} - [\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ + /?[\s]+\-p[\s]+\b([rx]*w[rx]*a[rx]*|[rx]*a[rx]*w[rx]*)\b.*$ {{% endif %}} diff --git a/shared/templates/audit_rules_watch/template.py b/shared/templates/audit_rules_watch/template.py index 1ed61e14622e..732506b957bb 100644 --- a/shared/templates/audit_rules_watch/template.py +++ b/shared/templates/audit_rules_watch/template.py @@ -7,7 +7,13 @@ def preprocess(data, lang): name = ssg.utils.escape_id(os.path.basename(os.path.normpath(path))) data["name"] = name if lang == "oval": - data["path_escaped"] = path.replace("/", "\\/") + # Normalize away a trailing slash so it can be made optional in the + # OVAL pattern (via '/?'). This lets the check accept the watched path + # with or without a trailing slash regardless of how the rule is + # written on disk (e.g. both '-F dir=/etc/sudoers.d' and + # '-F path=/etc/sudoers.d/'). + normalized_path = path.rstrip("/") or "/" + data["path_escaped"] = normalized_path.replace("/", "\\/") if "key" not in data: data["key"] = data["_rule_id"] if data["path"].endswith("/"): diff --git a/shared/templates/audit_rules_watch/tests/auditctl_alternate_filter_type.pass.sh b/shared/templates/audit_rules_watch/tests/auditctl_alternate_filter_type.pass.sh new file mode 100644 index 000000000000..0908cdbb9209 --- /dev/null +++ b/shared/templates/audit_rules_watch/tests/auditctl_alternate_filter_type.pass.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# packages = audit + +{{{ setup_auditctl_environment() }}} +path={{{ PATH }}} +style={{{ audit_watches_style }}} +filter_type={{{ FILTER_TYPE }}} +. $SHARED/audit_rules_watch/auditctl_alternate_filter_type.pass.sh diff --git a/shared/templates/audit_rules_watch/tests/auditctl_trailing_slash_toggle.pass.sh b/shared/templates/audit_rules_watch/tests/auditctl_trailing_slash_toggle.pass.sh new file mode 100644 index 000000000000..5a30f28c1088 --- /dev/null +++ b/shared/templates/audit_rules_watch/tests/auditctl_trailing_slash_toggle.pass.sh @@ -0,0 +1,8 @@ +#!/bin/bash +# packages = audit + +{{{ setup_auditctl_environment() }}} +path={{{ PATH }}} +style={{{ audit_watches_style }}} +filter_type={{{ FILTER_TYPE }}} +. $SHARED/audit_rules_watch/auditctl_trailing_slash_toggle.pass.sh diff --git a/shared/templates/audit_rules_watch/tests/augenrules_alternate_filter_type.pass.sh b/shared/templates/audit_rules_watch/tests/augenrules_alternate_filter_type.pass.sh new file mode 100644 index 000000000000..fa90c39cbe6c --- /dev/null +++ b/shared/templates/audit_rules_watch/tests/augenrules_alternate_filter_type.pass.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# packages = audit + +{{{ setup_augenrules_environment() }}} + +path={{{ PATH }}} +style={{{ audit_watches_style }}} +filter_type={{{ FILTER_TYPE }}} +. $SHARED/audit_rules_watch/augenrules_alternate_filter_type.pass.sh diff --git a/shared/templates/audit_rules_watch/tests/augenrules_trailing_slash_toggle.pass.sh b/shared/templates/audit_rules_watch/tests/augenrules_trailing_slash_toggle.pass.sh new file mode 100644 index 000000000000..f3358e011245 --- /dev/null +++ b/shared/templates/audit_rules_watch/tests/augenrules_trailing_slash_toggle.pass.sh @@ -0,0 +1,9 @@ +#!/bin/bash +# packages = audit + +{{{ setup_augenrules_environment() }}} + +path={{{ PATH }}} +style={{{ audit_watches_style }}} +filter_type={{{ FILTER_TYPE }}} +. $SHARED/audit_rules_watch/augenrules_trailing_slash_toggle.pass.sh diff --git a/tests/shared/audit_rules_watch/auditctl_alternate_filter_type.pass.sh b/tests/shared/audit_rules_watch/auditctl_alternate_filter_type.pass.sh new file mode 100644 index 000000000000..4daf4e3c7665 --- /dev/null +++ b/tests/shared/audit_rules_watch/auditctl_alternate_filter_type.pass.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# packages = audit + +# The OVAL check accepts either the 'dir=' (subtree) or 'path=' (single object) +# filter type. Configure the rule using the opposite filter type from the one +# the remediation would emit and confirm the check still passes. +if [[ "$style" == "modern" ]] ; then + if [[ "$filter_type" == "dir" ]]; then alt_filter="path"; else alt_filter="dir"; fi + echo "-a always,exit -F arch=b32 -F $alt_filter=$path -F perm=wa -F key=logins" >> /etc/audit/audit.rules + echo "-a always,exit -F arch=b64 -F $alt_filter=$path -F perm=wa -F key=logins" >> /etc/audit/audit.rules +else + # Legacy '-w' watches have no dir/path distinction; use the standard form. + echo "-w $path -p wa -k login" >> /etc/audit/audit.rules +fi diff --git a/tests/shared/audit_rules_watch/auditctl_trailing_slash_toggle.pass.sh b/tests/shared/audit_rules_watch/auditctl_trailing_slash_toggle.pass.sh new file mode 100644 index 000000000000..a8ed2ef80000 --- /dev/null +++ b/tests/shared/audit_rules_watch/auditctl_trailing_slash_toggle.pass.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# packages = audit + +# The OVAL check treats a trailing slash on the watched path as optional. +# Toggle the trailing slash relative to the rule's configured path and confirm +# the check still passes. +if [[ "$path" == */ ]]; then toggled_path="${path%/}"; else toggled_path="${path}/"; fi + +if [[ "$style" == "modern" ]] ; then + echo "-a always,exit -F arch=b32 -F $filter_type=$toggled_path -F perm=wa -F key=logins" >> /etc/audit/audit.rules + echo "-a always,exit -F arch=b64 -F $filter_type=$toggled_path -F perm=wa -F key=logins" >> /etc/audit/audit.rules +else + echo "-w $toggled_path -p wa -k login" >> /etc/audit/audit.rules +fi diff --git a/tests/shared/audit_rules_watch/augenrules_alternate_filter_type.pass.sh b/tests/shared/audit_rules_watch/augenrules_alternate_filter_type.pass.sh new file mode 100644 index 000000000000..2c8717c55070 --- /dev/null +++ b/tests/shared/audit_rules_watch/augenrules_alternate_filter_type.pass.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# packages = audit + +# The OVAL check accepts either the 'dir=' (subtree) or 'path=' (single object) +# filter type. Configure the rule using the opposite filter type from the one +# the remediation would emit and confirm the check still passes. +if [[ "$style" == "modern" ]] ; then + if [[ "$filter_type" == "dir" ]]; then alt_filter="path"; else alt_filter="dir"; fi + echo "-a always,exit -F arch=b32 -F $alt_filter=$path -F perm=wa -F key=logins" >> /etc/audit/rules.d/login.rules + echo "-a always,exit -F arch=b64 -F $alt_filter=$path -F perm=wa -F key=logins" >> /etc/audit/rules.d/login.rules +else + # Legacy '-w' watches have no dir/path distinction; use the standard form. + echo "-w $path -p wa -k login" >> /etc/audit/rules.d/login.rules +fi diff --git a/tests/shared/audit_rules_watch/augenrules_trailing_slash_toggle.pass.sh b/tests/shared/audit_rules_watch/augenrules_trailing_slash_toggle.pass.sh new file mode 100644 index 000000000000..3259be7c3566 --- /dev/null +++ b/tests/shared/audit_rules_watch/augenrules_trailing_slash_toggle.pass.sh @@ -0,0 +1,14 @@ +#!/bin/bash +# packages = audit + +# The OVAL check treats a trailing slash on the watched path as optional. +# Toggle the trailing slash relative to the rule's configured path and confirm +# the check still passes. +if [[ "$path" == */ ]]; then toggled_path="${path%/}"; else toggled_path="${path}/"; fi + +if [[ "$style" == "modern" ]] ; then + echo "-a always,exit -F arch=b32 -F $filter_type=$toggled_path -F perm=wa -F key=logins" >> /etc/audit/rules.d/login.rules + echo "-a always,exit -F arch=b64 -F $filter_type=$toggled_path -F perm=wa -F key=logins" >> /etc/audit/rules.d/login.rules +else + echo "-w $toggled_path -p wa -k login" >> /etc/audit/rules.d/login.rules +fi