#!/usr/bin/env bash #DESC Harden system per pa-dss4.0 recommendations. #HELP command to generate this file: #HELP oscap xccdf generate fix --profile pci-dss /usr/share/xml/scap/ssg/content/ssg-rhel9-ds.xml > padss4_harden #install or update deps yum -y openscap #fix authselect authselect select minimal --force #fix usr2 perms chown -R root:root /usr2/basis /usr2/bbx/bin/rtiperms.pl /usr2/bbx #scare message echo "+----------------------------------------------------+ | This is a controlled access system. The activities | | on this system are monitored. | | Evidence of unauthorised activities may be | | disclosed to the appropriate authorities. | +----------------------------------------------------+" > /etc/issue ############################################################################### # # Bash Remediation Script for PCI-DSS v4.0.1 Control Baseline for Red Hat Enterprise Linux 9 # # Profile Description: # Payment Card Industry - Data Security Standard (PCI-DSS) is a set of # security standards designed to ensure the secure handling of payment card # data, with the goal of preventing data breaches and protecting sensitive # financial information. # This profile ensures Red Hat Enterprise Linux 9 is configured in alignment # with PCI-DSS v4.0.1 requirements. # # Profile ID: xccdf_org.ssgproject.content_profile_pci-dss # Benchmark ID: xccdf_org.ssgproject.content_benchmark_RHEL-9 # Benchmark Version: 0.1.79 # XCCDF Version: 1.2 # # This file was generated by OpenSCAP 1.3.13 using: # $ oscap xccdf generate fix --profile xccdf_org.ssgproject.content_profile_pci-dss --fix-type bash xccdf-file.xml # # This Bash Remediation Script is generated from an OpenSCAP profile without preliminary evaluation. # It attempts to fix every selected rule, even if the system is already compliant. # # How to apply this Bash Remediation Script: # $ sudo ./remediation-script.sh # ############################################################################### ############################################################################### # BEGIN fix (1 / 252) for 'xccdf_org.ssgproject.content_rule_rpm_verify_hashes' ############################################################################### (>&2 echo "Remediating rule 1/252: 'xccdf_org.ssgproject.content_rule_rpm_verify_hashes'"); ( # Remediation is applicable only in certain platforms if ! ( { rpm --quiet -q kernel-core ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} && { ! rpm --quiet -q openshift-kubelet ;} && ([ -f /run/ostree-booted ] || [ -L /ostree ]) ); then # Find which files have incorrect hash (not in /etc, because of the system related config files) and then get files names files_with_incorrect_hash="$(rpm -Va --noconfig | grep -E '^..5' | awk '{print $NF}' )" if [ -n "$files_with_incorrect_hash" ]; then # From files names get package names and change newline to space, because rpm writes each package to new line packages_to_reinstall="$(rpm -qf $files_with_incorrect_hash | tr '\n' ' ')" dnf reinstall -y $packages_to_reinstall fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_rpm_verify_hashes' ############################################################################### # BEGIN fix (2 / 252) for 'xccdf_org.ssgproject.content_rule_rpm_verify_ownership' ############################################################################### (>&2 echo "Remediating rule 2/252: 'xccdf_org.ssgproject.content_rule_rpm_verify_ownership'"); ( # Remediation is applicable only in certain platforms if ! ( { rpm --quiet -q kernel-core ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} && { ! rpm --quiet -q openshift-kubelet ;} && ([ -f /run/ostree-booted ] || [ -L /ostree ]) ); then # Declare array to hold set of RPM packages we need to correct permissions for declare -A SETPERMS_RPM_DICT # Create a list of files on the system having permissions different from what # is expected by the RPM database readarray -t FILES_WITH_INCORRECT_PERMS < <(rpm -Va --nofiledigest | awk '{ if (substr($0,6,1)=="U" || substr($0,7,1)=="G") print $NF }') for FILE_PATH in "${FILES_WITH_INCORRECT_PERMS[@]}" do RPM_PACKAGE=$(rpm -qf "$FILE_PATH") # Use an associative array to store packages as it's keys, not having to care about duplicates. SETPERMS_RPM_DICT["$RPM_PACKAGE"]=1 done # For each of the RPM packages left in the list -- reset its permissions to the # correct values for RPM_PACKAGE in "${!SETPERMS_RPM_DICT[@]}" do rpm --restore "${RPM_PACKAGE}" done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_rpm_verify_ownership' ############################################################################### # BEGIN fix (3 / 252) for 'xccdf_org.ssgproject.content_rule_package_aide_installed' ############################################################################### (>&2 echo "Remediating rule 3/252: 'xccdf_org.ssgproject.content_rule_package_aide_installed'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "aide" ; then dnf install -y "aide" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_aide_installed' ############################################################################### # BEGIN fix (4 / 252) for 'xccdf_org.ssgproject.content_rule_aide_build_database' ############################################################################### (>&2 echo "Remediating rule 4/252: 'xccdf_org.ssgproject.content_rule_aide_build_database'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "aide" ; then dnf install -y "aide" fi /usr/sbin/aide --init /bin/cp -p /var/lib/aide/aide.db.new.gz /var/lib/aide/aide.db.gz else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_aide_build_database' ############################################################################### # BEGIN fix (5 / 252) for 'xccdf_org.ssgproject.content_rule_aide_periodic_cron_checking' ############################################################################### (>&2 echo "Remediating rule 5/252: 'xccdf_org.ssgproject.content_rule_aide_periodic_cron_checking'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "aide" ; then dnf install -y "aide" fi if ! rpm -q --quiet "cronie" ; then dnf install -y "cronie" fi CRON_FILE="/etc/crontab" if ! grep -q "/usr/sbin/aide --check" "${CRON_FILE}" ; then echo "05 4 * * * root /usr/sbin/aide --check" >> "${CRON_FILE}" else sed -i '\!^.* --check.*$!d' "${CRON_FILE}" echo "05 4 * * * root /usr/sbin/aide --check" >> "${CRON_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_aide_periodic_cron_checking' ############################################################################### # BEGIN fix (6 / 252) for 'xccdf_org.ssgproject.content_rule_configure_crypto_policy' ############################################################################### (>&2 echo "Remediating rule 6/252: 'xccdf_org.ssgproject.content_rule_configure_crypto_policy'"); ( var_system_crypto_policy='DEFAULT' stderr_of_call=$(update-crypto-policies --set ${var_system_crypto_policy} 2>&1 > /dev/null) rc=$? if test "$rc" = 127; then echo "$stderr_of_call" >&2 echo "Make sure that the script is installed on the remediated system." >&2 echo "See output of the 'dnf provides update-crypto-policies' command" >&2 echo "to see what package to (re)install" >&2 false # end with an error code elif test "$rc" != 0; then echo "Error invoking the update-crypto-policies script: $stderr_of_call" >&2 false # end with an error code fi ) # END fix for 'xccdf_org.ssgproject.content_rule_configure_crypto_policy' ############################################################################### # BEGIN fix (7 / 252) for 'xccdf_org.ssgproject.content_rule_configure_ssh_crypto_policy' ############################################################################### (>&2 echo "Remediating rule 7/252: 'xccdf_org.ssgproject.content_rule_configure_ssh_crypto_policy'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then SSH_CONF="/etc/sysconfig/sshd" sed -i "/^\s*CRYPTO_POLICY.*$/Id" $SSH_CONF else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_configure_ssh_crypto_policy' ############################################################################### # BEGIN fix (8 / 252) for 'xccdf_org.ssgproject.content_rule_dconf_db_up_to_date' ############################################################################### (>&2 echo "Remediating rule 8/252: 'xccdf_org.ssgproject.content_rule_dconf_db_up_to_date'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q gdm && { rpm --quiet -q kernel-core; }; then dconf update else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_dconf_db_up_to_date' ############################################################################### # BEGIN fix (9 / 252) for 'xccdf_org.ssgproject.content_rule_enable_dconf_user_profile' ############################################################################### (>&2 echo "Remediating rule 9/252: 'xccdf_org.ssgproject.content_rule_enable_dconf_user_profile'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_enable_dconf_user_profile' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_enable_dconf_user_profile' ############################################################################### # BEGIN fix (10 / 252) for 'xccdf_org.ssgproject.content_rule_gnome_gdm_disable_automatic_login' ############################################################################### (>&2 echo "Remediating rule 10/252: 'xccdf_org.ssgproject.content_rule_gnome_gdm_disable_automatic_login'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q gdm; then if rpm --quiet -q gdm then if ! grep -q "^AutomaticLoginEnable=" /etc/gdm/custom.conf then sed -i "/^\[daemon\]/a \ AutomaticLoginEnable=False" /etc/gdm/custom.conf else sed -i "s/^AutomaticLoginEnable=.*/AutomaticLoginEnable=False/g" /etc/gdm/custom.conf fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_gnome_gdm_disable_automatic_login' ############################################################################### # BEGIN fix (11 / 252) for 'xccdf_org.ssgproject.content_rule_dconf_gnome_disable_automount' ############################################################################### (>&2 echo "Remediating rule 11/252: 'xccdf_org.ssgproject.content_rule_dconf_gnome_disable_automount'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q gdm; then # Check for setting in any of the DConf db directories # If files contain ibus or distro, ignore them. # The assignment assumes that individual filenames don't contain : readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/media-handling\\]" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1) DCONFFILE="/etc/dconf/db/local.d/00-security-settings" DBDIR="/etc/dconf/db/local.d" mkdir -p "${DBDIR}" # Comment out the configurations in databases different from the target one if [ "${#SETTINGSFILES[@]}" -ne 0 ] then if grep -q "^\\s*automount\\s*=" "${SETTINGSFILES[@]}" then sed -Ei "s/(^\s*)automount(\s*=)/#\1automount\2/g" "${SETTINGSFILES[@]}" fi fi [ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}" if ! grep -q "\\[org/gnome/desktop/media-handling\\]" "${DCONFFILE}" then printf '%s\n' "[org/gnome/desktop/media-handling]" >> ${DCONFFILE} fi escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" if grep -q "^\\s*automount\\s*=" "${DCONFFILE}" then sed -i "s/\\s*automount\\s*=\\s*.*/automount=${escaped_value}/g" "${DCONFFILE}" else sed -i "\\|\\[org/gnome/desktop/media-handling\\]|a\\automount=${escaped_value}" "${DCONFFILE}" fi dconf update # Check for setting in any of the DConf db directories LOCKFILES=$(grep -r "^/org/gnome/desktop/media-handling/automount$" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1) LOCKSFOLDER="/etc/dconf/db/local.d/locks" mkdir -p "${LOCKSFOLDER}" # Comment out the configurations in databases different from the target one if [[ ! -z "${LOCKFILES}" ]] then sed -i -E "s|^/org/gnome/desktop/media-handling/automount$|#&|" "${LOCKFILES[@]}" fi if ! grep -qr "^/org/gnome/desktop/media-handling/automount$" /etc/dconf/db/local.d/ then echo "/org/gnome/desktop/media-handling/automount" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" fi dconf update else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_dconf_gnome_disable_automount' ############################################################################### # BEGIN fix (12 / 252) for 'xccdf_org.ssgproject.content_rule_dconf_gnome_disable_automount_open' ############################################################################### (>&2 echo "Remediating rule 12/252: 'xccdf_org.ssgproject.content_rule_dconf_gnome_disable_automount_open'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q gdm; then # Check for setting in any of the DConf db directories # If files contain ibus or distro, ignore them. # The assignment assumes that individual filenames don't contain : readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/media-handling\\]" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1) DCONFFILE="/etc/dconf/db/local.d/00-security-settings" DBDIR="/etc/dconf/db/local.d" mkdir -p "${DBDIR}" # Comment out the configurations in databases different from the target one if [ "${#SETTINGSFILES[@]}" -ne 0 ] then if grep -q "^\\s*automount-open\\s*=" "${SETTINGSFILES[@]}" then sed -Ei "s/(^\s*)automount-open(\s*=)/#\1automount-open\2/g" "${SETTINGSFILES[@]}" fi fi [ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}" if ! grep -q "\\[org/gnome/desktop/media-handling\\]" "${DCONFFILE}" then printf '%s\n' "[org/gnome/desktop/media-handling]" >> ${DCONFFILE} fi escaped_value="$(sed -e 's/\\/\\\\/g' <<< "false")" if grep -q "^\\s*automount-open\\s*=" "${DCONFFILE}" then sed -i "s/\\s*automount-open\\s*=\\s*.*/automount-open=${escaped_value}/g" "${DCONFFILE}" else sed -i "\\|\\[org/gnome/desktop/media-handling\\]|a\\automount-open=${escaped_value}" "${DCONFFILE}" fi dconf update # Check for setting in any of the DConf db directories LOCKFILES=$(grep -r "^/org/gnome/desktop/media-handling/automount-open$" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1) LOCKSFOLDER="/etc/dconf/db/local.d/locks" mkdir -p "${LOCKSFOLDER}" # Comment out the configurations in databases different from the target one if [[ ! -z "${LOCKFILES}" ]] then sed -i -E "s|^/org/gnome/desktop/media-handling/automount-open$|#&|" "${LOCKFILES[@]}" fi if ! grep -qr "^/org/gnome/desktop/media-handling/automount-open$" /etc/dconf/db/local.d/ then echo "/org/gnome/desktop/media-handling/automount-open" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" fi dconf update else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_dconf_gnome_disable_automount_open' ############################################################################### # BEGIN fix (13 / 252) for 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_activation_enabled' ############################################################################### (>&2 echo "Remediating rule 13/252: 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_activation_enabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q gdm; then # Check for setting in any of the DConf db directories # If files contain ibus or distro, ignore them. # The assignment assumes that individual filenames don't contain : readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1) DCONFFILE="/etc/dconf/db/local.d/00-security-settings" DBDIR="/etc/dconf/db/local.d" mkdir -p "${DBDIR}" # Comment out the configurations in databases different from the target one if [ "${#SETTINGSFILES[@]}" -ne 0 ] then if grep -q "^\\s*idle-activation-enabled\\s*=" "${SETTINGSFILES[@]}" then sed -Ei "s/(^\s*)idle-activation-enabled(\s*=)/#\1idle-activation-enabled\2/g" "${SETTINGSFILES[@]}" fi fi [ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}" if ! grep -q "\\[org/gnome/desktop/screensaver\\]" "${DCONFFILE}" then printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} fi escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" if grep -q "^\\s*idle-activation-enabled\\s*=" "${DCONFFILE}" then sed -i "s/\\s*idle-activation-enabled\\s*=\\s*.*/idle-activation-enabled=${escaped_value}/g" "${DCONFFILE}" else sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\idle-activation-enabled=${escaped_value}" "${DCONFFILE}" fi dconf update # Check for setting in any of the DConf db directories LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/idle-activation-enabled$" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1) LOCKSFOLDER="/etc/dconf/db/local.d/locks" mkdir -p "${LOCKSFOLDER}" # Comment out the configurations in databases different from the target one if [[ ! -z "${LOCKFILES}" ]] then sed -i -E "s|^/org/gnome/desktop/screensaver/idle-activation-enabled$|#&|" "${LOCKFILES[@]}" fi if ! grep -qr "^/org/gnome/desktop/screensaver/idle-activation-enabled$" /etc/dconf/db/local.d/ then echo "/org/gnome/desktop/screensaver/idle-activation-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" fi dconf update else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_activation_enabled' ############################################################################### # BEGIN fix (14 / 252) for 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_delay' ############################################################################### (>&2 echo "Remediating rule 14/252: 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_delay'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q gdm; then inactivity_timeout_value='900' # Check for setting in any of the DConf db directories # If files contain ibus or distro, ignore them. # The assignment assumes that individual filenames don't contain : readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/session\\]" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1) DCONFFILE="/etc/dconf/db/local.d/00-security-settings" DBDIR="/etc/dconf/db/local.d" mkdir -p "${DBDIR}" # Comment out the configurations in databases different from the target one if [ "${#SETTINGSFILES[@]}" -ne 0 ] then if grep -q "^\\s*idle-delay\\s*=" "${SETTINGSFILES[@]}" then sed -Ei "s/(^\s*)idle-delay(\s*=)/#\1idle-delay\2/g" "${SETTINGSFILES[@]}" fi fi [ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}" if ! grep -q "\\[org/gnome/desktop/session\\]" "${DCONFFILE}" then printf '%s\n' "[org/gnome/desktop/session]" >> ${DCONFFILE} fi escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${inactivity_timeout_value}")" if grep -q "^\\s*idle-delay\\s*=" "${DCONFFILE}" then sed -i "s/\\s*idle-delay\\s*=\\s*.*/idle-delay=${escaped_value}/g" "${DCONFFILE}" else sed -i "\\|\\[org/gnome/desktop/session\\]|a\\idle-delay=${escaped_value}" "${DCONFFILE}" fi dconf update else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_idle_delay' ############################################################################### # BEGIN fix (15 / 252) for 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_delay' ############################################################################### (>&2 echo "Remediating rule 15/252: 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_delay'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q gdm; then var_screensaver_lock_delay='10' # Check for setting in any of the DConf db directories # If files contain ibus or distro, ignore them. # The assignment assumes that individual filenames don't contain : readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1) DCONFFILE="/etc/dconf/db/local.d/00-security-settings" DBDIR="/etc/dconf/db/local.d" mkdir -p "${DBDIR}" # Comment out the configurations in databases different from the target one if [ "${#SETTINGSFILES[@]}" -ne 0 ] then if grep -q "^\\s*lock-delay\\s*=" "${SETTINGSFILES[@]}" then sed -Ei "s/(^\s*)lock-delay(\s*=)/#\1lock-delay\2/g" "${SETTINGSFILES[@]}" fi fi [ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}" if ! grep -q "\\[org/gnome/desktop/screensaver\\]" "${DCONFFILE}" then printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} fi escaped_value="$(sed -e 's/\\/\\\\/g' <<< "uint32 ${var_screensaver_lock_delay}")" if grep -q "^\\s*lock-delay\\s*=" "${DCONFFILE}" then sed -i "s/\\s*lock-delay\\s*=\\s*.*/lock-delay=${escaped_value}/g" "${DCONFFILE}" else sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-delay=${escaped_value}" "${DCONFFILE}" fi dconf update else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_delay' ############################################################################### # BEGIN fix (16 / 252) for 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_enabled' ############################################################################### (>&2 echo "Remediating rule 16/252: 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_enabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q gdm; then # Check for setting in any of the DConf db directories # If files contain ibus or distro, ignore them. # The assignment assumes that individual filenames don't contain : readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1) DCONFFILE="/etc/dconf/db/local.d/00-security-settings" DBDIR="/etc/dconf/db/local.d" mkdir -p "${DBDIR}" # Comment out the configurations in databases different from the target one if [ "${#SETTINGSFILES[@]}" -ne 0 ] then if grep -q "^\\s*lock-enabled\\s*=" "${SETTINGSFILES[@]}" then sed -Ei "s/(^\s*)lock-enabled(\s*=)/#\1lock-enabled\2/g" "${SETTINGSFILES[@]}" fi fi [ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}" if ! grep -q "\\[org/gnome/desktop/screensaver\\]" "${DCONFFILE}" then printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} fi escaped_value="$(sed -e 's/\\/\\\\/g' <<< "true")" if grep -q "^\\s*lock-enabled\\s*=" "${DCONFFILE}" then sed -i "s/\\s*lock-enabled\\s*=\\s*.*/lock-enabled=${escaped_value}/g" "${DCONFFILE}" else sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\lock-enabled=${escaped_value}" "${DCONFFILE}" fi dconf update # Check for setting in any of the DConf db directories LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/lock-enabled$" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1) LOCKSFOLDER="/etc/dconf/db/local.d/locks" mkdir -p "${LOCKSFOLDER}" # Comment out the configurations in databases different from the target one if [[ ! -z "${LOCKFILES}" ]] then sed -i -E "s|^/org/gnome/desktop/screensaver/lock-enabled$|#&|" "${LOCKFILES[@]}" fi if ! grep -qr "^/org/gnome/desktop/screensaver/lock-enabled$" /etc/dconf/db/local.d/ then echo "/org/gnome/desktop/screensaver/lock-enabled" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" fi dconf update else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_lock_enabled' ############################################################################### # BEGIN fix (17 / 252) for 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_mode_blank' ############################################################################### (>&2 echo "Remediating rule 17/252: 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_mode_blank'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q gdm; then # Check for setting in any of the DConf db directories # If files contain ibus or distro, ignore them. # The assignment assumes that individual filenames don't contain : readarray -t SETTINGSFILES < <(grep -r "\\[org/gnome/desktop/screensaver\\]" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | cut -d":" -f1) DCONFFILE="/etc/dconf/db/local.d/00-security-settings" DBDIR="/etc/dconf/db/local.d" mkdir -p "${DBDIR}" # Comment out the configurations in databases different from the target one if [ "${#SETTINGSFILES[@]}" -ne 0 ] then if grep -q "^\\s*picture-uri\\s*=" "${SETTINGSFILES[@]}" then sed -Ei "s/(^\s*)picture-uri(\s*=)/#\1picture-uri\2/g" "${SETTINGSFILES[@]}" fi fi [ ! -z "${DCONFFILE}" ] && echo "" >> "${DCONFFILE}" if ! grep -q "\\[org/gnome/desktop/screensaver\\]" "${DCONFFILE}" then printf '%s\n' "[org/gnome/desktop/screensaver]" >> ${DCONFFILE} fi escaped_value="$(sed -e 's/\\/\\\\/g' <<< "string ''")" if grep -q "^\\s*picture-uri\\s*=" "${DCONFFILE}" then sed -i "s/\\s*picture-uri\\s*=\\s*.*/picture-uri=${escaped_value}/g" "${DCONFFILE}" else sed -i "\\|\\[org/gnome/desktop/screensaver\\]|a\\picture-uri=${escaped_value}" "${DCONFFILE}" fi dconf update # Check for setting in any of the DConf db directories LOCKFILES=$(grep -r "^/org/gnome/desktop/screensaver/picture-uri$" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1) LOCKSFOLDER="/etc/dconf/db/local.d/locks" mkdir -p "${LOCKSFOLDER}" # Comment out the configurations in databases different from the target one if [[ ! -z "${LOCKFILES}" ]] then sed -i -E "s|^/org/gnome/desktop/screensaver/picture-uri$|#&|" "${LOCKFILES[@]}" fi if ! grep -qr "^/org/gnome/desktop/screensaver/picture-uri$" /etc/dconf/db/local.d/ then echo "/org/gnome/desktop/screensaver/picture-uri" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" fi dconf update else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_dconf_gnome_screensaver_mode_blank' ############################################################################### # BEGIN fix (18 / 252) for 'xccdf_org.ssgproject.content_rule_dconf_gnome_session_idle_user_locks' ############################################################################### (>&2 echo "Remediating rule 18/252: 'xccdf_org.ssgproject.content_rule_dconf_gnome_session_idle_user_locks'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q gdm; then # Check for setting in any of the DConf db directories LOCKFILES=$(grep -r "^/org/gnome/desktop/session/idle-delay$" "/etc/dconf/db/" \ | grep -v 'distro\|ibus\|local.d' | grep ":" | cut -d":" -f1) LOCKSFOLDER="/etc/dconf/db/local.d/locks" mkdir -p "${LOCKSFOLDER}" # Comment out the configurations in databases different from the target one if [[ ! -z "${LOCKFILES}" ]] then sed -i -E "s|^/org/gnome/desktop/session/idle-delay$|#&|" "${LOCKFILES[@]}" fi if ! grep -qr "^/org/gnome/desktop/session/idle-delay$" /etc/dconf/db/local.d/ then echo "/org/gnome/desktop/session/idle-delay" >> "/etc/dconf/db/local.d/locks/00-security-settings-lock" fi dconf update else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_dconf_gnome_session_idle_user_locks' ############################################################################### # BEGIN fix (19 / 252) for 'xccdf_org.ssgproject.content_rule_package_sudo_installed' ############################################################################### (>&2 echo "Remediating rule 19/252: 'xccdf_org.ssgproject.content_rule_package_sudo_installed'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "sudo" ; then dnf install -y "sudo" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_sudo_installed' ############################################################################### # BEGIN fix (20 / 252) for 'xccdf_org.ssgproject.content_rule_sudo_add_use_pty' ############################################################################### (>&2 echo "Remediating rule 20/252: 'xccdf_org.ssgproject.content_rule_sudo_add_use_pty'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q sudo; }; then if /usr/sbin/visudo -qcf /etc/sudoers; then cp /etc/sudoers /etc/sudoers.bak if ! grep -P '^[\s]*Defaults\b[^!\n]*\buse_pty.*$' /etc/sudoers; then # sudoers file doesn't define Option use_pty echo "Defaults use_pty" >> /etc/sudoers fi # Check validity of sudoers and cleanup bak if /usr/sbin/visudo -qcf /etc/sudoers; then rm -f /etc/sudoers.bak else echo "Fail to validate remediated /etc/sudoers, reverting to original file." mv /etc/sudoers.bak /etc/sudoers false fi else echo "Skipping remediation, /etc/sudoers failed to validate" false fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sudo_add_use_pty' ############################################################################### # BEGIN fix (21 / 252) for 'xccdf_org.ssgproject.content_rule_sudo_custom_logfile' ############################################################################### (>&2 echo "Remediating rule 21/252: 'xccdf_org.ssgproject.content_rule_sudo_custom_logfile'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q sudo; }; then var_sudo_logfile='/var/log/sudo.log' if /usr/sbin/visudo -qcf /etc/sudoers; then cp /etc/sudoers /etc/sudoers.bak if ! grep -P '^[\s]*Defaults\b[^!\n]*\blogfile\s*=\s*(?:"?([^",\s]+)"?).*$' /etc/sudoers; then # sudoers file doesn't define Option logfile echo "Defaults logfile=${var_sudo_logfile}" >> /etc/sudoers else # sudoers file defines Option logfile, remediate if appropriate value is not set if ! grep -P "^[\s]*Defaults.*\blogfile=${var_sudo_logfile}\b.*$" /etc/sudoers; then escaped_variable=${var_sudo_logfile//$'/'/$'\/'} sed -Ei "s/(^[\s]*Defaults.*\blogfile=)[-]?.+(\b.*$)/\1$escaped_variable\2/" /etc/sudoers fi fi # Check validity of sudoers and cleanup bak if /usr/sbin/visudo -qcf /etc/sudoers; then rm -f /etc/sudoers.bak else echo "Fail to validate remediated /etc/sudoers, reverting to original file." mv /etc/sudoers.bak /etc/sudoers false fi else echo "Skipping remediation, /etc/sudoers failed to validate" false fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sudo_custom_logfile' ############################################################################### # BEGIN fix (22 / 252) for 'xccdf_org.ssgproject.content_rule_sudo_require_authentication' ############################################################################### (>&2 echo "Remediating rule 22/252: 'xccdf_org.ssgproject.content_rule_sudo_require_authentication'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then for f in /etc/sudoers /etc/sudoers.d/* ; do if [ ! -e "$f" ] ; then continue fi matching_list=$(grep -P '^(?!#).*[\s]+NOPASSWD[\s]*\:.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do # comment out "NOPASSWD" matches to preserve user data sed -i "s|^${entry}$|# &|g" $f done <<< "$matching_list" /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo" fi done for f in /etc/sudoers /etc/sudoers.d/* ; do if [ ! -e "$f" ] ; then continue fi matching_list=$(grep -P '^(?!#).*[\s]+\!authenticate.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do # comment out "!authenticate" matches to preserve user data sed -i "s|^${entry}$|# &|g" $f done <<< "$matching_list" /usr/sbin/visudo -cf $f &> /dev/null || echo "Fail to validate $f with visudo" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sudo_require_authentication' ############################################################################### # BEGIN fix (23 / 252) for 'xccdf_org.ssgproject.content_rule_sudo_require_reauthentication' ############################################################################### (>&2 echo "Remediating rule 23/252: 'xccdf_org.ssgproject.content_rule_sudo_require_reauthentication'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q sudo; }; then var_sudo_timestamp_timeout='5' if grep -Px '^[\s]*Defaults.*timestamp_timeout[\s]*=.*' /etc/sudoers.d/*; then find /etc/sudoers.d/ -type f -exec sed -Ei "/^[[:blank:]]*Defaults.*timestamp_timeout[[:blank:]]*=.*/d" {} \; fi if /usr/sbin/visudo -qcf /etc/sudoers; then cp /etc/sudoers /etc/sudoers.bak if ! grep -P '^[\s]*Defaults.*timestamp_timeout[\s]*=[\s]*[-]?\w+.*$' /etc/sudoers; then # sudoers file doesn't define Option timestamp_timeout echo "Defaults timestamp_timeout=${var_sudo_timestamp_timeout}" >> /etc/sudoers else # sudoers file defines Option timestamp_timeout, remediate wrong values if present if grep -qP "^[\s]*Defaults\s.*\btimestamp_timeout[\s]*=[\s]*(?!${var_sudo_timestamp_timeout}\b)[-]?\w+\b.*$" /etc/sudoers; then sed -Ei "s/(^[[:blank:]]*Defaults.*timestamp_timeout[[:blank:]]*=)[[:blank:]]*[-]?\w+(.*$)/\1${var_sudo_timestamp_timeout}\2/" /etc/sudoers fi fi # Check validity of sudoers and cleanup bak if /usr/sbin/visudo -qcf /etc/sudoers; then rm -f /etc/sudoers.bak else echo "Fail to validate remediated /etc/sudoers, reverting to original file." mv /etc/sudoers.bak /etc/sudoers false fi else echo "Skipping remediation, /etc/sudoers failed to validate" false fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sudo_require_reauthentication' ############################################################################### # BEGIN fix (24 / 252) for 'xccdf_org.ssgproject.content_rule_package_cryptsetup-luks_installed' ############################################################################### (>&2 echo "Remediating rule 24/252: 'xccdf_org.ssgproject.content_rule_package_cryptsetup-luks_installed'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "cryptsetup" ; then dnf install -y "cryptsetup" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_cryptsetup-luks_installed' ############################################################################### # BEGIN fix (25 / 252) for 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_globally_activated' ############################################################################### (>&2 echo "Remediating rule 25/252: 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_globally_activated'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q dnf; then # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^gpgcheck") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "1" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^gpgcheck\\>" "/etc/dnf/dnf.conf"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^gpgcheck\\>.*/$escaped_formatted_output/gi" "/etc/dnf/dnf.conf" else if [[ -s "/etc/dnf/dnf.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/dnf/dnf.conf" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/dnf/dnf.conf" fi cce="CCE-83457-2" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/dnf/dnf.conf" >> "/etc/dnf/dnf.conf" printf '%s\n' "$formatted_output" >> "/etc/dnf/dnf.conf" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_globally_activated' ############################################################################### # BEGIN fix (26 / 252) for 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_never_disabled' ############################################################################### (>&2 echo "Remediating rule 26/252: 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_never_disabled'"); ( sed -i 's/gpgcheck\s*=.*/gpgcheck=1/g' /etc/yum.repos.d/* ) # END fix for 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_never_disabled' ############################################################################### # BEGIN fix (27 / 252) for 'xccdf_org.ssgproject.content_rule_ensure_redhat_gpgkey_installed' ############################################################################### (>&2 echo "Remediating rule 27/252: 'xccdf_org.ssgproject.content_rule_ensure_redhat_gpgkey_installed'"); ( # The two fingerprints below are retrieved from https://access.redhat.com/security/team/key readonly REDHAT_RELEASE_FINGERPRINT="567E347AD0044ADE55BA8A5F199E2F91FD431D51" readonly REDHAT_AUXILIARY_FINGERPRINT="7E4624258C406535D56D6F135054E4A45A6340B3" # Location of the key we would like to import (once it's integrity verified) readonly REDHAT_RELEASE_KEY="/etc/pki/rpm-gpg/RPM-GPG-KEY-redhat-release" RPM_GPG_DIR_PERMS=$(stat -c %a "$(dirname "$REDHAT_RELEASE_KEY")") # Verify /etc/pki/rpm-gpg directory permissions are safe if [ "${RPM_GPG_DIR_PERMS}" -le "755" ] then # If they are safe, try to obtain fingerprints from the key file # (to ensure there won't be e.g. CRC error). readarray -t GPG_OUT < <(gpg --show-keys --with-fingerprint --with-colons "$REDHAT_RELEASE_KEY" | grep -A1 "^pub" | grep "^fpr" | cut -d ":" -f 10) GPG_RESULT=$? # No CRC error, safe to proceed if [ "${GPG_RESULT}" -eq "0" ] then echo "${GPG_OUT[*]}" | grep -vE "${REDHAT_RELEASE_FINGERPRINT}|${REDHAT_AUXILIARY_FINGERPRINT}" || { # If $REDHAT_RELEASE_KEY file doesn't contain any keys with unknown fingerprint, import it rpm --import "${REDHAT_RELEASE_KEY}" } fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_ensure_redhat_gpgkey_installed' ############################################################################### # BEGIN fix (28 / 252) for 'xccdf_org.ssgproject.content_rule_security_patches_up_to_date' ############################################################################### (>&2 echo "Remediating rule 28/252: 'xccdf_org.ssgproject.content_rule_security_patches_up_to_date'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_security_patches_up_to_date' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_security_patches_up_to_date' ############################################################################### # BEGIN fix (29 / 252) for 'xccdf_org.ssgproject.content_rule_enable_authselect' ############################################################################### (>&2 echo "Remediating rule 29/252: 'xccdf_org.ssgproject.content_rule_enable_authselect'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_authselect_profile='sssd' authselect current if test "$?" -ne 0; then if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; }; then authselect select --force "$var_authselect_profile" else authselect select "$var_authselect_profile" fi if test "$?" -ne 0; then if rpm --quiet --verify pam; then authselect select --force "$var_authselect_profile" else echo "authselect is not used but files from the 'pam' package have been altered, so the authselect configuration won't be forced." >&2 fi fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_enable_authselect' ############################################################################### # BEGIN fix (30 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_issue_net' ############################################################################### (>&2 echo "Remediating rule 30/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_issue_net'"); ( newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/etc/issue.net" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /etc/issue.net fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_issue_net' ############################################################################### # BEGIN fix (31 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_etc_issue_net' ############################################################################### (>&2 echo "Remediating rule 31/252: 'xccdf_org.ssgproject.content_rule_file_owner_etc_issue_net'"); ( newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/etc/issue.net" | grep -E -w -q "0"; then chown --no-dereference "$newown" /etc/issue.net fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_etc_issue_net' ############################################################################### # BEGIN fix (32 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_etc_issue_net' ############################################################################### (>&2 echo "Remediating rule 32/252: 'xccdf_org.ssgproject.content_rule_file_permissions_etc_issue_net'"); ( chmod u-xs,g-xws,o-xwt /etc/issue.net ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_etc_issue_net' ############################################################################### # BEGIN fix (33 / 252) for 'xccdf_org.ssgproject.content_rule_display_login_attempts' ############################################################################### (>&2 echo "Remediating rule 33/252: 'xccdf_org.ssgproject.content_rule_display_login_attempts'"); ( # Remediation is applicable only in certain platforms if ( rpm --quiet -q pam && rpm --quiet -q kernel-core ); then if [ -f /usr/bin/authselect ]; then if authselect list-features sssd | grep -q with-silent-lastlog; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi authselect disable-feature with-silent-lastlog authselect apply-changes -b else if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/postlogin") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b if [ -e "$PAM_FILE_PATH" ] ; then PAM_FILE_PATH="$PAM_FILE_PATH" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "$PAM_FILE_PATH") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if ! grep -qP "^\s*session\s+\[default=1\]\s+pam_lastlog.so\s*.*" "$PAM_FILE_PATH"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*session\s+.*\s+pam_lastlog.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*session\s+).*(\bpam_lastlog.so.*)/\1[default=1] \2/" "$PAM_FILE_PATH" else LAST_MATCH_LINE=$(grep -nP "^\s*session\s+.*pam_succeed_if\.so.*" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) if [ ! -z $LAST_MATCH_LINE ]; then sed -i --follow-symlinks $LAST_MATCH_LINE" a session [default=1] pam_lastlog.so" "$PAM_FILE_PATH" else echo "session [default=1] pam_lastlog.so" >> "$PAM_FILE_PATH" fi fi fi # Check the option if ! grep -qP "^\s*session\s+\[default=1\]\s+pam_lastlog.so\s*.*\sshowfailed\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "/\s*session\s+\[default=1\]\s+pam_lastlog.so.*/ s/$/ showfailed/" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "$PAM_FILE_PATH was not found" >&2 fi if [ -e "$PAM_FILE_PATH" ] ; then PAM_FILE_PATH="$PAM_FILE_PATH" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "$PAM_FILE_PATH") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if grep -qP "^\s*session\s+[default=1]\s+pam_lastlog.so\s.*\bsilent\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "s/(.*session.*[default=1].*pam_lastlog.so.*)\bsilent\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "$PAM_FILE_PATH was not found" >&2 fi fi else if [ -e "/etc/pam.d/postlogin" ] ; then PAM_FILE_PATH="/etc/pam.d/postlogin" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/postlogin") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if ! grep -qP "^\s*session\s+\[default=1\]\s+pam_lastlog.so\s*.*" "$PAM_FILE_PATH"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*session\s+.*\s+pam_lastlog.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*session\s+).*(\bpam_lastlog.so.*)/\1[default=1] \2/" "$PAM_FILE_PATH" else LAST_MATCH_LINE=$(grep -nP "^\s*session\s+.*pam_succeed_if\.so.*" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) if [ ! -z $LAST_MATCH_LINE ]; then sed -i --follow-symlinks $LAST_MATCH_LINE" a session [default=1] pam_lastlog.so" "$PAM_FILE_PATH" else echo "session [default=1] pam_lastlog.so" >> "$PAM_FILE_PATH" fi fi fi # Check the option if ! grep -qP "^\s*session\s+\[default=1\]\s+pam_lastlog.so\s*.*\sshowfailed\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "/\s*session\s+\[default=1\]\s+pam_lastlog.so.*/ s/$/ showfailed/" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "/etc/pam.d/postlogin was not found" >&2 fi if [ -e "/etc/pam.d/postlogin" ] ; then PAM_FILE_PATH="/etc/pam.d/postlogin" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/postlogin") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if grep -qP "^\s*session\s+[default=1]\s+pam_lastlog.so\s.*\bsilent\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "s/(.*session.*[default=1].*pam_lastlog.so.*)\bsilent\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "/etc/pam.d/postlogin was not found" >&2 fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_display_login_attempts' ############################################################################### # BEGIN fix (34 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth' ############################################################################### (>&2 echo "Remediating rule 34/252: 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q pam; }; then var_password_pam_remember='4' var_password_pam_remember_control_flag='requisite,required' var_password_pam_remember_control_flag="$(echo $var_password_pam_remember_control_flag | cut -d \, -f 1)" if [ -f /usr/bin/authselect ]; then if authselect list-features sssd | grep -q with-pwhistory; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi authselect enable-feature with-pwhistory authselect apply-changes -b else if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b if ! grep -qP "^\s*password\s+\$var_password_pam_remember_control_flag\s+pam_pwhistory.so\s*.*" "$PAM_FILE_PATH"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1$var_password_pam_remember_control_flag \2/" "$PAM_FILE_PATH" else LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) if [ ! -z $LAST_MATCH_LINE ]; then sed -i --follow-symlinks $LAST_MATCH_LINE" a password $var_password_pam_remember_control_flag pam_pwhistory.so" "$PAM_FILE_PATH" else echo "password $var_password_pam_remember_control_flag pam_pwhistory.so" >> "$PAM_FILE_PATH" fi fi fi fi else if ! grep -qP "^\s*password\s+\$var_password_pam_remember_control_flag\s+pam_pwhistory.so\s*.*" "/etc/pam.d/password-auth"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "/etc/pam.d/password-auth")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1$var_password_pam_remember_control_flag \2/" "/etc/pam.d/password-auth" else LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "/etc/pam.d/password-auth" | tail -n 1 | cut -d: -f 1) if [ ! -z $LAST_MATCH_LINE ]; then sed -i --follow-symlinks $LAST_MATCH_LINE" a password $var_password_pam_remember_control_flag pam_pwhistory.so" "/etc/pam.d/password-auth" else echo "password $var_password_pam_remember_control_flag pam_pwhistory.so" >> "/etc/pam.d/password-auth" fi fi fi fi PWHISTORY_CONF="/etc/security/pwhistory.conf" if [ -f $PWHISTORY_CONF ]; then regex="^\s*remember\s*=" line="remember = $var_password_pam_remember" if ! grep -q $regex $PWHISTORY_CONF; then echo $line >> $PWHISTORY_CONF else sed -i --follow-symlinks 's|^\s*\(remember\s*=\s*\)\(\S\+\)|\1'"$var_password_pam_remember"'|g' $PWHISTORY_CONF fi if [ -e "/etc/pam.d/password-auth" ] ; then PAM_FILE_PATH="/etc/pam.d/password-auth" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if grep -qP "^\s*password\s.*\bpam_pwhistory.so\s.*\bremember\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "s/(.*password.*pam_pwhistory.so.*)\bremember\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "/etc/pam.d/password-auth was not found" >&2 fi else PAM_FILE_PATH="/etc/pam.d/password-auth" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/password-auth") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if ! grep -qP "^\s*password\s+requisite\s+pam_pwhistory.so\s*.*" "$PAM_FILE_PATH"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1requisite \2/" "$PAM_FILE_PATH" else echo "password requisite pam_pwhistory.so" >> "$PAM_FILE_PATH" fi fi # Check the option if ! grep -qP "^\s*password\s+requisite\s+pam_pwhistory.so\s*.*\sremember\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "/\s*password\s+requisite\s+pam_pwhistory.so.*/ s/$/ remember=$var_password_pam_remember/" "$PAM_FILE_PATH" else sed -i -E --follow-symlinks "s/(\s*password\s+requisite\s+pam_pwhistory.so\s+.*)(remember=)[[:alnum:]]*\s*(.*)/\1\2$var_password_pam_remember \3/" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_password_auth' ############################################################################### # BEGIN fix (35 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth' ############################################################################### (>&2 echo "Remediating rule 35/252: 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q pam; }; then var_password_pam_remember='4' var_password_pam_remember_control_flag='requisite,required' var_password_pam_remember_control_flag="$(echo $var_password_pam_remember_control_flag | cut -d \, -f 1)" if [ -f /usr/bin/authselect ]; then if authselect list-features sssd | grep -q with-pwhistory; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi authselect enable-feature with-pwhistory authselect apply-changes -b else if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b if ! grep -qP "^\s*password\s+\$var_password_pam_remember_control_flag\s+pam_pwhistory.so\s*.*" "$PAM_FILE_PATH"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1$var_password_pam_remember_control_flag \2/" "$PAM_FILE_PATH" else LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) if [ ! -z $LAST_MATCH_LINE ]; then sed -i --follow-symlinks $LAST_MATCH_LINE" a password $var_password_pam_remember_control_flag pam_pwhistory.so" "$PAM_FILE_PATH" else echo "password $var_password_pam_remember_control_flag pam_pwhistory.so" >> "$PAM_FILE_PATH" fi fi fi fi else if ! grep -qP "^\s*password\s+\$var_password_pam_remember_control_flag\s+pam_pwhistory.so\s*.*" "/etc/pam.d/system-auth"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "/etc/pam.d/system-auth")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1$var_password_pam_remember_control_flag \2/" "/etc/pam.d/system-auth" else LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "/etc/pam.d/system-auth" | tail -n 1 | cut -d: -f 1) if [ ! -z $LAST_MATCH_LINE ]; then sed -i --follow-symlinks $LAST_MATCH_LINE" a password $var_password_pam_remember_control_flag pam_pwhistory.so" "/etc/pam.d/system-auth" else echo "password $var_password_pam_remember_control_flag pam_pwhistory.so" >> "/etc/pam.d/system-auth" fi fi fi fi PWHISTORY_CONF="/etc/security/pwhistory.conf" if [ -f $PWHISTORY_CONF ]; then regex="^\s*remember\s*=" line="remember = $var_password_pam_remember" if ! grep -q $regex $PWHISTORY_CONF; then echo $line >> $PWHISTORY_CONF else sed -i --follow-symlinks 's|^\s*\(remember\s*=\s*\)\(\S\+\)|\1'"$var_password_pam_remember"'|g' $PWHISTORY_CONF fi if [ -e "/etc/pam.d/system-auth" ] ; then PAM_FILE_PATH="/etc/pam.d/system-auth" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if grep -qP "^\s*password\s.*\bpam_pwhistory.so\s.*\bremember\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "s/(.*password.*pam_pwhistory.so.*)\bremember\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "/etc/pam.d/system-auth was not found" >&2 fi else PAM_FILE_PATH="/etc/pam.d/system-auth" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if ! grep -qP "^\s*password\s+requisite\s+pam_pwhistory.so\s*.*" "$PAM_FILE_PATH"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1requisite \2/" "$PAM_FILE_PATH" else echo "password requisite pam_pwhistory.so" >> "$PAM_FILE_PATH" fi fi # Check the option if ! grep -qP "^\s*password\s+requisite\s+pam_pwhistory.so\s*.*\sremember\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "/\s*password\s+requisite\s+pam_pwhistory.so.*/ s/$/ remember=$var_password_pam_remember/" "$PAM_FILE_PATH" else sed -i -E --follow-symlinks "s/(\s*password\s+requisite\s+pam_pwhistory.so\s+.*)(remember=)[[:alnum:]]*\s*(.*)/\1\2$var_password_pam_remember \3/" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_pwhistory_remember_system_auth' ############################################################################### # BEGIN fix (36 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_unix_remember' ############################################################################### (>&2 echo "Remediating rule 36/252: 'xccdf_org.ssgproject.content_rule_accounts_password_pam_unix_remember'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q libpwquality; }; then var_password_pam_unix_remember='4' if [ -f /usr/bin/authselect ]; then if authselect list-features sssd | grep -q with-pwhistory; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi authselect enable-feature with-pwhistory authselect apply-changes -b else if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b if ! grep -qP "^\s*password\s+requisite\s+pam_pwhistory.so\s*.*" "$PAM_FILE_PATH"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1requisite \2/" "$PAM_FILE_PATH" else LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "$PAM_FILE_PATH" | tail -n 1 | cut -d: -f 1) if [ ! -z $LAST_MATCH_LINE ]; then sed -i --follow-symlinks $LAST_MATCH_LINE" a password requisite pam_pwhistory.so" "$PAM_FILE_PATH" else echo "password requisite pam_pwhistory.so" >> "$PAM_FILE_PATH" fi fi fi fi else if ! grep -qP "^\s*password\s+requisite\s+pam_pwhistory.so\s*.*" "/etc/pam.d/system-auth"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "/etc/pam.d/system-auth")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1requisite \2/" "/etc/pam.d/system-auth" else LAST_MATCH_LINE=$(grep -nP "^password.*requisite.*pam_pwquality\.so" "/etc/pam.d/system-auth" | tail -n 1 | cut -d: -f 1) if [ ! -z $LAST_MATCH_LINE ]; then sed -i --follow-symlinks $LAST_MATCH_LINE" a password requisite pam_pwhistory.so" "/etc/pam.d/system-auth" else echo "password requisite pam_pwhistory.so" >> "/etc/pam.d/system-auth" fi fi fi fi PWHISTORY_CONF="/etc/security/pwhistory.conf" if [ -f $PWHISTORY_CONF ]; then regex="^\s*remember\s*=" line="remember = $var_password_pam_unix_remember" if ! grep -q $regex $PWHISTORY_CONF; then echo $line >> $PWHISTORY_CONF else sed -i --follow-symlinks 's|^\s*\(remember\s*=\s*\)\(\S\+\)|\1'"$var_password_pam_unix_remember"'|g' $PWHISTORY_CONF fi if [ -e "/etc/pam.d/system-auth" ] ; then PAM_FILE_PATH="/etc/pam.d/system-auth" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if grep -qP "^\s*password\s.*\bpam_pwhistory.so\s.*\bremember\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "s/(.*password.*pam_pwhistory.so.*)\bremember\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "/etc/pam.d/system-auth was not found" >&2 fi else PAM_FILE_PATH="/etc/pam.d/system-auth" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "/etc/pam.d/system-auth") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if ! grep -qP "^\s*password\s+requisite\s+pam_pwhistory.so\s*.*" "$PAM_FILE_PATH"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*password\s+.*\s+pam_pwhistory.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_pwhistory.so.*)/\1requisite \2/" "$PAM_FILE_PATH" else echo "password requisite pam_pwhistory.so" >> "$PAM_FILE_PATH" fi fi # Check the option if ! grep -qP "^\s*password\s+requisite\s+pam_pwhistory.so\s*.*\sremember\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "/\s*password\s+requisite\s+pam_pwhistory.so.*/ s/$/ remember=$var_password_pam_unix_remember/" "$PAM_FILE_PATH" else sed -i -E --follow-symlinks "s/(\s*password\s+requisite\s+pam_pwhistory.so\s+.*)(remember=)[[:alnum:]]*\s*(.*)/\1\2$var_password_pam_unix_remember \3/" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_unix_remember' ############################################################################### # BEGIN fix (37 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny' ############################################################################### (>&2 echo "Remediating rule 37/252: 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q pam; }; then var_accounts_passwords_pam_faillock_deny='10' if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi authselect enable-feature with-faillock authselect apply-changes -b else AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") for pam_file in "${AUTH_FILES[@]}" do if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" fi sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" done fi AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") SKIP_FAILLOCK_CHECK=false FAILLOCK_CONF="/etc/security/faillock.conf" if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then regex="^\s*deny\s*=" line="deny = $var_accounts_passwords_pam_faillock_deny" if ! grep -q $regex $FAILLOCK_CONF; then echo $line >> $FAILLOCK_CONF else sed -i --follow-symlinks 's|^\s*\(deny\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_deny"'|g' $FAILLOCK_CONF fi for pam_file in "${AUTH_FILES[@]}" do if [ -e "$pam_file" ] ; then PAM_FILE_PATH="$pam_file" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "$pam_file") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\bdeny\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bdeny\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "$pam_file was not found" >&2 fi done else for pam_file in "${AUTH_FILES[@]}" do if ! grep -qE '^\s*auth.*pam_faillock\.so\s+(preauth|authfail).*deny' "$pam_file"; then sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file" sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ deny='"$var_accounts_passwords_pam_faillock_deny"'/' "$pam_file" else sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*\)\('"deny"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file" sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"deny"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file" fi done fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny' ############################################################################### # BEGIN fix (38 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_unlock_time' ############################################################################### (>&2 echo "Remediating rule 38/252: 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_unlock_time'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q pam; }; then var_accounts_passwords_pam_faillock_unlock_time='1800' if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi authselect enable-feature with-faillock authselect apply-changes -b else AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") for pam_file in "${AUTH_FILES[@]}" do if ! grep -qE '^\s*auth\s+required\s+pam_faillock\.so\s+(preauth silent|authfail).*$' "$pam_file" ; then sed -i --follow-symlinks '/^auth.*sufficient.*pam_unix\.so.*/i auth required pam_faillock.so preauth silent' "$pam_file" sed -i --follow-symlinks '/^auth.*required.*pam_deny\.so.*/i auth required pam_faillock.so authfail' "$pam_file" sed -i --follow-symlinks '/^account.*required.*pam_unix\.so.*/i account required pam_faillock.so' "$pam_file" fi sed -Ei 's/(auth.*)(\[default=die\])(.*pam_faillock\.so)/\1required \3/g' "$pam_file" done fi AUTH_FILES=("/etc/pam.d/system-auth" "/etc/pam.d/password-auth") SKIP_FAILLOCK_CHECK=false FAILLOCK_CONF="/etc/security/faillock.conf" if [ -f $FAILLOCK_CONF ] || [ "$SKIP_FAILLOCK_CHECK" = "true" ]; then regex="^\s*unlock_time\s*=" line="unlock_time = $var_accounts_passwords_pam_faillock_unlock_time" if ! grep -q $regex $FAILLOCK_CONF; then echo $line >> $FAILLOCK_CONF else sed -i --follow-symlinks 's|^\s*\(unlock_time\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_unlock_time"'|g' $FAILLOCK_CONF fi for pam_file in "${AUTH_FILES[@]}" do if [ -e "$pam_file" ] ; then PAM_FILE_PATH="$pam_file" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "$pam_file") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if grep -qP "^\s*auth\s.*\bpam_faillock.so\s.*\bunlock_time\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bunlock_time\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "$pam_file was not found" >&2 fi done else for pam_file in "${AUTH_FILES[@]}" do if ! grep -qE '^\s*auth.*pam_faillock\.so\s+(preauth|authfail).*unlock_time' "$pam_file"; then sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file" sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ unlock_time='"$var_accounts_passwords_pam_faillock_unlock_time"'/' "$pam_file" else sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*\)\('"unlock_time"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file" sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"unlock_time"'=\)\S\+\b\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_unlock_time"'\3/' "$pam_file" fi done fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_unlock_time' ############################################################################### # BEGIN fix (39 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_dcredit' ############################################################################### (>&2 echo "Remediating rule 39/252: 'xccdf_org.ssgproject.content_rule_accounts_password_pam_dcredit'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q libpwquality; }; then var_password_pam_dcredit='-1' if grep -sq dcredit /etc/security/pwquality.conf.d/*.conf ; then sed -i "/dcredit/d" /etc/security/pwquality.conf.d/*.conf fi # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^dcredit") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_dcredit" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^dcredit\\>" "/etc/security/pwquality.conf"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^dcredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" else if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf" fi cce="CCE-83566-0" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_dcredit' ############################################################################### # BEGIN fix (40 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_lcredit' ############################################################################### (>&2 echo "Remediating rule 40/252: 'xccdf_org.ssgproject.content_rule_accounts_password_pam_lcredit'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q libpwquality; }; then var_password_pam_lcredit='-1' if grep -sq lcredit /etc/security/pwquality.conf.d/*.conf ; then sed -i "/lcredit/d" /etc/security/pwquality.conf.d/*.conf fi # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^lcredit") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_lcredit" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^lcredit\\>" "/etc/security/pwquality.conf"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^lcredit\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" else if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf" fi cce="CCE-83570-2" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_lcredit' ############################################################################### # BEGIN fix (41 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_minlen' ############################################################################### (>&2 echo "Remediating rule 41/252: 'xccdf_org.ssgproject.content_rule_accounts_password_pam_minlen'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q libpwquality; }; then var_password_pam_minlen='12' if grep -sq minlen /etc/security/pwquality.conf.d/*.conf ; then sed -i "/minlen/d" /etc/security/pwquality.conf.d/*.conf fi # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^minlen") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$var_password_pam_minlen" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^minlen\\>" "/etc/security/pwquality.conf"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^minlen\\>.*/$escaped_formatted_output/gi" "/etc/security/pwquality.conf" else if [[ -s "/etc/security/pwquality.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/security/pwquality.conf" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/security/pwquality.conf" fi cce="CCE-83579-3" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/security/pwquality.conf" >> "/etc/security/pwquality.conf" printf '%s\n' "$formatted_output" >> "/etc/security/pwquality.conf" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_minlen' ############################################################################### # BEGIN fix (42 / 252) for 'xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_libuserconf' ############################################################################### (>&2 echo "Remediating rule 42/252: 'xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_libuserconf'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q libuser; }; then var_password_hashing_algorithm_pam='sha512' LIBUSER_CONF="/etc/libuser.conf" CRYPT_STYLE_REGEX='[[:space:]]*\[defaults](.*(\n)+)+?[[:space:]]*crypt_style[[:space:]]*' # Try find crypt_style in [defaults] section. If it is here, then change algorithm to sha512. # If it isn't here, then add it to [defaults] section. if grep -qzosP $CRYPT_STYLE_REGEX $LIBUSER_CONF ; then sed -i "s/\(crypt_style[[:space:]]*=[[:space:]]*\).*/\1$var_password_hashing_algorithm_pam/g" $LIBUSER_CONF elif grep -qs "\[defaults]" $LIBUSER_CONF ; then sed -i "/[[:space:]]*\[defaults]/a crypt_style = $var_password_hashing_algorithm_pam" $LIBUSER_CONF else echo -e "[defaults]\ncrypt_style = $var_password_hashing_algorithm_pam" >> $LIBUSER_CONF fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_libuserconf' ############################################################################### # BEGIN fix (43 / 252) for 'xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_logindefs' ############################################################################### (>&2 echo "Remediating rule 43/252: 'xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_logindefs'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q shadow-utils; }; then var_password_hashing_algorithm='SHA512' # Allow multiple algorithms, but choose the first one for remediation # var_password_hashing_algorithm="$(echo $var_password_hashing_algorithm | cut -d \| -f 1)" # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^ENCRYPT_METHOD") # shellcheck disable=SC2059 printf -v formatted_output "%s %s" "$stripped_key" "$var_password_hashing_algorithm" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^ENCRYPT_METHOD\\>" "/etc/login.defs"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^ENCRYPT_METHOD\\>.*/$escaped_formatted_output/gi" "/etc/login.defs" else if [[ -s "/etc/login.defs" ]] && [[ -n "$(tail -c 1 -- "/etc/login.defs" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/login.defs" fi cce="CCE-90590-1" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/login.defs" >> "/etc/login.defs" printf '%s\n' "$formatted_output" >> "/etc/login.defs" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_logindefs' ############################################################################### # BEGIN fix (44 / 252) for 'xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_systemauth' ############################################################################### (>&2 echo "Remediating rule 44/252: 'xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_systemauth'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q pam; }; then var_password_hashing_algorithm_pam='sha512' PAM_FILE_PATH="/etc/pam.d/system-auth" # Ensure all the hashing algorithm option is removed. declare -a HASHING_ALGORITHMS_OPTIONS=("sha512" "yescrypt" "gost_yescrypt" "blowfish" "sha256" "md5" "bigcrypt") for hash_option in "${HASHING_ALGORITHMS_OPTIONS[@]}"; do if grep -qP "^\s*password\s+.*\s+pam_unix.so\s+.*\b$hash_option\b" "$PAM_FILE_PATH"; then if [ -e "$PAM_FILE_PATH" ] ; then PAM_FILE_PATH="$PAM_FILE_PATH" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "$PAM_FILE_PATH") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if grep -qP "^\s*password\s+.*\s+pam_unix.so\s.*\b$hash_option\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "s/(.*password.*.*.*pam_unix.so.*)\b$hash_option\b=?[[:alnum:]]*(.*)/\1\2/g" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "$PAM_FILE_PATH was not found" >&2 fi fi done if [ -e "$PAM_FILE_PATH" ] ; then PAM_FILE_PATH="$PAM_FILE_PATH" if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi CURRENT_PROFILE=$(authselect current -r | awk '{ print $1 }') # If not already in use, a custom profile is created preserving the enabled features. if [[ ! $CURRENT_PROFILE == custom/* ]]; then ENABLED_FEATURES=$(authselect current | tail -n+3 | awk '{ print $2 }') # The "local" profile does not contain essential security features required by multiple Benchmarks. # If currently used, it is replaced by "sssd", which is the best option in this case. if [[ $CURRENT_PROFILE == local ]]; then CURRENT_PROFILE="sssd" fi authselect create-profile hardening -b $CURRENT_PROFILE CURRENT_PROFILE="custom/hardening" authselect apply-changes -b --backup=before-hardening-custom-profile authselect select $CURRENT_PROFILE for feature in $ENABLED_FEATURES; do authselect enable-feature $feature; done authselect apply-changes -b --backup=after-hardening-custom-profile fi PAM_FILE_NAME=$(basename "$PAM_FILE_PATH") PAM_FILE_PATH="/etc/authselect/$CURRENT_PROFILE/$PAM_FILE_NAME" authselect apply-changes -b fi if ! grep -qP "^\s*password\s+sufficient\s+pam_unix.so\s*.*" "$PAM_FILE_PATH"; then # Line matching group + control + module was not found. Check group + module. if [ "$(grep -cP '^\s*password\s+.*\s+pam_unix.so\s*' "$PAM_FILE_PATH")" -eq 1 ]; then # The control is updated only if one single line matches. sed -i -E --follow-symlinks "s/^(\s*password\s+).*(\bpam_unix.so.*)/\1sufficient \2/" "$PAM_FILE_PATH" else echo "password sufficient pam_unix.so" >> "$PAM_FILE_PATH" fi fi # Check the option if ! grep -qP "^\s*password\s+sufficient\s+pam_unix.so\s*.*\s$var_password_hashing_algorithm_pam\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "/\s*password\s+sufficient\s+pam_unix.so.*/ s/$/ $var_password_hashing_algorithm_pam/" "$PAM_FILE_PATH" fi if [ -f /usr/bin/authselect ]; then authselect apply-changes -b fi else echo "$PAM_FILE_PATH was not found" >&2 fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_set_password_hashing_algorithm_systemauth' ############################################################################### # BEGIN fix (45 / 252) for 'xccdf_org.ssgproject.content_rule_account_unique_id' ############################################################################### (>&2 echo "Remediating rule 45/252: 'xccdf_org.ssgproject.content_rule_account_unique_id'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_account_unique_id' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_account_unique_id' ############################################################################### # BEGIN fix (46 / 252) for 'xccdf_org.ssgproject.content_rule_group_unique_id' ############################################################################### (>&2 echo "Remediating rule 46/252: 'xccdf_org.ssgproject.content_rule_group_unique_id'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_group_unique_id' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_group_unique_id' ############################################################################### # BEGIN fix (47 / 252) for 'xccdf_org.ssgproject.content_rule_account_disable_post_pw_expiration' ############################################################################### (>&2 echo "Remediating rule 47/252: 'xccdf_org.ssgproject.content_rule_account_disable_post_pw_expiration'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q shadow-utils; }; then var_account_disable_post_pw_expiration='90' # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^INACTIVE") # shellcheck disable=SC2059 printf -v formatted_output "%s=%s" "$stripped_key" "$var_account_disable_post_pw_expiration" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^INACTIVE\\>" "/etc/default/useradd"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^INACTIVE\\>.*/$escaped_formatted_output/gi" "/etc/default/useradd" else if [[ -s "/etc/default/useradd" ]] && [[ -n "$(tail -c 1 -- "/etc/default/useradd" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/default/useradd" fi cce="CCE-83627-0" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/default/useradd" >> "/etc/default/useradd" printf '%s\n' "$formatted_output" >> "/etc/default/useradd" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_account_disable_post_pw_expiration' ############################################################################### # BEGIN fix (48 / 252) for 'xccdf_org.ssgproject.content_rule_account_unique_name' ############################################################################### (>&2 echo "Remediating rule 48/252: 'xccdf_org.ssgproject.content_rule_account_unique_name'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_account_unique_name' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_account_unique_name' ############################################################################### # BEGIN fix (49 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_maximum_age_login_defs' ############################################################################### (>&2 echo "Remediating rule 49/252: 'xccdf_org.ssgproject.content_rule_accounts_maximum_age_login_defs'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q shadow-utils; }; then var_accounts_maximum_age_login_defs='90' # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^PASS_MAX_DAYS") # shellcheck disable=SC2059 printf -v formatted_output "%s %s" "$stripped_key" "$var_accounts_maximum_age_login_defs" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^PASS_MAX_DAYS\\>" "/etc/login.defs"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^PASS_MAX_DAYS\\>.*/$escaped_formatted_output/gi" "/etc/login.defs" else if [[ -s "/etc/login.defs" ]] && [[ -n "$(tail -c 1 -- "/etc/login.defs" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/login.defs" fi cce="CCE-83606-4" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/login.defs" >> "/etc/login.defs" printf '%s\n' "$formatted_output" >> "/etc/login.defs" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_maximum_age_login_defs' ############################################################################### # BEGIN fix (50 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_set_max_life_existing' ############################################################################### (>&2 echo "Remediating rule 50/252: 'xccdf_org.ssgproject.content_rule_accounts_password_set_max_life_existing'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_accounts_maximum_age_login_defs='90' while IFS= read -r i; do chage -M $var_accounts_maximum_age_login_defs $i done < <(awk -v var="$var_accounts_maximum_age_login_defs" -F: '(/^[^:]+:[^!*]/ && ($5 > var || $5 == "")) {print $1}' /etc/shadow) else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_set_max_life_existing' ############################################################################### # BEGIN fix (51 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_set_warn_age_existing' ############################################################################### (>&2 echo "Remediating rule 51/252: 'xccdf_org.ssgproject.content_rule_accounts_password_set_warn_age_existing'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_accounts_password_warn_age_login_defs='7' while IFS= read -r i; do chage --warndays $var_accounts_password_warn_age_login_defs $i done < <(awk -v var="$var_accounts_password_warn_age_login_defs" -F: '(($6 < var || $6 == "") && $2 ~ /^\$/) {print $1}' /etc/shadow) else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_set_warn_age_existing' ############################################################################### # BEGIN fix (52 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_warn_age_login_defs' ############################################################################### (>&2 echo "Remediating rule 52/252: 'xccdf_org.ssgproject.content_rule_accounts_password_warn_age_login_defs'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q shadow-utils; }; then var_accounts_password_warn_age_login_defs='7' # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^PASS_WARN_AGE") # shellcheck disable=SC2059 printf -v formatted_output "%s %s" "$stripped_key" "$var_accounts_password_warn_age_login_defs" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^PASS_WARN_AGE\\>" "/etc/login.defs"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^PASS_WARN_AGE\\>.*/$escaped_formatted_output/gi" "/etc/login.defs" else if [[ -s "/etc/login.defs" ]] && [[ -n "$(tail -c 1 -- "/etc/login.defs" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/login.defs" fi cce="CCE-83609-8" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "/etc/login.defs" >> "/etc/login.defs" printf '%s\n' "$formatted_output" >> "/etc/login.defs" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_warn_age_login_defs' ############################################################################### # BEGIN fix (53 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_set_post_pw_existing' ############################################################################### (>&2 echo "Remediating rule 53/252: 'xccdf_org.ssgproject.content_rule_accounts_set_post_pw_existing'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_account_disable_post_pw_expiration='90' while IFS= read -r i; do chage --inactive $var_account_disable_post_pw_expiration $i done < <(awk -v var="$var_account_disable_post_pw_expiration" -F: '(($7 > var || $7 == "") && $2 ~ /^\$/) {print $1}' /etc/shadow) else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_set_post_pw_existing' ############################################################################### # BEGIN fix (54 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_all_shadowed' ############################################################################### (>&2 echo "Remediating rule 54/252: 'xccdf_org.ssgproject.content_rule_accounts_password_all_shadowed'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_accounts_password_all_shadowed' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_all_shadowed' ############################################################################### # BEGIN fix (55 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_password_last_change_is_in_past' ############################################################################### (>&2 echo "Remediating rule 55/252: 'xccdf_org.ssgproject.content_rule_accounts_password_last_change_is_in_past'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_accounts_password_last_change_is_in_past' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_password_last_change_is_in_past' ############################################################################### # BEGIN fix (56 / 252) for 'xccdf_org.ssgproject.content_rule_gid_passwd_group_same' ############################################################################### (>&2 echo "Remediating rule 56/252: 'xccdf_org.ssgproject.content_rule_gid_passwd_group_same'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_gid_passwd_group_same' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_gid_passwd_group_same' ############################################################################### # BEGIN fix (57 / 252) for 'xccdf_org.ssgproject.content_rule_no_empty_passwords' ############################################################################### (>&2 echo "Remediating rule 57/252: 'xccdf_org.ssgproject.content_rule_no_empty_passwords'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if [ -f /usr/bin/authselect ]; then if ! authselect check; then echo " authselect integrity check failed. Remediation aborted! This remediation could not be applied because an authselect profile was not selected or the selected profile is not intact. It is not recommended to manually edit the PAM files when authselect tool is available. In cases where the default authselect profile does not cover a specific demand, a custom authselect profile is recommended." exit 1 fi authselect enable-feature without-nullok authselect apply-changes -b else if grep -qP "^\s*auth\s+sufficient\s+pam_unix.so\s.*\bnullok\b" "/etc/pam.d/system-auth"; then sed -i -E --follow-symlinks "s/(.*auth.*sufficient.*pam_unix.so.*)\bnullok\b=?[[:alnum:]]*(.*)/\1\2/g" "/etc/pam.d/system-auth" fi if grep -qP "^\s*password\s+sufficient\s+pam_unix.so\s.*\bnullok\b" "/etc/pam.d/system-auth"; then sed -i -E --follow-symlinks "s/(.*password.*sufficient.*pam_unix.so.*)\bnullok\b=?[[:alnum:]]*(.*)/\1\2/g" "/etc/pam.d/system-auth" fi if grep -qP "^\s*auth\s+sufficient\s+pam_unix.so\s.*\bnullok\b" "/etc/pam.d/password-auth"; then sed -i -E --follow-symlinks "s/(.*auth.*sufficient.*pam_unix.so.*)\bnullok\b=?[[:alnum:]]*(.*)/\1\2/g" "/etc/pam.d/password-auth" fi if grep -qP "^\s*password\s+sufficient\s+pam_unix.so\s.*\bnullok\b" "/etc/pam.d/password-auth"; then sed -i -E --follow-symlinks "s/(.*password.*sufficient.*pam_unix.so.*)\bnullok\b=?[[:alnum:]]*(.*)/\1\2/g" "/etc/pam.d/password-auth" fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_no_empty_passwords' ############################################################################### # BEGIN fix (58 / 252) for 'xccdf_org.ssgproject.content_rule_no_empty_passwords_etc_shadow' ############################################################################### (>&2 echo "Remediating rule 58/252: 'xccdf_org.ssgproject.content_rule_no_empty_passwords_etc_shadow'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then readarray -t users_with_empty_pass < <(sudo awk -F: '!$2 {print $1}' /etc/shadow) for user_with_empty_pass in "${users_with_empty_pass[@]}" do passwd -l $user_with_empty_pass done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_no_empty_passwords_etc_shadow' ############################################################################### # BEGIN fix (59 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_no_uid_except_zero' ############################################################################### (>&2 echo "Remediating rule 59/252: 'xccdf_org.ssgproject.content_rule_accounts_no_uid_except_zero'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then awk -F: '$3 == 0 && $1 != "root" { print $1 }' /etc/passwd | xargs --no-run-if-empty --max-lines=1 passwd -l else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_no_uid_except_zero' ############################################################################### # BEGIN fix (60 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_root_gid_zero' ############################################################################### (>&2 echo "Remediating rule 60/252: 'xccdf_org.ssgproject.content_rule_accounts_root_gid_zero'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_accounts_root_gid_zero' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_root_gid_zero' ############################################################################### # BEGIN fix (61 / 252) for 'xccdf_org.ssgproject.content_rule_ensure_pam_wheel_group_empty' ############################################################################### (>&2 echo "Remediating rule 61/252: 'xccdf_org.ssgproject.content_rule_ensure_pam_wheel_group_empty'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_pam_wheel_group_for_su='sugroup' # Workaround for https://github.com/OpenSCAP/openscap/issues/2242: Use full # path to groupadd command to avoid the issue with the command not being found. if ! grep -q "^${var_pam_wheel_group_for_su}:[^:]*:[^:]*:[^:]*" /etc/group; then /usr/sbin/groupadd ${var_pam_wheel_group_for_su} fi # group must be empty gpasswd -M '' ${var_pam_wheel_group_for_su} else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_ensure_pam_wheel_group_empty' ############################################################################### # BEGIN fix (62 / 252) for 'xccdf_org.ssgproject.content_rule_ensure_root_password_configured' ############################################################################### (>&2 echo "Remediating rule 62/252: 'xccdf_org.ssgproject.content_rule_ensure_root_password_configured'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_ensure_root_password_configured' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_ensure_root_password_configured' ############################################################################### # BEGIN fix (63 / 252) for 'xccdf_org.ssgproject.content_rule_no_direct_root_logins' ############################################################################### (>&2 echo "Remediating rule 63/252: 'xccdf_org.ssgproject.content_rule_no_direct_root_logins'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then echo > /etc/securetty else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_no_direct_root_logins' ############################################################################### # BEGIN fix (64 / 252) for 'xccdf_org.ssgproject.content_rule_no_password_auth_for_systemaccounts' ############################################################################### (>&2 echo "Remediating rule 64/252: 'xccdf_org.ssgproject.content_rule_no_password_auth_for_systemaccounts'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then readarray -t systemaccounts < <(awk -F: \ '($3 < 1000 && $3 != root && $3 != halt && $3 != sync && $3 != shutdown \ && $3 != nfsnobody) { print $1 }' /etc/passwd) for systemaccount in "${systemaccounts[@]}"; do usermod -L "$systemaccount" done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_no_password_auth_for_systemaccounts' ############################################################################### # BEGIN fix (65 / 252) for 'xccdf_org.ssgproject.content_rule_no_shelllogin_for_systemaccounts' ############################################################################### (>&2 echo "Remediating rule 65/252: 'xccdf_org.ssgproject.content_rule_no_shelllogin_for_systemaccounts'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then readarray -t systemaccounts < <(awk -F: '($3 < 1000 && $3 != root \ && $7 != "\/sbin\/shutdown" && $7 != "\/sbin\/halt" && $7 != "\/bin\/sync") \ { print $1 }' /etc/passwd) for systemaccount in "${systemaccounts[@]}"; do usermod -s /sbin/nologin "$systemaccount" done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_no_shelllogin_for_systemaccounts' ############################################################################### # BEGIN fix (66 / 252) for 'xccdf_org.ssgproject.content_rule_securetty_root_login_console_only' ############################################################################### (>&2 echo "Remediating rule 66/252: 'xccdf_org.ssgproject.content_rule_securetty_root_login_console_only'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then sed -i '/^vc\/[0-9]/d' /etc/securetty else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_securetty_root_login_console_only' ############################################################################### # BEGIN fix (67 / 252) for 'xccdf_org.ssgproject.content_rule_use_pam_wheel_group_for_su' ############################################################################### (>&2 echo "Remediating rule 67/252: 'xccdf_org.ssgproject.content_rule_use_pam_wheel_group_for_su'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q pam; then var_pam_wheel_group_for_su='sugroup' PAM_CONF=/etc/pam.d/su pamstr=$(grep -P '^auth\s+required\s+pam_wheel\.so\s+(?=[^#]*\buse_uid\b)(?=[^#]*\bgroup=)' ${PAM_CONF}) if [ -z "$pamstr" ]; then sed -Ei '/^auth\b.*\brequired\b.*\bpam_wheel\.so/d' ${PAM_CONF} # remove any remaining uncommented pam_wheel.so line sed -Ei "/^auth\s+sufficient\s+pam_rootok\.so.*$/a auth required pam_wheel.so use_uid group=${var_pam_wheel_group_for_su}" ${PAM_CONF} else group_val=$(echo -n "$pamstr" | grep -Eo '\bgroup=[_a-z][-0-9_a-z]*' | cut -d '=' -f 2) if [ -z "${group_val}" ] || [ ${group_val} != ${var_pam_wheel_group_for_su} ]; then sed -Ei "s/(^auth\s+required\s+pam_wheel.so\s+[^#]*group=)[_a-z][-0-9_a-z]*/\1${var_pam_wheel_group_for_su}/" ${PAM_CONF} fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_use_pam_wheel_group_for_su' ############################################################################### # BEGIN fix (68 / 252) for 'xccdf_org.ssgproject.content_rule_accounts_tmout' ############################################################################### (>&2 echo "Remediating rule 68/252: 'xccdf_org.ssgproject.content_rule_accounts_tmout'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_accounts_tmout='600' # if 0, no occurrence of tmout found, if 1, occurrence found tmout_found=0 for f in /etc/profile /etc/profile.d/*.sh; do if grep --silent '^[^#].*TMOUT' $f; then sed -i -E "s/^(.*)TMOUT\s*=\s*(\w|\$)*(.*)$/typeset -xr TMOUT=$var_accounts_tmout\3/g" $f tmout_found=1 fi done if [ $tmout_found -eq 0 ]; then echo -e "\n# Set TMOUT to $var_accounts_tmout per security requirements" >> /etc/profile.d/tmout.sh echo "typeset -xr TMOUT=$var_accounts_tmout" >> /etc/profile.d/tmout.sh fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_accounts_tmout' ############################################################################### # BEGIN fix (69 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_grub2_cfg' ############################################################################### (>&2 echo "Remediating rule 69/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_grub2_cfg'"); ( # Remediation is applicable only in certain platforms if ( rpm --quiet -q grub2-common && rpm --quiet -q kernel-core ) && { ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ); }; then newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/boot/grub2/grub.cfg" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /boot/grub2/grub.cfg fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_grub2_cfg' ############################################################################### # BEGIN fix (70 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_user_cfg' ############################################################################### (>&2 echo "Remediating rule 70/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_user_cfg'"); ( # Remediation is applicable only in certain platforms if ( rpm --quiet -q grub2-common && rpm --quiet -q kernel-core ) && { ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ); }; then newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/boot/grub2/user.cfg" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /boot/grub2/user.cfg fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_user_cfg' ############################################################################### # BEGIN fix (71 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_grub2_cfg' ############################################################################### (>&2 echo "Remediating rule 71/252: 'xccdf_org.ssgproject.content_rule_file_owner_grub2_cfg'"); ( # Remediation is applicable only in certain platforms if ( rpm --quiet -q grub2-common && rpm --quiet -q kernel-core ) && { ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ); }; then newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/boot/grub2/grub.cfg" | grep -E -w -q "0"; then chown --no-dereference "$newown" /boot/grub2/grub.cfg fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_grub2_cfg' ############################################################################### # BEGIN fix (72 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_user_cfg' ############################################################################### (>&2 echo "Remediating rule 72/252: 'xccdf_org.ssgproject.content_rule_file_owner_user_cfg'"); ( # Remediation is applicable only in certain platforms if ( rpm --quiet -q grub2-common && rpm --quiet -q kernel-core ) && { ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ); }; then newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/boot/grub2/user.cfg" | grep -E -w -q "0"; then chown --no-dereference "$newown" /boot/grub2/user.cfg fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_user_cfg' ############################################################################### # BEGIN fix (73 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_grub2_cfg' ############################################################################### (>&2 echo "Remediating rule 73/252: 'xccdf_org.ssgproject.content_rule_file_permissions_grub2_cfg'"); ( # Remediation is applicable only in certain platforms if ( rpm --quiet -q grub2-common && rpm --quiet -q kernel-core ) && { ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ); }; then chmod u-xs,g-xwrs,o-xwrt /boot/grub2/grub.cfg else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_grub2_cfg' ############################################################################### # BEGIN fix (74 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_user_cfg' ############################################################################### (>&2 echo "Remediating rule 74/252: 'xccdf_org.ssgproject.content_rule_file_permissions_user_cfg'"); ( # Remediation is applicable only in certain platforms if ( rpm --quiet -q grub2-common && rpm --quiet -q kernel-core ) && { ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ); }; then chmod u-xs,g-xwrs,o-xwrt /boot/grub2/user.cfg else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_user_cfg' ############################################################################### # BEGIN fix (75 / 252) for 'xccdf_org.ssgproject.content_rule_rsyslog_files_groupownership' ############################################################################### (>&2 echo "Remediating rule 75/252: 'xccdf_org.ssgproject.content_rule_rsyslog_files_groupownership'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && rpm --quiet -q rsyslog; then # List of log file paths to be inspected for correct permissions # * Primarily inspect log file paths listed in /etc/rsyslog.conf RSYSLOG_ETC_CONFIG="/etc/rsyslog.conf" # * And also the log file paths listed after rsyslog's $IncludeConfig directive # (store the result into array for the case there's shell glob used as value of IncludeConfig) readarray -t OLD_INC < <(grep -e "\$IncludeConfig[[:space:]]\+[^[:space:];]\+" /etc/rsyslog.conf | cut -d ' ' -f 2) readarray -t RSYSLOG_INCLUDE_CONFIG < <(for INCPATH in "${OLD_INC[@]}"; do eval printf '%s\\n' "${INCPATH}"; done) readarray -t NEW_INC < <(sed -n '/^\s*include(/,/)/Ip' /etc/rsyslog.conf | sed -n 's@.*file\s*=\s*"\([/[:alnum:][:punct:]]*\)".*@\1@Ip') readarray -t RSYSLOG_INCLUDE < <(for INCPATH in "${NEW_INC[@]}"; do eval printf '%s\\n' "${INCPATH}"; done) # Declare an array to hold the final list of different log file paths declare -a LOG_FILE_PATHS # Array to hold all rsyslog config entries RSYSLOG_CONFIGS=() RSYSLOG_CONFIGS=("${RSYSLOG_ETC_CONFIG}" "${RSYSLOG_INCLUDE_CONFIG[@]}" "${RSYSLOG_INCLUDE[@]}") # Get full list of files to be checked # RSYSLOG_CONFIGS may contain globs such as # /etc/rsyslog.d/*.conf /etc/rsyslog.d/*.frule # So, loop over the entries in RSYSLOG_CONFIGS and use find to get the list of included files. RSYSLOG_CONFIG_FILES=() for ENTRY in "${RSYSLOG_CONFIGS[@]}" do # If directory, rsyslog will search for config files in recursively. # However, files in hidden sub-directories or hidden files will be ignored. if [ -d "${ENTRY}" ] then readarray -t FINDOUT < <(find "${ENTRY}" -not -path '*/.*' -type f) RSYSLOG_CONFIG_FILES+=("${FINDOUT[@]}") elif [ -f "${ENTRY}" ] then RSYSLOG_CONFIG_FILES+=("${ENTRY}") else echo "Invalid include object: ${ENTRY}" fi done # Browse each file selected above as containing paths of log files # ('/etc/rsyslog.conf' and '/etc/rsyslog.d/*.conf' in the default configuration) for LOG_FILE in "${RSYSLOG_CONFIG_FILES[@]}" do # From each of these files extract just particular log file path(s), thus: # * Ignore lines starting with space (' '), comment ('#"), or variable syntax ('$') characters, # * Ignore empty lines, # * Strip quotes and closing brackets from paths. # * Ignore paths that match /dev|/etc.*\.conf, as those are paths, but likely not log files # * From the remaining valid rows select only fields constituting a log file path # Text file column is understood to represent a log file path if and only if all of the # following are met: # * it contains at least one slash '/' character, # * it is preceded by space # * it doesn't contain space (' '), colon (':'), and semicolon (';') characters # Search log file for path(s) only in case it exists! if [[ -f "${LOG_FILE}" ]] then NORMALIZED_CONFIG_FILE_LINES=$(sed -e "/^[#|$]/d" "${LOG_FILE}") LINES_WITH_PATHS=$(grep '[^/]*\s\+\S*/\S\+$' <<< "${NORMALIZED_CONFIG_FILE_LINES}") FILTERED_PATHS=$(awk '{if(NF>=2&&($NF~/^\//||$NF~/^-\//)){sub(/^-\//,"/",$NF);print $NF}}' <<< "${LINES_WITH_PATHS}") CLEANED_PATHS=$(sed -e "s/[\"')]//g; /\\/etc.*\.conf/d; /\\/dev\\//d" <<< "${FILTERED_PATHS}") MATCHED_ITEMS=$(sed -e "/^$/d" <<< "${CLEANED_PATHS}") # Since above sed command might return more than one item (delimited by newline), split # the particular matches entries into new array specific for this log file readarray -t ARRAY_FOR_LOG_FILE <<< "$MATCHED_ITEMS" # Concatenate the two arrays - previous content of $LOG_FILE_PATHS array with # items from newly created array for this log file LOG_FILE_PATHS+=("${ARRAY_FOR_LOG_FILE[@]}") # Delete the temporary array unset ARRAY_FOR_LOG_FILE fi done # Check for RainerScript action log format which might be also multiline so grep regex is a bit # curly: # extract possibly multiline action omfile expressions # extract File="logfile" expression # match only "logfile" expression for LOG_FILE in "${RSYSLOG_CONFIG_FILES[@]}" do ACTION_OMFILE_LINES=$(grep -iozP "action\s*\(\s*type\s*=\s*\"omfile\"[^\)]*\)" "${LOG_FILE}") OMFILE_LINES=$(echo "${ACTION_OMFILE_LINES}"| grep -iaoP "\bFile\s*=\s*\"([/[:alnum:][:punct:]]*)\"\s*\)") LOG_FILE_PATHS+=("$(echo "${OMFILE_LINES}"| grep -oE "\"([/[:alnum:][:punct:]]*)\""|tr -d "\"")") done # Ensure the correct attribute if file exists FILE_CMD="chgrp" for LOG_FILE_PATH in "${LOG_FILE_PATHS[@]}" do # Sanity check - if particular $LOG_FILE_PATH is empty string, skip it from further processing if [ -z "$LOG_FILE_PATH" ] then continue fi $FILE_CMD "root" "$LOG_FILE_PATH" done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_rsyslog_files_groupownership' ############################################################################### # BEGIN fix (76 / 252) for 'xccdf_org.ssgproject.content_rule_rsyslog_files_ownership' ############################################################################### (>&2 echo "Remediating rule 76/252: 'xccdf_org.ssgproject.content_rule_rsyslog_files_ownership'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && rpm --quiet -q rsyslog; then # List of log file paths to be inspected for correct permissions # * Primarily inspect log file paths listed in /etc/rsyslog.conf RSYSLOG_ETC_CONFIG="/etc/rsyslog.conf" # * And also the log file paths listed after rsyslog's $IncludeConfig directive # (store the result into array for the case there's shell glob used as value of IncludeConfig) readarray -t OLD_INC < <(grep -e "\$IncludeConfig[[:space:]]\+[^[:space:];]\+" /etc/rsyslog.conf | cut -d ' ' -f 2) readarray -t RSYSLOG_INCLUDE_CONFIG < <(for INCPATH in "${OLD_INC[@]}"; do eval printf '%s\\n' "${INCPATH}"; done) readarray -t NEW_INC < <(sed -n '/^\s*include(/,/)/Ip' /etc/rsyslog.conf | sed -n 's@.*file\s*=\s*"\([/[:alnum:][:punct:]]*\)".*@\1@Ip') readarray -t RSYSLOG_INCLUDE < <(for INCPATH in "${NEW_INC[@]}"; do eval printf '%s\\n' "${INCPATH}"; done) # Declare an array to hold the final list of different log file paths declare -a LOG_FILE_PATHS # Array to hold all rsyslog config entries RSYSLOG_CONFIGS=() RSYSLOG_CONFIGS=("${RSYSLOG_ETC_CONFIG}" "${RSYSLOG_INCLUDE_CONFIG[@]}" "${RSYSLOG_INCLUDE[@]}") # Get full list of files to be checked # RSYSLOG_CONFIGS may contain globs such as # /etc/rsyslog.d/*.conf /etc/rsyslog.d/*.frule # So, loop over the entries in RSYSLOG_CONFIGS and use find to get the list of included files. RSYSLOG_CONFIG_FILES=() for ENTRY in "${RSYSLOG_CONFIGS[@]}" do # If directory, rsyslog will search for config files in recursively. # However, files in hidden sub-directories or hidden files will be ignored. if [ -d "${ENTRY}" ] then readarray -t FINDOUT < <(find "${ENTRY}" -not -path '*/.*' -type f) RSYSLOG_CONFIG_FILES+=("${FINDOUT[@]}") elif [ -f "${ENTRY}" ] then RSYSLOG_CONFIG_FILES+=("${ENTRY}") else echo "Invalid include object: ${ENTRY}" fi done # Browse each file selected above as containing paths of log files # ('/etc/rsyslog.conf' and '/etc/rsyslog.d/*.conf' in the default configuration) for LOG_FILE in "${RSYSLOG_CONFIG_FILES[@]}" do # From each of these files extract just particular log file path(s), thus: # * Ignore lines starting with space (' '), comment ('#"), or variable syntax ('$') characters, # * Ignore empty lines, # * Strip quotes and closing brackets from paths. # * Ignore paths that match /dev|/etc.*\.conf, as those are paths, but likely not log files # * From the remaining valid rows select only fields constituting a log file path # Text file column is understood to represent a log file path if and only if all of the # following are met: # * it contains at least one slash '/' character, # * it is preceded by space # * it doesn't contain space (' '), colon (':'), and semicolon (';') characters # Search log file for path(s) only in case it exists! if [[ -f "${LOG_FILE}" ]] then NORMALIZED_CONFIG_FILE_LINES=$(sed -e "/^[#|$]/d" "${LOG_FILE}") LINES_WITH_PATHS=$(grep '[^/]*\s\+\S*/\S\+$' <<< "${NORMALIZED_CONFIG_FILE_LINES}") FILTERED_PATHS=$(awk '{if(NF>=2&&($NF~/^\//||$NF~/^-\//)){sub(/^-\//,"/",$NF);print $NF}}' <<< "${LINES_WITH_PATHS}") CLEANED_PATHS=$(sed -e "s/[\"')]//g; /\\/etc.*\.conf/d; /\\/dev\\//d" <<< "${FILTERED_PATHS}") MATCHED_ITEMS=$(sed -e "/^$/d" <<< "${CLEANED_PATHS}") # Since above sed command might return more than one item (delimited by newline), split # the particular matches entries into new array specific for this log file readarray -t ARRAY_FOR_LOG_FILE <<< "$MATCHED_ITEMS" # Concatenate the two arrays - previous content of $LOG_FILE_PATHS array with # items from newly created array for this log file LOG_FILE_PATHS+=("${ARRAY_FOR_LOG_FILE[@]}") # Delete the temporary array unset ARRAY_FOR_LOG_FILE fi done # Check for RainerScript action log format which might be also multiline so grep regex is a bit # curly: # extract possibly multiline action omfile expressions # extract File="logfile" expression # match only "logfile" expression for LOG_FILE in "${RSYSLOG_CONFIG_FILES[@]}" do ACTION_OMFILE_LINES=$(grep -iozP "action\s*\(\s*type\s*=\s*\"omfile\"[^\)]*\)" "${LOG_FILE}") OMFILE_LINES=$(echo "${ACTION_OMFILE_LINES}"| grep -iaoP "\bFile\s*=\s*\"([/[:alnum:][:punct:]]*)\"\s*\)") LOG_FILE_PATHS+=("$(echo "${OMFILE_LINES}"| grep -oE "\"([/[:alnum:][:punct:]]*)\""|tr -d "\"")") done # Ensure the correct attribute if file exists FILE_CMD="chown" for LOG_FILE_PATH in "${LOG_FILE_PATHS[@]}" do # Sanity check - if particular $LOG_FILE_PATH is empty string, skip it from further processing if [ -z "$LOG_FILE_PATH" ] then continue fi $FILE_CMD "root" "$LOG_FILE_PATH" done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_rsyslog_files_ownership' ############################################################################### # BEGIN fix (77 / 252) for 'xccdf_org.ssgproject.content_rule_rsyslog_files_permissions' ############################################################################### (>&2 echo "Remediating rule 77/252: 'xccdf_org.ssgproject.content_rule_rsyslog_files_permissions'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && rpm --quiet -q rsyslog; then # List of log file paths to be inspected for correct permissions # * Primarily inspect log file paths listed in /etc/rsyslog.conf RSYSLOG_ETC_CONFIG="/etc/rsyslog.conf" # * And also the log file paths listed after rsyslog's $IncludeConfig directive # (store the result into array for the case there's shell glob used as value of IncludeConfig) readarray -t OLD_INC < <(grep -e "\$IncludeConfig[[:space:]]\+[^[:space:];]\+" /etc/rsyslog.conf | cut -d ' ' -f 2) readarray -t RSYSLOG_INCLUDE_CONFIG < <(for INCPATH in "${OLD_INC[@]}"; do eval printf '%s\\n' "${INCPATH}"; done) readarray -t NEW_INC < <(sed -n '/^\s*include(/,/)/Ip' /etc/rsyslog.conf | sed -n 's@.*file\s*=\s*"\([/[:alnum:][:punct:]]*\)".*@\1@Ip') readarray -t RSYSLOG_INCLUDE < <(for INCPATH in "${NEW_INC[@]}"; do eval printf '%s\\n' "${INCPATH}"; done) # Declare an array to hold the final list of different log file paths declare -a LOG_FILE_PATHS # Array to hold all rsyslog config entries RSYSLOG_CONFIGS=() RSYSLOG_CONFIGS=("${RSYSLOG_ETC_CONFIG}" "${RSYSLOG_INCLUDE_CONFIG[@]}" "${RSYSLOG_INCLUDE[@]}") # Get full list of files to be checked # RSYSLOG_CONFIGS may contain globs such as # /etc/rsyslog.d/*.conf /etc/rsyslog.d/*.frule # So, loop over the entries in RSYSLOG_CONFIGS and use find to get the list of included files. RSYSLOG_CONFIG_FILES=() for ENTRY in "${RSYSLOG_CONFIGS[@]}" do # If directory, rsyslog will search for config files in recursively. # However, files in hidden sub-directories or hidden files will be ignored. if [ -d "${ENTRY}" ] then readarray -t FINDOUT < <(find "${ENTRY}" -not -path '*/.*' -type f) RSYSLOG_CONFIG_FILES+=("${FINDOUT[@]}") elif [ -f "${ENTRY}" ] then RSYSLOG_CONFIG_FILES+=("${ENTRY}") else echo "Invalid include object: ${ENTRY}" fi done # Browse each file selected above as containing paths of log files # ('/etc/rsyslog.conf' and '/etc/rsyslog.d/*.conf' in the default configuration) for LOG_FILE in "${RSYSLOG_CONFIG_FILES[@]}" do # From each of these files extract just particular log file path(s), thus: # * Ignore lines starting with space (' '), comment ('#"), or variable syntax ('$') characters, # * Ignore empty lines, # * Strip quotes and closing brackets from paths. # * Ignore paths that match /dev|/etc.*\.conf, as those are paths, but likely not log files # * From the remaining valid rows select only fields constituting a log file path # Text file column is understood to represent a log file path if and only if all of the # following are met: # * it contains at least one slash '/' character, # * it is preceded by space # * it doesn't contain space (' '), colon (':'), and semicolon (';') characters # Search log file for path(s) only in case it exists! if [[ -f "${LOG_FILE}" ]] then NORMALIZED_CONFIG_FILE_LINES=$(sed -e "/^[#|$]/d" "${LOG_FILE}") LINES_WITH_PATHS=$(grep '[^/]*\s\+\S*/\S\+$' <<< "${NORMALIZED_CONFIG_FILE_LINES}") FILTERED_PATHS=$(awk '{if(NF>=2&&($NF~/^\//||$NF~/^-\//)){sub(/^-\//,"/",$NF);print $NF}}' <<< "${LINES_WITH_PATHS}") CLEANED_PATHS=$(sed -e "s/[\"')]//g; /\\/etc.*\.conf/d; /\\/dev\\//d" <<< "${FILTERED_PATHS}") MATCHED_ITEMS=$(sed -e "/^$/d" <<< "${CLEANED_PATHS}") # Since above sed command might return more than one item (delimited by newline), split # the particular matches entries into new array specific for this log file readarray -t ARRAY_FOR_LOG_FILE <<< "$MATCHED_ITEMS" # Concatenate the two arrays - previous content of $LOG_FILE_PATHS array with # items from newly created array for this log file LOG_FILE_PATHS+=("${ARRAY_FOR_LOG_FILE[@]}") # Delete the temporary array unset ARRAY_FOR_LOG_FILE fi done # Check for RainerScript action log format which might be also multiline so grep regex is a bit # curly: # extract possibly multiline action omfile expressions # extract File="logfile" expression # match only "logfile" expression for LOG_FILE in "${RSYSLOG_CONFIG_FILES[@]}" do ACTION_OMFILE_LINES=$(grep -iozP "action\s*\(\s*type\s*=\s*\"omfile\"[^\)]*\)" "${LOG_FILE}") OMFILE_LINES=$(echo "${ACTION_OMFILE_LINES}"| grep -iaoP "\bFile\s*=\s*\"([/[:alnum:][:punct:]]*)\"\s*\)") LOG_FILE_PATHS+=("$(echo "${OMFILE_LINES}"| grep -oE "\"([/[:alnum:][:punct:]]*)\""|tr -d "\"")") done # Ensure the correct attribute if file exists FILE_CMD="chmod" for LOG_FILE_PATH in "${LOG_FILE_PATHS[@]}" do # Sanity check - if particular $LOG_FILE_PATH is empty string, skip it from further processing if [ -z "$LOG_FILE_PATH" ] then continue fi $FILE_CMD "0640" "$LOG_FILE_PATH" done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_rsyslog_files_permissions' ############################################################################### # BEGIN fix (78 / 252) for 'xccdf_org.ssgproject.content_rule_package_logrotate_installed' ############################################################################### (>&2 echo "Remediating rule 78/252: 'xccdf_org.ssgproject.content_rule_package_logrotate_installed'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "logrotate" ; then dnf install -y "logrotate" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_logrotate_installed' ############################################################################### # BEGIN fix (79 / 252) for 'xccdf_org.ssgproject.content_rule_timer_logrotate_enabled' ############################################################################### (>&2 echo "Remediating rule 79/252: 'xccdf_org.ssgproject.content_rule_timer_logrotate_enabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { ( grep -qP "^ID=[\"']?rhel[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="9"; printf "%s\n%s" "$expected" "$real" | sort -VC; } && rpm --quiet -q logrotate ); }; then SYSTEMCTL_EXEC='/usr/bin/systemctl' if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" start 'logrotate.timer' fi "$SYSTEMCTL_EXEC" enable 'logrotate.timer' else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_timer_logrotate_enabled' ############################################################################### # BEGIN fix (80 / 252) for 'xccdf_org.ssgproject.content_rule_network_nmcli_permissions' ############################################################################### (>&2 echo "Remediating rule 80/252: 'xccdf_org.ssgproject.content_rule_network_nmcli_permissions'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q polkit; then if ! rpm -q --quiet "polkit-pkla-compat" ; then dnf install -y "polkit-pkla-compat" fi printf "[Disable General User Access to NetworkManager]\nIdentity=default\nAction=org.freedesktop.NetworkManager.*\nResultAny=no\nResultInactive=no\nResultActive=auth_admin\n" > /etc/polkit-1/localauthority/20-org.d/10-nm-harden-access.pkla else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_network_nmcli_permissions' ############################################################################### # BEGIN fix (81 / 252) for 'xccdf_org.ssgproject.content_rule_network_sniffer_disabled' ############################################################################### (>&2 echo "Remediating rule 81/252: 'xccdf_org.ssgproject.content_rule_network_sniffer_disabled'"); ( # Remediation is applicable only in certain platforms if [ ! -f /.dockerenv ] && [ ! -f /run/.containerenv ]; then for interface in $(ip -o link show | cut -d ":" -f 2); do ip link set dev $interface multicast off promisc off done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_network_sniffer_disabled' ############################################################################### # BEGIN fix (82 / 252) for 'xccdf_org.ssgproject.content_rule_package_firewalld_installed' ############################################################################### (>&2 echo "Remediating rule 82/252: 'xccdf_org.ssgproject.content_rule_package_firewalld_installed'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "firewalld" ; then dnf install -y "firewalld" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_firewalld_installed' ############################################################################### # BEGIN fix (83 / 252) for 'xccdf_org.ssgproject.content_rule_service_firewalld_enabled' ############################################################################### (>&2 echo "Remediating rule 83/252: 'xccdf_org.ssgproject.content_rule_service_firewalld_enabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q firewalld; }; then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" unmask 'firewalld.service' if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" start 'firewalld.service' fi "$SYSTEMCTL_EXEC" enable 'firewalld.service' else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_service_firewalld_enabled' ############################################################################### # BEGIN fix (84 / 252) for 'xccdf_org.ssgproject.content_rule_configure_firewalld_ports' ############################################################################### (>&2 echo "Remediating rule 84/252: 'xccdf_org.ssgproject.content_rule_configure_firewalld_ports'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_configure_firewalld_ports' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_configure_firewalld_ports' ############################################################################### # BEGIN fix (85 / 252) for 'xccdf_org.ssgproject.content_rule_firewalld_loopback_traffic_restricted' ############################################################################### (>&2 echo "Remediating rule 85/252: 'xccdf_org.ssgproject.content_rule_firewalld_loopback_traffic_restricted'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "firewalld" ; then dnf install -y "firewalld" fi ipv4_rule='rule family=ipv4 source address="127.0.0.1" destination not address="127.0.0.1" drop' ipv6_rule='rule family=ipv6 source address="::1" destination not address="::1" drop' if test "$(stat -c %d:%i /)" != "$(stat -c %d:%i /proc/1/root/.)" || { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; }; then firewall-offline-cmd --zone=trusted --add-rich-rule="${ipv4_rule}" firewall-offline-cmd --zone=trusted --add-rich-rule="${ipv6_rule}" elif systemctl is-active firewalld; then firewall-cmd --permanent --zone=trusted --add-rich-rule="${ipv4_rule}" firewall-cmd --permanent --zone=trusted --add-rich-rule="${ipv6_rule}" firewall-cmd --reload else echo " firewalld service is not active. Remediation aborted! This remediation could not be applied because it depends on firewalld service running. The service is not started by this remediation in order to prevent connection issues." fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_firewalld_loopback_traffic_restricted' ############################################################################### # BEGIN fix (86 / 252) for 'xccdf_org.ssgproject.content_rule_firewalld_loopback_traffic_trusted' ############################################################################### (>&2 echo "Remediating rule 86/252: 'xccdf_org.ssgproject.content_rule_firewalld_loopback_traffic_trusted'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "firewalld" ; then dnf install -y "firewalld" fi if test "$(stat -c %d:%i /)" != "$(stat -c %d:%i /proc/1/root/.)" || { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; }; then firewall-offline-cmd --zone=trusted --add-interface=lo elif systemctl is-active firewalld; then firewall-cmd --permanent --zone=trusted --add-interface=lo firewall-cmd --reload else echo " firewalld service is not active. Remediation aborted! This remediation could not be applied because it depends on firewalld service running. The service is not started by this remediation in order to prevent connection issues." fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_firewalld_loopback_traffic_trusted' ############################################################################### # BEGIN fix (87 / 252) for 'xccdf_org.ssgproject.content_rule_set_firewalld_default_zone' ############################################################################### (>&2 echo "Remediating rule 87/252: 'xccdf_org.ssgproject.content_rule_set_firewalld_default_zone'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_set_firewalld_default_zone' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_set_firewalld_default_zone' ############################################################################### # BEGIN fix (88 / 252) for 'xccdf_org.ssgproject.content_rule_set_ip6tables_default_rule' ############################################################################### (>&2 echo "Remediating rule 88/252: 'xccdf_org.ssgproject.content_rule_set_ip6tables_default_rule'"); ( # Remediation is applicable only in certain platforms if ( ! ( rpm --quiet -q nftables ) && ! ( rpm --quiet -q ufw ) && rpm --quiet -q iptables ); then sed -i 's/^:INPUT ACCEPT.*/:INPUT DROP [0:0]/g' /etc/sysconfig/ip6tables else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_set_ip6tables_default_rule' ############################################################################### # BEGIN fix (89 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_default_accept_source_route' ############################################################################### (>&2 echo "Remediating rule 89/252: 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_default_accept_source_route'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of net.ipv6.conf.default.accept_source_route from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*net.ipv6.conf.default.accept_source_route.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.ipv6.conf.default.accept_source_route" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" sysctl_net_ipv6_conf_default_accept_source_route_value='0' # # Set runtime for net.ipv6.conf.default.accept_source_route # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w net.ipv6.conf.default.accept_source_route="$sysctl_net_ipv6_conf_default_accept_source_route_value" fi # # If net.ipv6.conf.default.accept_source_route present in /etc/sysctl.conf, change value to appropriate value # else, add "net.ipv6.conf.default.accept_source_route = value" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv6.conf.default.accept_source_route") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv6_conf_default_accept_source_route_value" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^net.ipv6.conf.default.accept_source_route\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.ipv6.conf.default.accept_source_route\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-84130-4" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv6_conf_default_accept_source_route' ############################################################################### # BEGIN fix (90 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_rp_filter' ############################################################################### (>&2 echo "Remediating rule 90/252: 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_rp_filter'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of net.ipv4.conf.all.rp_filter from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.rp_filter.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.ipv4.conf.all.rp_filter" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" sysctl_net_ipv4_conf_all_rp_filter_value='1' # # Set runtime for net.ipv4.conf.all.rp_filter # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w net.ipv4.conf.all.rp_filter="$sysctl_net_ipv4_conf_all_rp_filter_value" fi # # If net.ipv4.conf.all.rp_filter present in /etc/sysctl.conf, change value to appropriate value # else, add "net.ipv4.conf.all.rp_filter = value" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.rp_filter") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_rp_filter_value" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.rp_filter\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.all.rp_filter\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-84008-2" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_rp_filter' ############################################################################### # BEGIN fix (91 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_secure_redirects' ############################################################################### (>&2 echo "Remediating rule 91/252: 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_secure_redirects'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of net.ipv4.conf.all.secure_redirects from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.secure_redirects.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.ipv4.conf.all.secure_redirects" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" sysctl_net_ipv4_conf_all_secure_redirects_value='0' # # Set runtime for net.ipv4.conf.all.secure_redirects # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w net.ipv4.conf.all.secure_redirects="$sysctl_net_ipv4_conf_all_secure_redirects_value" fi # # If net.ipv4.conf.all.secure_redirects present in /etc/sysctl.conf, change value to appropriate value # else, add "net.ipv4.conf.all.secure_redirects = value" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.secure_redirects") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_all_secure_redirects_value" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.secure_redirects\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.all.secure_redirects\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-84016-5" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_secure_redirects' ############################################################################### # BEGIN fix (92 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_accept_redirects' ############################################################################### (>&2 echo "Remediating rule 92/252: 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_accept_redirects'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of net.ipv4.conf.default.accept_redirects from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.accept_redirects.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.ipv4.conf.default.accept_redirects" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" sysctl_net_ipv4_conf_default_accept_redirects_value='0' # # Set runtime for net.ipv4.conf.default.accept_redirects # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w net.ipv4.conf.default.accept_redirects="$sysctl_net_ipv4_conf_default_accept_redirects_value" fi # # If net.ipv4.conf.default.accept_redirects present in /etc/sysctl.conf, change value to appropriate value # else, add "net.ipv4.conf.default.accept_redirects = value" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.accept_redirects") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_conf_default_accept_redirects_value" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.accept_redirects\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.default.accept_redirects\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-84003-3" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_accept_redirects' ############################################################################### # BEGIN fix (93 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_icmp_echo_ignore_broadcasts' ############################################################################### (>&2 echo "Remediating rule 93/252: 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_icmp_echo_ignore_broadcasts'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of net.ipv4.icmp_echo_ignore_broadcasts from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.icmp_echo_ignore_broadcasts.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.ipv4.icmp_echo_ignore_broadcasts" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value='1' # # Set runtime for net.ipv4.icmp_echo_ignore_broadcasts # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w net.ipv4.icmp_echo_ignore_broadcasts="$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value" fi # # If net.ipv4.icmp_echo_ignore_broadcasts present in /etc/sysctl.conf, change value to appropriate value # else, add "net.ipv4.icmp_echo_ignore_broadcasts = value" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.icmp_echo_ignore_broadcasts") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_icmp_echo_ignore_broadcasts_value" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.icmp_echo_ignore_broadcasts\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.icmp_echo_ignore_broadcasts\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-84004-1" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_icmp_echo_ignore_broadcasts' ############################################################################### # BEGIN fix (94 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_icmp_ignore_bogus_error_responses' ############################################################################### (>&2 echo "Remediating rule 94/252: 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_icmp_ignore_bogus_error_responses'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of net.ipv4.icmp_ignore_bogus_error_responses from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.icmp_ignore_bogus_error_responses.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.ipv4.icmp_ignore_bogus_error_responses" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value='1' # # Set runtime for net.ipv4.icmp_ignore_bogus_error_responses # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w net.ipv4.icmp_ignore_bogus_error_responses="$sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value" fi # # If net.ipv4.icmp_ignore_bogus_error_responses present in /etc/sysctl.conf, change value to appropriate value # else, add "net.ipv4.icmp_ignore_bogus_error_responses = value" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.icmp_ignore_bogus_error_responses") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_icmp_ignore_bogus_error_responses_value" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.icmp_ignore_bogus_error_responses\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.icmp_ignore_bogus_error_responses\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-84015-7" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_icmp_ignore_bogus_error_responses' ############################################################################### # BEGIN fix (95 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_tcp_syncookies' ############################################################################### (>&2 echo "Remediating rule 95/252: 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_tcp_syncookies'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of net.ipv4.tcp_syncookies from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.tcp_syncookies.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.ipv4.tcp_syncookies" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" sysctl_net_ipv4_tcp_syncookies_value='1' # # Set runtime for net.ipv4.tcp_syncookies # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w net.ipv4.tcp_syncookies="$sysctl_net_ipv4_tcp_syncookies_value" fi # # If net.ipv4.tcp_syncookies present in /etc/sysctl.conf, change value to appropriate value # else, add "net.ipv4.tcp_syncookies = value" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.tcp_syncookies") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_net_ipv4_tcp_syncookies_value" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.tcp_syncookies\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.tcp_syncookies\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-84006-6" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_tcp_syncookies' ############################################################################### # BEGIN fix (96 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_send_redirects' ############################################################################### (>&2 echo "Remediating rule 96/252: 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_send_redirects'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of net.ipv4.conf.all.send_redirects from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.all.send_redirects.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.ipv4.conf.all.send_redirects" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" # # Set runtime for net.ipv4.conf.all.send_redirects # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w net.ipv4.conf.all.send_redirects="0" fi # # If net.ipv4.conf.all.send_redirects present in /etc/sysctl.conf, change value to "0" # else, add "net.ipv4.conf.all.send_redirects = 0" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.all.send_redirects") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "0" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.all.send_redirects\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.all.send_redirects\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-83997-7" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_all_send_redirects' ############################################################################### # BEGIN fix (97 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_send_redirects' ############################################################################### (>&2 echo "Remediating rule 97/252: 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_send_redirects'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of net.ipv4.conf.default.send_redirects from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.conf.default.send_redirects.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.ipv4.conf.default.send_redirects" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" # # Set runtime for net.ipv4.conf.default.send_redirects # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w net.ipv4.conf.default.send_redirects="0" fi # # If net.ipv4.conf.default.send_redirects present in /etc/sysctl.conf, change value to "0" # else, add "net.ipv4.conf.default.send_redirects = 0" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.conf.default.send_redirects") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "0" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.conf.default.send_redirects\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.conf.default.send_redirects\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-83999-3" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_conf_default_send_redirects' ############################################################################### # BEGIN fix (98 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_ip_forward' ############################################################################### (>&2 echo "Remediating rule 98/252: 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_ip_forward'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of net.ipv4.ip_forward from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*net.ipv4.ip_forward.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.ipv4.ip_forward" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" # # Set runtime for net.ipv4.ip_forward # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w net.ipv4.ip_forward="0" fi # # If net.ipv4.ip_forward present in /etc/sysctl.conf, change value to "0" # else, add "net.ipv4.ip_forward = 0" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^net.ipv4.ip_forward") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "0" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^net.ipv4.ip_forward\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.ipv4.ip_forward\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-83998-5" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_net_ipv4_ip_forward' ############################################################################### # BEGIN fix (99 / 252) for 'xccdf_org.ssgproject.content_rule_package_nftables_installed' ############################################################################### (>&2 echo "Remediating rule 99/252: 'xccdf_org.ssgproject.content_rule_package_nftables_installed'"); ( # Remediation is applicable only in certain platforms if ( ! (systemctl is-active iptables &>/dev/null) && ! (systemctl is-active ufw &>/dev/null) && rpm --quiet -q kernel-core ); then if ! rpm -q --quiet "nftables" ; then dnf install -y "nftables" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_nftables_installed' ############################################################################### # BEGIN fix (100 / 252) for 'xccdf_org.ssgproject.content_rule_service_nftables_disabled' ############################################################################### (>&2 echo "Remediating rule 100/252: 'xccdf_org.ssgproject.content_rule_service_nftables_disabled'"); ( # Remediation is applicable only in certain platforms if ( rpm --quiet -q firewalld && rpm --quiet -q nftables && rpm --quiet -q kernel-core ); then SYSTEMCTL_EXEC='/usr/bin/systemctl' if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" stop 'nftables.service' fi "$SYSTEMCTL_EXEC" disable 'nftables.service' "$SYSTEMCTL_EXEC" mask 'nftables.service' # Disable socket activation if we have a unit file for it if "$SYSTEMCTL_EXEC" -q list-unit-files nftables.socket; then if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" stop 'nftables.socket' fi "$SYSTEMCTL_EXEC" mask 'nftables.socket' fi # The service may not be running because it has been started and failed, # so let's reset the state so OVAL checks pass. # Service should be 'inactive', not 'failed' after reboot though. "$SYSTEMCTL_EXEC" reset-failed 'nftables.service' || true else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_service_nftables_disabled' ############################################################################### # BEGIN fix (101 / 252) for 'xccdf_org.ssgproject.content_rule_kernel_module_dccp_disabled' ############################################################################### (>&2 echo "Remediating rule 101/252: 'xccdf_org.ssgproject.content_rule_kernel_module_dccp_disabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if LC_ALL=C grep -q -m 1 "^install dccp" /etc/modprobe.d/dccp.conf ; then sed -i 's#^install dccp.*#install dccp /bin/false#g' /etc/modprobe.d/dccp.conf else echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/dccp.conf echo "install dccp /bin/false" >> /etc/modprobe.d/dccp.conf fi if ! LC_ALL=C grep -q -m 1 "^blacklist dccp$" /etc/modprobe.d/dccp.conf ; then echo "blacklist dccp" >> /etc/modprobe.d/dccp.conf fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_kernel_module_dccp_disabled' ############################################################################### # BEGIN fix (102 / 252) for 'xccdf_org.ssgproject.content_rule_kernel_module_sctp_disabled' ############################################################################### (>&2 echo "Remediating rule 102/252: 'xccdf_org.ssgproject.content_rule_kernel_module_sctp_disabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if LC_ALL=C grep -q -m 1 "^install sctp" /etc/modprobe.d/sctp.conf ; then sed -i 's#^install sctp.*#install sctp /bin/false#g' /etc/modprobe.d/sctp.conf else echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/sctp.conf echo "install sctp /bin/false" >> /etc/modprobe.d/sctp.conf fi if ! LC_ALL=C grep -q -m 1 "^blacklist sctp$" /etc/modprobe.d/sctp.conf ; then echo "blacklist sctp" >> /etc/modprobe.d/sctp.conf fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_kernel_module_sctp_disabled' ############################################################################### # BEGIN fix (103 / 252) for 'xccdf_org.ssgproject.content_rule_wireless_disable_interfaces' ############################################################################### (>&2 echo "Remediating rule 103/252: 'xccdf_org.ssgproject.content_rule_wireless_disable_interfaces'"); ( # Remediation is applicable only in certain platforms if ( ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ); then if ! rpm -q --quiet "NetworkManager" ; then dnf install -y "NetworkManager" fi if command -v nmcli >/dev/null 2>&1 && systemctl is-active NetworkManager >/dev/null 2>&1; then nmcli radio all off fi if command -v wicked >/dev/null 2>&1 && systemctl is-active wickedd >/dev/null 2>&1; then if [ -n "$(find /sys/class/net/*/ -type d -name wireless)" ]; then interfaces=$(find /sys/class/net/*/wireless -type d -name wireless | xargs -0 dirname | xargs basename) for iface in $interfaces; do wicked ifdown $iface sed -i 's/STARTMODE=.*/STARTMODE=off/' /etc/sysconfig/network/ifcfg-$iface done fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_wireless_disable_interfaces' ############################################################################### # BEGIN fix (104 / 252) for 'xccdf_org.ssgproject.content_rule_dir_perms_world_writable_sticky_bits' ############################################################################### (>&2 echo "Remediating rule 104/252: 'xccdf_org.ssgproject.content_rule_dir_perms_world_writable_sticky_bits'"); ( df --local -P | awk '{if (NR!=1) print $6}' \ | xargs -I '$6' find '$6' -xdev -type d \ \( -perm -0002 -a ! -perm -1000 \) 2>/dev/null \ -exec chmod a+t {} + ) # END fix for 'xccdf_org.ssgproject.content_rule_dir_perms_world_writable_sticky_bits' ############################################################################### # BEGIN fix (105 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_world_writable' ############################################################################### (>&2 echo "Remediating rule 105/252: 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_world_writable'"); ( FILTER_NODEV=$(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) # Do not consider /sysroot partition because it contains only the physical # read-only root on bootable containers. PARTITIONS=$(findmnt -n -l -k -it $FILTER_NODEV | awk '{ print $1 }' | grep -v "/sysroot") for PARTITION in $PARTITIONS; do find "${PARTITION}" -xdev -type f -perm -002 -exec chmod o-w {} \; 2>/dev/null done # Ensure /tmp is also fixed when tmpfs is used. if grep "^tmpfs /tmp" /proc/mounts; then find /tmp -xdev -type f -perm -002 -exec chmod o-w {} \; 2>/dev/null fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_world_writable' ############################################################################### # BEGIN fix (106 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_ungroupowned' ############################################################################### (>&2 echo "Remediating rule 106/252: 'xccdf_org.ssgproject.content_rule_file_permissions_ungroupowned'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_file_permissions_ungroupowned' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_ungroupowned' ############################################################################### # BEGIN fix (107 / 252) for 'xccdf_org.ssgproject.content_rule_no_files_unowned_by_user' ############################################################################### (>&2 echo "Remediating rule 107/252: 'xccdf_org.ssgproject.content_rule_no_files_unowned_by_user'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_no_files_unowned_by_user' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_no_files_unowned_by_user' ############################################################################### # BEGIN fix (108 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_backup_etc_group' ############################################################################### (>&2 echo "Remediating rule 108/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_backup_etc_group'"); ( newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/etc/group-" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /etc/group- fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_backup_etc_group' ############################################################################### # BEGIN fix (109 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_backup_etc_passwd' ############################################################################### (>&2 echo "Remediating rule 109/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_backup_etc_passwd'"); ( newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/etc/passwd-" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /etc/passwd- fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_backup_etc_passwd' ############################################################################### # BEGIN fix (110 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_backup_etc_shadow' ############################################################################### (>&2 echo "Remediating rule 110/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_backup_etc_shadow'"); ( newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/etc/shadow-" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /etc/shadow- fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_backup_etc_shadow' ############################################################################### # BEGIN fix (111 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_group' ############################################################################### (>&2 echo "Remediating rule 111/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_group'"); ( newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/etc/group" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /etc/group fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_group' ############################################################################### # BEGIN fix (112 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_passwd' ############################################################################### (>&2 echo "Remediating rule 112/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_passwd'"); ( newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/etc/passwd" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /etc/passwd fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_passwd' ############################################################################### # BEGIN fix (113 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_shadow' ############################################################################### (>&2 echo "Remediating rule 113/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_shadow'"); ( newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/etc/shadow" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /etc/shadow fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_etc_shadow' ############################################################################### # BEGIN fix (114 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_backup_etc_group' ############################################################################### (>&2 echo "Remediating rule 114/252: 'xccdf_org.ssgproject.content_rule_file_owner_backup_etc_group'"); ( newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/etc/group-" | grep -E -w -q "0"; then chown --no-dereference "$newown" /etc/group- fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_backup_etc_group' ############################################################################### # BEGIN fix (115 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_backup_etc_passwd' ############################################################################### (>&2 echo "Remediating rule 115/252: 'xccdf_org.ssgproject.content_rule_file_owner_backup_etc_passwd'"); ( newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/etc/passwd-" | grep -E -w -q "0"; then chown --no-dereference "$newown" /etc/passwd- fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_backup_etc_passwd' ############################################################################### # BEGIN fix (116 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_backup_etc_shadow' ############################################################################### (>&2 echo "Remediating rule 116/252: 'xccdf_org.ssgproject.content_rule_file_owner_backup_etc_shadow'"); ( newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/etc/shadow-" | grep -E -w -q "0"; then chown --no-dereference "$newown" /etc/shadow- fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_backup_etc_shadow' ############################################################################### # BEGIN fix (117 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_etc_group' ############################################################################### (>&2 echo "Remediating rule 117/252: 'xccdf_org.ssgproject.content_rule_file_owner_etc_group'"); ( newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/etc/group" | grep -E -w -q "0"; then chown --no-dereference "$newown" /etc/group fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_etc_group' ############################################################################### # BEGIN fix (118 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_etc_passwd' ############################################################################### (>&2 echo "Remediating rule 118/252: 'xccdf_org.ssgproject.content_rule_file_owner_etc_passwd'"); ( newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/etc/passwd" | grep -E -w -q "0"; then chown --no-dereference "$newown" /etc/passwd fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_etc_passwd' ############################################################################### # BEGIN fix (119 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_etc_shadow' ############################################################################### (>&2 echo "Remediating rule 119/252: 'xccdf_org.ssgproject.content_rule_file_owner_etc_shadow'"); ( newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/etc/shadow" | grep -E -w -q "0"; then chown --no-dereference "$newown" /etc/shadow fi fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_etc_shadow' ############################################################################### # BEGIN fix (120 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_backup_etc_group' ############################################################################### (>&2 echo "Remediating rule 120/252: 'xccdf_org.ssgproject.content_rule_file_permissions_backup_etc_group'"); ( chmod u-xs,g-xws,o-xwt /etc/group- ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_backup_etc_group' ############################################################################### # BEGIN fix (121 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_backup_etc_passwd' ############################################################################### (>&2 echo "Remediating rule 121/252: 'xccdf_org.ssgproject.content_rule_file_permissions_backup_etc_passwd'"); ( chmod u-xs,g-xws,o-xwt /etc/passwd- ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_backup_etc_passwd' ############################################################################### # BEGIN fix (122 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_backup_etc_shadow' ############################################################################### (>&2 echo "Remediating rule 122/252: 'xccdf_org.ssgproject.content_rule_file_permissions_backup_etc_shadow'"); ( chmod u-xwrs,g-xwrs,o-xwrt /etc/shadow- ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_backup_etc_shadow' ############################################################################### # BEGIN fix (123 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_etc_group' ############################################################################### (>&2 echo "Remediating rule 123/252: 'xccdf_org.ssgproject.content_rule_file_permissions_etc_group'"); ( chmod u-xs,g-xws,o-xwt /etc/group ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_etc_group' ############################################################################### # BEGIN fix (124 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_etc_passwd' ############################################################################### (>&2 echo "Remediating rule 124/252: 'xccdf_org.ssgproject.content_rule_file_permissions_etc_passwd'"); ( chmod u-xs,g-xws,o-xwt /etc/passwd ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_etc_passwd' ############################################################################### # BEGIN fix (125 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_etc_shadow' ############################################################################### (>&2 echo "Remediating rule 125/252: 'xccdf_org.ssgproject.content_rule_file_permissions_etc_shadow'"); ( chmod u-xwrs,g-xwrs,o-xwrt /etc/shadow ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_etc_shadow' ############################################################################### # BEGIN fix (126 / 252) for 'xccdf_org.ssgproject.content_rule_kernel_module_usb-storage_disabled' ############################################################################### (>&2 echo "Remediating rule 126/252: 'xccdf_org.ssgproject.content_rule_kernel_module_usb-storage_disabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if LC_ALL=C grep -q -m 1 "^install usb-storage" /etc/modprobe.d/usb-storage.conf ; then sed -i 's#^install usb-storage.*#install usb-storage /bin/false#g' /etc/modprobe.d/usb-storage.conf else echo -e "\n# Disable per security requirements" >> /etc/modprobe.d/usb-storage.conf echo "install usb-storage /bin/false" >> /etc/modprobe.d/usb-storage.conf fi if ! LC_ALL=C grep -q -m 1 "^blacklist usb-storage$" /etc/modprobe.d/usb-storage.conf ; then echo "blacklist usb-storage" >> /etc/modprobe.d/usb-storage.conf fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_kernel_module_usb-storage_disabled' ############################################################################### # BEGIN fix (127 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_core_pattern' ############################################################################### (>&2 echo "Remediating rule 127/252: 'xccdf_org.ssgproject.content_rule_sysctl_kernel_core_pattern'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of kernel.core_pattern from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*kernel.core_pattern.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "kernel.core_pattern" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" # # Set runtime for kernel.core_pattern # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w kernel.core_pattern="|/bin/false" fi # # If kernel.core_pattern present in /etc/sysctl.conf, change value to "|/bin/false" # else, add "kernel.core_pattern = |/bin/false" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.core_pattern") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "|/bin/false" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^kernel.core_pattern\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^kernel.core_pattern\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-83961-3" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_core_pattern' ############################################################################### # BEGIN fix (128 / 252) for 'xccdf_org.ssgproject.content_rule_coredump_disable_backtraces' ############################################################################### (>&2 echo "Remediating rule 128/252: 'xccdf_org.ssgproject.content_rule_coredump_disable_backtraces'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q systemd; }; then found=false # set value in all files if they contain section or key for f in $(echo -n "/etc/systemd/coredump.conf.d/complianceascode_hardening.conf /etc/systemd/coredump.conf.d/*.conf /etc/systemd/coredump.conf"); do if [ ! -e "$f" ]; then continue fi # find key in section and change value if grep -qzosP "[[:space:]]*\[Coredump\]([^\n\[]*\n+)+?[[:space:]]*ProcessSizeMax" "$f"; then if ! grep -qPz "ProcessSizeMax=0" "$f"; then sed -i "s/ProcessSizeMax[^(\n)]*/ProcessSizeMax=0/" "$f" fi found=true # find section and add key = value to it elif grep -qs "[[:space:]]*\[Coredump\]" "$f"; then sed -i "/[[:space:]]*\[Coredump\]/a ProcessSizeMax=0" "$f" found=true fi done # if section not in any file, append section with key = value to FIRST file in files parameter if ! $found ; then file=$(echo "/etc/systemd/coredump.conf.d/complianceascode_hardening.conf /etc/systemd/coredump.conf.d/*.conf /etc/systemd/coredump.conf" | cut -f1 -d ' ') mkdir -p "$(dirname "$file")" echo -e "[Coredump]\nProcessSizeMax=0" >> "$file" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_coredump_disable_backtraces' ############################################################################### # BEGIN fix (129 / 252) for 'xccdf_org.ssgproject.content_rule_coredump_disable_storage' ############################################################################### (>&2 echo "Remediating rule 129/252: 'xccdf_org.ssgproject.content_rule_coredump_disable_storage'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q systemd; }; then found=false # set value in all files if they contain section or key for f in $(echo -n "/etc/systemd/coredump.conf.d/complianceascode_hardening.conf /etc/systemd/coredump.conf.d/*.conf /etc/systemd/coredump.conf"); do if [ ! -e "$f" ]; then continue fi # find key in section and change value if grep -qzosP "[[:space:]]*\[Coredump\]([^\n\[]*\n+)+?[[:space:]]*Storage" "$f"; then if ! grep -qPz "Storage=none" "$f"; then sed -i "s/Storage[^(\n)]*/Storage=none/" "$f" fi found=true # find section and add key = value to it elif grep -qs "[[:space:]]*\[Coredump\]" "$f"; then sed -i "/[[:space:]]*\[Coredump\]/a Storage=none" "$f" found=true fi done # if section not in any file, append section with key = value to FIRST file in files parameter if ! $found ; then file=$(echo "/etc/systemd/coredump.conf.d/complianceascode_hardening.conf /etc/systemd/coredump.conf.d/*.conf /etc/systemd/coredump.conf" | cut -f1 -d ' ') mkdir -p "$(dirname "$file")" echo -e "[Coredump]\nStorage=none" >> "$file" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_coredump_disable_storage' ############################################################################### # BEGIN fix (130 / 252) for 'xccdf_org.ssgproject.content_rule_disable_users_coredumps' ############################################################################### (>&2 echo "Remediating rule 130/252: 'xccdf_org.ssgproject.content_rule_disable_users_coredumps'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q pam; }; then SECURITY_LIMITS_FILE="/etc/security/limits.conf" DROPIN_DIR="/etc/security/limits.d" DROPIN_FILE="$DROPIN_DIR/10-ssg-hardening.conf" REGEX_CORRECT_VALUE="^\s*\*\s+hard\s+core\s+0\s*$" # Remove bad configuration in drop-ins if [ -d "$DROPIN_DIR" ]; then for override in "$DROPIN_DIR"/*.conf; do if [ -f "$override" ] && ! grep -qE "$REGEX_CORRECT_VALUE" "$override"; then sed -ir -E '/^[[:space:]]*\*[[:space:]]+hard[[:space:]]+core[[:space:]]+/ s/^/#/' "$override" fi done fi if [ -d "$DROPIN_DIR" ] && grep -qEr "$REGEX_CORRECT_VALUE" "$DROPIN_DIR"; then exit 0 elif [ ! -d "$DROPIN_DIR" ] && grep -qE "$REGEX_CORRECT_VALUE" "$SECURITY_LIMITS_FILE"; then exit 0 else mkdir -p "$DROPIN_DIR" echo "* hard core 0" >> $DROPIN_FILE fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_disable_users_coredumps' ############################################################################### # BEGIN fix (131 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_fs_suid_dumpable' ############################################################################### (>&2 echo "Remediating rule 131/252: 'xccdf_org.ssgproject.content_rule_sysctl_fs_suid_dumpable'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of fs.suid_dumpable from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*fs.suid_dumpable.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "fs.suid_dumpable" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" # # Set runtime for fs.suid_dumpable # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w fs.suid_dumpable="0" fi # # If fs.suid_dumpable present in /etc/sysctl.conf, change value to "0" # else, add "fs.suid_dumpable = 0" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^fs.suid_dumpable") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "0" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^fs.suid_dumpable\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^fs.suid_dumpable\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-83981-1" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_fs_suid_dumpable' ############################################################################### # BEGIN fix (132 / 252) for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_randomize_va_space' ############################################################################### (>&2 echo "Remediating rule 132/252: 'xccdf_org.ssgproject.content_rule_sysctl_kernel_randomize_va_space'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then # Comment out any occurrences of kernel.randomize_va_space from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf; do # skip systemd-sysctl symlink (/etc/sysctl.d/99-sysctl.conf -> /etc/sysctl.conf) if [[ "$(readlink -f "$f")" == "/etc/sysctl.conf" ]]; then continue; fi matching_list=$(grep -P '^(?!#).*[\s]*kernel.randomize_va_space.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "kernel.randomize_va_space" matches to preserve user data sed -i --follow-symlinks "s/^${escaped_entry}$/# &/g" $f done <<< "$matching_list" fi done # # Set sysctl config file which to save the desired value # SYSCONFIG_FILE="/etc/sysctl.conf" # # Set runtime for kernel.randomize_va_space # if ! { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then /sbin/sysctl -q -n -w kernel.randomize_va_space="2" fi # # If kernel.randomize_va_space present in /etc/sysctl.conf, change value to "2" # else, add "kernel.randomize_va_space = 2" to /etc/sysctl.conf # # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^kernel.randomize_va_space") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "2" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^kernel.randomize_va_space\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^kernel.randomize_va_space\\>.*/$escaped_formatted_output/gi" "${SYSCONFIG_FILE}" else if [[ -s "${SYSCONFIG_FILE}" ]] && [[ -n "$(tail -c 1 -- "${SYSCONFIG_FILE}" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "${SYSCONFIG_FILE}" fi cce="CCE-83971-2" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "${SYSCONFIG_FILE}" >> "${SYSCONFIG_FILE}" printf '%s\n' "$formatted_output" >> "${SYSCONFIG_FILE}" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_randomize_va_space' ############################################################################### # BEGIN fix (133 / 252) for 'xccdf_org.ssgproject.content_rule_bios_enable_execution_restrictions' ############################################################################### (>&2 echo "Remediating rule 133/252: 'xccdf_org.ssgproject.content_rule_bios_enable_execution_restrictions'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_bios_enable_execution_restrictions' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_bios_enable_execution_restrictions' ############################################################################### # BEGIN fix (134 / 252) for 'xccdf_org.ssgproject.content_rule_package_libselinux_installed' ############################################################################### (>&2 echo "Remediating rule 134/252: 'xccdf_org.ssgproject.content_rule_package_libselinux_installed'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "libselinux" ; then dnf install -y "libselinux" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_libselinux_installed' ############################################################################### # BEGIN fix (135 / 252) for 'xccdf_org.ssgproject.content_rule_grub2_enable_selinux' ############################################################################### (>&2 echo "Remediating rule 135/252: 'xccdf_org.ssgproject.content_rule_grub2_enable_selinux'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q grub2-common; }; then sed -i --follow-symlinks "s/selinux=0//gI" /etc/default/grub /etc/grub2.cfg /etc/grub.d/* sed -i --follow-symlinks "s/enforcing=0//gI" /etc/default/grub /etc/grub2.cfg /etc/grub.d/* else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_grub2_enable_selinux' ############################################################################### # BEGIN fix (136 / 252) for 'xccdf_org.ssgproject.content_rule_selinux_confinement_of_daemons' ############################################################################### (>&2 echo "Remediating rule 136/252: 'xccdf_org.ssgproject.content_rule_selinux_confinement_of_daemons'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_selinux_confinement_of_daemons' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_selinux_confinement_of_daemons' ############################################################################### # BEGIN fix (137 / 252) for 'xccdf_org.ssgproject.content_rule_selinux_policytype' ############################################################################### (>&2 echo "Remediating rule 137/252: 'xccdf_org.ssgproject.content_rule_selinux_policytype'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_selinux_policy_name='targeted' if [ -e "/etc/selinux/config" ] ; then LC_ALL=C sed -i "/^SELINUXTYPE=/Id" "/etc/selinux/config" else touch "/etc/selinux/config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/selinux/config" cp "/etc/selinux/config" "/etc/selinux/config.bak" # Insert at the end of the file printf '%s\n' "SELINUXTYPE=$var_selinux_policy_name" >> "/etc/selinux/config" # Clean up after ourselves. rm "/etc/selinux/config.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_selinux_policytype' ############################################################################### # BEGIN fix (138 / 252) for 'xccdf_org.ssgproject.content_rule_selinux_state' ############################################################################### (>&2 echo "Remediating rule 138/252: 'xccdf_org.ssgproject.content_rule_selinux_state'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_selinux_state='enforcing' if [ -e "/etc/selinux/config" ] ; then LC_ALL=C sed -i "/^SELINUX=/Id" "/etc/selinux/config" else touch "/etc/selinux/config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/selinux/config" cp "/etc/selinux/config" "/etc/selinux/config.bak" # Insert at the end of the file printf '%s\n' "SELINUX=$var_selinux_state" >> "/etc/selinux/config" # Clean up after ourselves. rm "/etc/selinux/config.bak" fixfiles onboot else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_selinux_state' ############################################################################### # BEGIN fix (139 / 252) for 'xccdf_org.ssgproject.content_rule_service_avahi-daemon_disabled' ############################################################################### (>&2 echo "Remediating rule 139/252: 'xccdf_org.ssgproject.content_rule_service_avahi-daemon_disabled'"); ( # Remediation is applicable only in certain platforms if ( rpm --quiet -q avahi && rpm --quiet -q kernel-core ); then SYSTEMCTL_EXEC='/usr/bin/systemctl' if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" stop 'avahi-daemon.service' fi "$SYSTEMCTL_EXEC" disable 'avahi-daemon.service' "$SYSTEMCTL_EXEC" mask 'avahi-daemon.service' # Disable socket activation if we have a unit file for it if "$SYSTEMCTL_EXEC" -q list-unit-files avahi-daemon.socket; then if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" stop 'avahi-daemon.socket' fi "$SYSTEMCTL_EXEC" mask 'avahi-daemon.socket' fi # The service may not be running because it has been started and failed, # so let's reset the state so OVAL checks pass. # Service should be 'inactive', not 'failed' after reboot though. "$SYSTEMCTL_EXEC" reset-failed 'avahi-daemon.service' || true else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_service_avahi-daemon_disabled' ############################################################################### # BEGIN fix (140 / 252) for 'xccdf_org.ssgproject.content_rule_package_cron_installed' ############################################################################### (>&2 echo "Remediating rule 140/252: 'xccdf_org.ssgproject.content_rule_package_cron_installed'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "cronie" ; then dnf install -y "cronie" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_cron_installed' ############################################################################### # BEGIN fix (141 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_d' ############################################################################### (>&2 echo "Remediating rule 141/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_d'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else find -P /etc/cron.d/ -maxdepth 0 -type d ! -group 0 -exec chgrp --no-dereference "$newgroup" {} \; fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_d' ############################################################################### # BEGIN fix (142 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_daily' ############################################################################### (>&2 echo "Remediating rule 142/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_daily'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else find -P /etc/cron.daily/ -maxdepth 0 -type d ! -group 0 -exec chgrp --no-dereference "$newgroup" {} \; fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_daily' ############################################################################### # BEGIN fix (143 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_hourly' ############################################################################### (>&2 echo "Remediating rule 143/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_hourly'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else find -P /etc/cron.hourly/ -maxdepth 0 -type d ! -group 0 -exec chgrp --no-dereference "$newgroup" {} \; fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_hourly' ############################################################################### # BEGIN fix (144 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_monthly' ############################################################################### (>&2 echo "Remediating rule 144/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_monthly'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else find -P /etc/cron.monthly/ -maxdepth 0 -type d ! -group 0 -exec chgrp --no-dereference "$newgroup" {} \; fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_monthly' ############################################################################### # BEGIN fix (145 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_weekly' ############################################################################### (>&2 echo "Remediating rule 145/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_weekly'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else find -P /etc/cron.weekly/ -maxdepth 0 -type d ! -group 0 -exec chgrp --no-dereference "$newgroup" {} \; fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_weekly' ############################################################################### # BEGIN fix (146 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_crontab' ############################################################################### (>&2 echo "Remediating rule 146/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_crontab'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/etc/crontab" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /etc/crontab fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_crontab' ############################################################################### # BEGIN fix (147 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_cron_d' ############################################################################### (>&2 echo "Remediating rule 147/252: 'xccdf_org.ssgproject.content_rule_file_owner_cron_d'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else find -P /etc/cron.d/ -maxdepth 0 -type d ! -user 0 -exec chown --no-dereference "$newown" {} \; fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_cron_d' ############################################################################### # BEGIN fix (148 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_cron_daily' ############################################################################### (>&2 echo "Remediating rule 148/252: 'xccdf_org.ssgproject.content_rule_file_owner_cron_daily'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else find -P /etc/cron.daily/ -maxdepth 0 -type d ! -user 0 -exec chown --no-dereference "$newown" {} \; fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_cron_daily' ############################################################################### # BEGIN fix (149 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_cron_hourly' ############################################################################### (>&2 echo "Remediating rule 149/252: 'xccdf_org.ssgproject.content_rule_file_owner_cron_hourly'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else find -P /etc/cron.hourly/ -maxdepth 0 -type d ! -user 0 -exec chown --no-dereference "$newown" {} \; fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_cron_hourly' ############################################################################### # BEGIN fix (150 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_cron_monthly' ############################################################################### (>&2 echo "Remediating rule 150/252: 'xccdf_org.ssgproject.content_rule_file_owner_cron_monthly'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else find -P /etc/cron.monthly/ -maxdepth 0 -type d ! -user 0 -exec chown --no-dereference "$newown" {} \; fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_cron_monthly' ############################################################################### # BEGIN fix (151 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_cron_weekly' ############################################################################### (>&2 echo "Remediating rule 151/252: 'xccdf_org.ssgproject.content_rule_file_owner_cron_weekly'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else find -P /etc/cron.weekly/ -maxdepth 0 -type d ! -user 0 -exec chown --no-dereference "$newown" {} \; fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_cron_weekly' ############################################################################### # BEGIN fix (152 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_crontab' ############################################################################### (>&2 echo "Remediating rule 152/252: 'xccdf_org.ssgproject.content_rule_file_owner_crontab'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/etc/crontab" | grep -E -w -q "0"; then chown --no-dereference "$newown" /etc/crontab fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_crontab' ############################################################################### # BEGIN fix (153 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_d' ############################################################################### (>&2 echo "Remediating rule 153/252: 'xccdf_org.ssgproject.content_rule_file_permissions_cron_d'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then find -H /etc/cron.d/ -maxdepth 0 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_d' ############################################################################### # BEGIN fix (154 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_daily' ############################################################################### (>&2 echo "Remediating rule 154/252: 'xccdf_org.ssgproject.content_rule_file_permissions_cron_daily'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then find -H /etc/cron.daily/ -maxdepth 0 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_daily' ############################################################################### # BEGIN fix (155 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_hourly' ############################################################################### (>&2 echo "Remediating rule 155/252: 'xccdf_org.ssgproject.content_rule_file_permissions_cron_hourly'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then find -H /etc/cron.hourly/ -maxdepth 0 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_hourly' ############################################################################### # BEGIN fix (156 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_monthly' ############################################################################### (>&2 echo "Remediating rule 156/252: 'xccdf_org.ssgproject.content_rule_file_permissions_cron_monthly'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then find -H /etc/cron.monthly/ -maxdepth 0 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_monthly' ############################################################################### # BEGIN fix (157 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_weekly' ############################################################################### (>&2 echo "Remediating rule 157/252: 'xccdf_org.ssgproject.content_rule_file_permissions_cron_weekly'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then find -H /etc/cron.weekly/ -maxdepth 0 -perm /u+s,g+xwrs,o+xwrt -type d -exec chmod u-s,g-xwrs,o-xwrt {} \; else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_weekly' ############################################################################### # BEGIN fix (158 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_crontab' ############################################################################### (>&2 echo "Remediating rule 158/252: 'xccdf_org.ssgproject.content_rule_file_permissions_crontab'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then chmod u-xs,g-xwrs,o-xwrt /etc/crontab else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_crontab' ############################################################################### # BEGIN fix (159 / 252) for 'xccdf_org.ssgproject.content_rule_file_at_deny_not_exist' ############################################################################### (>&2 echo "Remediating rule 159/252: 'xccdf_org.ssgproject.content_rule_file_at_deny_not_exist'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if [[ -f /etc/at.deny ]]; then rm /etc/at.deny fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_at_deny_not_exist' ############################################################################### # BEGIN fix (160 / 252) for 'xccdf_org.ssgproject.content_rule_file_cron_deny_not_exist' ############################################################################### (>&2 echo "Remediating rule 160/252: 'xccdf_org.ssgproject.content_rule_file_cron_deny_not_exist'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if [[ -f /etc/cron.deny ]]; then rm /etc/cron.deny fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_cron_deny_not_exist' ############################################################################### # BEGIN fix (161 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_at_allow' ############################################################################### (>&2 echo "Remediating rule 161/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_at_allow'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/etc/at.allow" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /etc/at.allow fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_at_allow' ############################################################################### # BEGIN fix (162 / 252) for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_allow' ############################################################################### (>&2 echo "Remediating rule 162/252: 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_allow'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newgroup="" if getent group "0" >/dev/null 2>&1; then newgroup="0" fi if [[ -z "${newgroup}" ]]; then >&2 echo "0 is not a defined group on the system" else if ! stat -c "%g %G" "/etc/cron.allow" | grep -E -w -q "0"; then chgrp --no-dereference "$newgroup" /etc/cron.allow fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_groupowner_cron_allow' ############################################################################### # BEGIN fix (163 / 252) for 'xccdf_org.ssgproject.content_rule_file_owner_cron_allow' ############################################################################### (>&2 echo "Remediating rule 163/252: 'xccdf_org.ssgproject.content_rule_file_owner_cron_allow'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then newown="" if id "0" >/dev/null 2>&1; then newown="0" fi if [[ -z "$newown" ]]; then >&2 echo "0 is not a defined user on the system" else if ! stat -c "%u %U" "/etc/cron.allow" | grep -E -w -q "0"; then chown --no-dereference "$newown" /etc/cron.allow fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_owner_cron_allow' ############################################################################### # BEGIN fix (164 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_at_allow' ############################################################################### (>&2 echo "Remediating rule 164/252: 'xccdf_org.ssgproject.content_rule_file_permissions_at_allow'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then chmod u-xs,g-xws,o-xwrt /etc/at.allow else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_at_allow' ############################################################################### # BEGIN fix (165 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_allow' ############################################################################### (>&2 echo "Remediating rule 165/252: 'xccdf_org.ssgproject.content_rule_file_permissions_cron_allow'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then chmod u-xs,g-xwrs,o-xwrt /etc/cron.allow else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_cron_allow' ############################################################################### # BEGIN fix (166 / 252) for 'xccdf_org.ssgproject.content_rule_package_dhcp_removed' ############################################################################### (>&2 echo "Remediating rule 166/252: 'xccdf_org.ssgproject.content_rule_package_dhcp_removed'"); ( # CAUTION: This remediation script will remove dhcp-server # from the system, and may remove any packages # that depend on dhcp-server. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "dhcp-server" ; then dnf remove -y --noautoremove "dhcp-server" fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_dhcp_removed' ############################################################################### # BEGIN fix (167 / 252) for 'xccdf_org.ssgproject.content_rule_package_ftp_removed' ############################################################################### (>&2 echo "Remediating rule 167/252: 'xccdf_org.ssgproject.content_rule_package_ftp_removed'"); ( # CAUTION: This remediation script will remove ftp # from the system, and may remove any packages # that depend on ftp. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "ftp" ; then dnf remove -y --noautoremove "ftp" fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_ftp_removed' ############################################################################### # BEGIN fix (168 / 252) for 'xccdf_org.ssgproject.content_rule_postfix_network_listening_disabled' ############################################################################### (>&2 echo "Remediating rule 168/252: 'xccdf_org.ssgproject.content_rule_postfix_network_listening_disabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q postfix; }; then var_postfix_inet_interfaces='loopback-only' if [ -e "/etc/postfix/main.cf" ] ; then LC_ALL=C sed -i "/^\s*inet_interfaces\s\+=\s\+/Id" "/etc/postfix/main.cf" else touch "/etc/postfix/main.cf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/postfix/main.cf" cp "/etc/postfix/main.cf" "/etc/postfix/main.cf.bak" # Insert at the end of the file printf '%s\n' "inet_interfaces=$var_postfix_inet_interfaces" >> "/etc/postfix/main.cf" # Clean up after ourselves. rm "/etc/postfix/main.cf.bak" systemctl restart postfix else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_postfix_network_listening_disabled' ############################################################################### # BEGIN fix (169 / 252) for 'xccdf_org.ssgproject.content_rule_service_rpcbind_disabled' ############################################################################### (>&2 echo "Remediating rule 169/252: 'xccdf_org.ssgproject.content_rule_service_rpcbind_disabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then SYSTEMCTL_EXEC='/usr/bin/systemctl' if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" stop 'rpcbind.service' fi "$SYSTEMCTL_EXEC" disable 'rpcbind.service' "$SYSTEMCTL_EXEC" mask 'rpcbind.service' # Disable socket activation if we have a unit file for it if "$SYSTEMCTL_EXEC" -q list-unit-files rpcbind.socket; then if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" stop 'rpcbind.socket' fi "$SYSTEMCTL_EXEC" mask 'rpcbind.socket' fi # The service may not be running because it has been started and failed, # so let's reset the state so OVAL checks pass. # Service should be 'inactive', not 'failed' after reboot though. "$SYSTEMCTL_EXEC" reset-failed 'rpcbind.service' || true else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_service_rpcbind_disabled' ############################################################################### # BEGIN fix (170 / 252) for 'xccdf_org.ssgproject.content_rule_package_chrony_installed' ############################################################################### (>&2 echo "Remediating rule 170/252: 'xccdf_org.ssgproject.content_rule_package_chrony_installed'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "chrony" ; then dnf install -y "chrony" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_chrony_installed' ############################################################################### # BEGIN fix (171 / 252) for 'xccdf_org.ssgproject.content_rule_chronyd_specify_remote_server' ############################################################################### (>&2 echo "Remediating rule 171/252: 'xccdf_org.ssgproject.content_rule_chronyd_specify_remote_server'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q chrony; }; then var_multiple_time_servers='0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org,3.pool.ntp.org' config_file="/etc/chrony.conf" if ! grep -q '^[[:space:]]*\(server\|pool\)[[:space:]]\+[[:graph:]]\+' "$config_file" ; then if ! grep -q '#[[:space:]]*server' "$config_file" ; then for server in $(echo "$var_multiple_time_servers" | tr ',' '\n') ; do printf '\nserver %s' "$server" >> "$config_file" done else sed -i 's/#[ \t]*server/server/g' "$config_file" fi if [[ -s "$config_file" ]] && [[ -n "$(tail -c 1 -- "$config_file" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "$config_file" fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_chronyd_specify_remote_server' ############################################################################### # BEGIN fix (172 / 252) for 'xccdf_org.ssgproject.content_rule_chronyd_run_as_chrony_user' ############################################################################### (>&2 echo "Remediating rule 172/252: 'xccdf_org.ssgproject.content_rule_chronyd_run_as_chrony_user'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q chrony; }; then if grep -q 'OPTIONS=.*' /etc/sysconfig/chronyd; then # trying to solve cases where the parameter after OPTIONS #may or may not be enclosed in quotes sed -i -E -e 's/\s*-u\s*\w+\s*/ /' -e 's/^([\s]*OPTIONS=["]?[^"]*)("?)/\1\2/' /etc/sysconfig/chronyd fi if grep -q 'OPTIONS=.*' /etc/sysconfig/chronyd; then # trying to solve cases where the parameter after OPTIONS #may or may not be enclosed in quotes sed -i -E -e 's/\s*-u\s*\w+\s*/ /' -e 's/^([\s]*OPTIONS=["]?[^"]*)("?)/\1 -u chrony\2/' /etc/sysconfig/chronyd else echo 'OPTIONS="-u chrony"' >> /etc/sysconfig/chronyd fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_chronyd_run_as_chrony_user' ############################################################################### # BEGIN fix (173 / 252) for 'xccdf_org.ssgproject.content_rule_service_rsyncd_disabled' ############################################################################### (>&2 echo "Remediating rule 173/252: 'xccdf_org.ssgproject.content_rule_service_rsyncd_disabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then SYSTEMCTL_EXEC='/usr/bin/systemctl' if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" stop 'rsyncd.service' fi "$SYSTEMCTL_EXEC" disable 'rsyncd.service' "$SYSTEMCTL_EXEC" mask 'rsyncd.service' # Disable socket activation if we have a unit file for it if "$SYSTEMCTL_EXEC" -q list-unit-files rsyncd.socket; then if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" stop 'rsyncd.socket' fi "$SYSTEMCTL_EXEC" mask 'rsyncd.socket' fi # The service may not be running because it has been started and failed, # so let's reset the state so OVAL checks pass. # Service should be 'inactive', not 'failed' after reboot though. "$SYSTEMCTL_EXEC" reset-failed 'rsyncd.service' || true else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_service_rsyncd_disabled' ############################################################################### # BEGIN fix (174 / 252) for 'xccdf_org.ssgproject.content_rule_package_telnet-server_removed' ############################################################################### (>&2 echo "Remediating rule 174/252: 'xccdf_org.ssgproject.content_rule_package_telnet-server_removed'"); ( # CAUTION: This remediation script will remove telnet-server # from the system, and may remove any packages # that depend on telnet-server. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "telnet-server" ; then dnf remove -y --noautoremove "telnet-server" fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_telnet-server_removed' ############################################################################### # BEGIN fix (175 / 252) for 'xccdf_org.ssgproject.content_rule_package_telnet_removed' ############################################################################### (>&2 echo "Remediating rule 175/252: 'xccdf_org.ssgproject.content_rule_package_telnet_removed'"); ( # CAUTION: This remediation script will remove telnet # from the system, and may remove any packages # that depend on telnet. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "telnet" ; then dnf remove -y --noautoremove "telnet" fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_telnet_removed' ############################################################################### # BEGIN fix (176 / 252) for 'xccdf_org.ssgproject.content_rule_package_tftp-server_removed' ############################################################################### (>&2 echo "Remediating rule 176/252: 'xccdf_org.ssgproject.content_rule_package_tftp-server_removed'"); ( # CAUTION: This remediation script will remove tftp-server # from the system, and may remove any packages # that depend on tftp-server. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "tftp-server" ; then dnf remove -y --noautoremove "tftp-server" fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_tftp-server_removed' ############################################################################### # BEGIN fix (177 / 252) for 'xccdf_org.ssgproject.content_rule_package_tftp_removed' ############################################################################### (>&2 echo "Remediating rule 177/252: 'xccdf_org.ssgproject.content_rule_package_tftp_removed'"); ( # CAUTION: This remediation script will remove tftp # from the system, and may remove any packages # that depend on tftp. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "tftp" ; then dnf remove -y --noautoremove "tftp" fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_tftp_removed' ############################################################################### # BEGIN fix (178 / 252) for 'xccdf_org.ssgproject.content_rule_package_net-snmp_removed' ############################################################################### (>&2 echo "Remediating rule 178/252: 'xccdf_org.ssgproject.content_rule_package_net-snmp_removed'"); ( # CAUTION: This remediation script will remove net-snmp # from the system, and may remove any packages # that depend on net-snmp. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "net-snmp" ; then dnf remove -y --noautoremove "net-snmp" fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_net-snmp_removed' ############################################################################### # BEGIN fix (179 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_config' ############################################################################### (>&2 echo "Remediating rule 179/252: 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_config'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then chmod u-xs,g-xwrs,o-xwrt /etc/ssh/sshd_config else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_config' ############################################################################### # BEGIN fix (180 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_private_key' ############################################################################### (>&2 echo "Remediating rule 180/252: 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_private_key'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then for keyfile in /etc/ssh/*_key; do test -f "$keyfile" || continue if test root:root = "$(stat -c "%U:%G" "$keyfile")"; then chmod u-xs,g-xwrs,o-xwrt "$keyfile" elif test root:ssh_keys = "$(stat -c "%U:%G" "$keyfile")"; then chmod u-xs,g-xws,o-xwrt "$keyfile" else echo "Key-like file '$keyfile' is owned by an unexpected user:group combination" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_private_key' ############################################################################### # BEGIN fix (181 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_pub_key' ############################################################################### (>&2 echo "Remediating rule 181/252: 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_pub_key'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then find -P /etc/ssh/ -maxdepth 1 -perm /u+xs,g+xws,o+xwt -type f -regextype posix-extended -regex '^.*\.pub$' -exec chmod u-xs,g-xws,o-xwt {} \; else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_pub_key' ############################################################################### # BEGIN fix (182 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_set_keepalive' ############################################################################### (>&2 echo "Remediating rule 182/252: 'xccdf_org.ssgproject.content_rule_sshd_set_keepalive'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_sshd_set_keepalive='1' mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then LC_ALL=C sed -i "/^\s*ClientAliveCountMax\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" else touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" # Insert at the beginning of the file printf '%s\n' "ClientAliveCountMax $var_sshd_set_keepalive" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_set_keepalive' ############################################################################### # BEGIN fix (183 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_set_idle_timeout' ############################################################################### (>&2 echo "Remediating rule 183/252: 'xccdf_org.ssgproject.content_rule_sshd_set_idle_timeout'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then sshd_idle_timeout_value='900' mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf LC_ALL=C sed -i "/^\s*ClientAliveInterval\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*ClientAliveInterval\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then LC_ALL=C sed -i "/^\s*ClientAliveInterval\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" else touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" # Insert at the beginning of the file printf '%s\n' "ClientAliveInterval $sshd_idle_timeout_value" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_set_idle_timeout' ############################################################################### # BEGIN fix (184 / 252) for 'xccdf_org.ssgproject.content_rule_disable_host_auth' ############################################################################### (>&2 echo "Remediating rule 184/252: 'xccdf_org.ssgproject.content_rule_disable_host_auth'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf chmod 0600 /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf LC_ALL=C sed -i "/^\s*HostbasedAuthentication\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*HostbasedAuthentication\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then LC_ALL=C sed -i "/^\s*HostbasedAuthentication\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" else touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" # Insert at the beginning of the file printf '%s\n' "HostbasedAuthentication no" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" cat "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_disable_host_auth' ############################################################################### # BEGIN fix (185 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_disable_empty_passwords' ############################################################################### (>&2 echo "Remediating rule 185/252: 'xccdf_org.ssgproject.content_rule_sshd_disable_empty_passwords'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf chmod 0600 /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf LC_ALL=C sed -i "/^\s*PermitEmptyPasswords\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*PermitEmptyPasswords\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then LC_ALL=C sed -i "/^\s*PermitEmptyPasswords\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" else touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" # Insert at the beginning of the file printf '%s\n' "PermitEmptyPasswords no" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" cat "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_disable_empty_passwords' ############################################################################### # BEGIN fix (186 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_disable_rhosts' ############################################################################### (>&2 echo "Remediating rule 186/252: 'xccdf_org.ssgproject.content_rule_sshd_disable_rhosts'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf chmod 0600 /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf LC_ALL=C sed -i "/^\s*IgnoreRhosts\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*IgnoreRhosts\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then LC_ALL=C sed -i "/^\s*IgnoreRhosts\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" else touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" # Insert at the beginning of the file printf '%s\n' "IgnoreRhosts yes" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" cat "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_disable_rhosts' ############################################################################### # BEGIN fix (187 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_disable_root_login' ############################################################################### (>&2 echo "Remediating rule 187/252: 'xccdf_org.ssgproject.content_rule_sshd_disable_root_login'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" else touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" # Insert at the beginning of the file printf '%s\n' "PermitRootLogin no" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_disable_root_login' ############################################################################### # BEGIN fix (188 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_disable_tcp_forwarding' ############################################################################### (>&2 echo "Remediating rule 188/252: 'xccdf_org.ssgproject.content_rule_sshd_disable_tcp_forwarding'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf LC_ALL=C sed -i "/^\s*AllowTcpForwarding\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*AllowTcpForwarding\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then LC_ALL=C sed -i "/^\s*AllowTcpForwarding\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" else touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" # Insert at the beginning of the file printf '%s\n' "AllowTcpForwarding no" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_disable_tcp_forwarding' ############################################################################### # BEGIN fix (189 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_disable_x11_forwarding' ############################################################################### (>&2 echo "Remediating rule 189/252: 'xccdf_org.ssgproject.content_rule_sshd_disable_x11_forwarding'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf chmod 0600 /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" else touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" # Insert at the beginning of the file printf '%s\n' "X11Forwarding no" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" cat "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_disable_x11_forwarding' ############################################################################### # BEGIN fix (190 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_do_not_permit_user_env' ############################################################################### (>&2 echo "Remediating rule 190/252: 'xccdf_org.ssgproject.content_rule_sshd_do_not_permit_user_env'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf chmod 0600 /etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf LC_ALL=C sed -i "/^\s*PermitUserEnvironment\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*PermitUserEnvironment\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" ] ; then LC_ALL=C sed -i "/^\s*PermitUserEnvironment\s\+/Id" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" else touch "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" cp "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" # Insert at the beginning of the file printf '%s\n' "PermitUserEnvironment no" > "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" cat "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" >> "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/01-complianceascode-reinforce-os-defaults.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_do_not_permit_user_env' ############################################################################### # BEGIN fix (191 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_enable_pam' ############################################################################### (>&2 echo "Remediating rule 191/252: 'xccdf_org.ssgproject.content_rule_sshd_enable_pam'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf LC_ALL=C sed -i "/^\s*UsePAM\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*UsePAM\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then LC_ALL=C sed -i "/^\s*UsePAM\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" else touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" # Insert at the beginning of the file printf '%s\n' "UsePAM yes" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_enable_pam' ############################################################################### # BEGIN fix (192 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_limit_user_access' ############################################################################### (>&2 echo "Remediating rule 192/252: 'xccdf_org.ssgproject.content_rule_sshd_limit_user_access'"); ( (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_sshd_limit_user_access' IS MISSING!") ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_limit_user_access' ############################################################################### # BEGIN fix (193 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_set_login_grace_time' ############################################################################### (>&2 echo "Remediating rule 193/252: 'xccdf_org.ssgproject.content_rule_sshd_set_login_grace_time'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_sshd_set_login_grace_time='60' mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf LC_ALL=C sed -i "/^\s*LoginGraceTime\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*LoginGraceTime\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then LC_ALL=C sed -i "/^\s*LoginGraceTime\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" else touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" # Insert at the beginning of the file printf '%s\n' "LoginGraceTime $var_sshd_set_login_grace_time" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_set_login_grace_time' ############################################################################### # BEGIN fix (194 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_set_loglevel_verbose' ############################################################################### (>&2 echo "Remediating rule 194/252: 'xccdf_org.ssgproject.content_rule_sshd_set_loglevel_verbose'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" else touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" # Insert at the beginning of the file printf '%s\n' "LogLevel VERBOSE" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_set_loglevel_verbose' ############################################################################### # BEGIN fix (195 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_set_max_auth_tries' ############################################################################### (>&2 echo "Remediating rule 195/252: 'xccdf_org.ssgproject.content_rule_sshd_set_max_auth_tries'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then sshd_max_auth_tries_value='4' mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf LC_ALL=C sed -i "/^\s*MaxAuthTries\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*MaxAuthTries\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then LC_ALL=C sed -i "/^\s*MaxAuthTries\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" else touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" # Insert at the beginning of the file printf '%s\n' "MaxAuthTries $sshd_max_auth_tries_value" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_set_max_auth_tries' ############################################################################### # BEGIN fix (196 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_set_max_sessions' ############################################################################### (>&2 echo "Remediating rule 196/252: 'xccdf_org.ssgproject.content_rule_sshd_set_max_sessions'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_sshd_max_sessions='10' mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf LC_ALL=C sed -i "/^\s*MaxSessions\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*MaxSessions\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then LC_ALL=C sed -i "/^\s*MaxSessions\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" else touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" # Insert at the beginning of the file printf '%s\n' "MaxSessions $var_sshd_max_sessions" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_set_max_sessions' ############################################################################### # BEGIN fix (197 / 252) for 'xccdf_org.ssgproject.content_rule_sshd_set_maxstartups' ############################################################################### (>&2 echo "Remediating rule 197/252: 'xccdf_org.ssgproject.content_rule_sshd_set_maxstartups'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then var_sshd_set_maxstartups='10:30:100' mkdir -p /etc/ssh/sshd_config.d touch /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf chmod 0600 /etc/ssh/sshd_config.d/00-complianceascode-hardening.conf LC_ALL=C sed -i "/^\s*MaxStartups\s\+/Id" "/etc/ssh/sshd_config" LC_ALL=C sed -i "/^\s*MaxStartups\s\+/Id" "/etc/ssh/sshd_config.d"/*.conf if [ -e "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" ] ; then LC_ALL=C sed -i "/^\s*MaxStartups\s\+/Id" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" else touch "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cp "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" # Insert at the beginning of the file printf '%s\n' "MaxStartups $var_sshd_set_maxstartups" > "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" cat "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" >> "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf" # Clean up after ourselves. rm "/etc/ssh/sshd_config.d/00-complianceascode-hardening.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_sshd_set_maxstartups' ############################################################################### # BEGIN fix (198 / 252) for 'xccdf_org.ssgproject.content_rule_package_audispd-plugins_installed' ############################################################################### (>&2 echo "Remediating rule 198/252: 'xccdf_org.ssgproject.content_rule_package_audispd-plugins_installed'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "audispd-plugins" ; then dnf install -y "audispd-plugins" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_audispd-plugins_installed' ############################################################################### # BEGIN fix (199 / 252) for 'xccdf_org.ssgproject.content_rule_package_audit_installed' ############################################################################### (>&2 echo "Remediating rule 199/252: 'xccdf_org.ssgproject.content_rule_package_audit_installed'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core; then if ! rpm -q --quiet "audit" ; then dnf install -y "audit" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_package_audit_installed' ############################################################################### # BEGIN fix (200 / 252) for 'xccdf_org.ssgproject.content_rule_service_auditd_enabled' ############################################################################### (>&2 echo "Remediating rule 200/252: 'xccdf_org.ssgproject.content_rule_service_auditd_enabled'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q audit; }; then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" unmask 'auditd.service' if [[ $("$SYSTEMCTL_EXEC" is-system-running) != "offline" ]]; then "$SYSTEMCTL_EXEC" start 'auditd.service' fi "$SYSTEMCTL_EXEC" enable 'auditd.service' else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_service_auditd_enabled' ############################################################################### # BEGIN fix (201 / 252) for 'xccdf_org.ssgproject.content_rule_grub2_audit_argument' ############################################################################### (>&2 echo "Remediating rule 201/252: 'xccdf_org.ssgproject.content_rule_grub2_audit_argument'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q grub2-common; }; then if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then KARGS_DIR="/usr/lib/bootc/kargs.d/" if grep -q -E "audit" "$KARGS_DIR/*.toml" ; then sed -i -E "s/^(\s*kargs\s*=\s*\[.*)\"audit=[^\"]*\"(.*]\s*)/\1\"audit=1\"\2/" "$KARGS_DIR/*.toml" else echo "kargs = [\"audit=1\"]" >> "$KARGS_DIR/10-audit.toml" fi else grubby --update-kernel=ALL --args=audit=1 fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_grub2_audit_argument' ############################################################################### # BEGIN fix (202 / 252) for 'xccdf_org.ssgproject.content_rule_grub2_audit_backlog_limit_argument' ############################################################################### (>&2 echo "Remediating rule 202/252: 'xccdf_org.ssgproject.content_rule_grub2_audit_backlog_limit_argument'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q kernel-core && { rpm --quiet -q grub2-common; }; then var_audit_backlog_limit='8192' if { rpm --quiet -q kernel rpm-ostree bootc && ! rpm --quiet -q openshift-kubelet && { [ -f "/run/.containerenv" ] || [ -f "/.containerenv" ]; }; } ; then KARGS_DIR="/usr/lib/bootc/kargs.d/" if grep -q -E "audit_backlog_limit" "$KARGS_DIR/*.toml" ; then sed -i -E "s/^(\s*kargs\s*=\s*\[.*)\"audit_backlog_limit=[^\"]*\"(.*]\s*)/\1\"audit_backlog_limit=$var_audit_backlog_limit\"\2/" "$KARGS_DIR/*.toml" else echo "kargs = [\"audit_backlog_limit=$var_audit_backlog_limit\"]" >> "$KARGS_DIR/10-audit_backlog_limit.toml" fi else grubby --update-kernel=ALL --args=audit_backlog_limit=$var_audit_backlog_limit fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_grub2_audit_backlog_limit_argument' ############################################################################### # BEGIN fix (203 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_immutable' ############################################################################### (>&2 echo "Remediating rule 203/252: 'xccdf_org.ssgproject.content_rule_audit_rules_immutable'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Traverse all of: # # /etc/audit/audit.rules, (for auditctl case) # /etc/audit/rules.d/*.rules (for augenrules case) # # files to check if '-e .*' setting is present in that '*.rules' file already. # If found, delete such occurrence since auditctl(8) manual page instructs the # '-e 2' rule should be placed as the last rule in the configuration find /etc/audit /etc/audit/rules.d -maxdepth 1 -type f -name '*.rules' -exec sed -i '/-e[[:space:]]\+.*/d' {} ';' # Append '-e 2' requirement at the end of both: # * /etc/audit/audit.rules file (for auditctl case) # * /etc/audit/rules.d/immutable.rules (for augenrules case) for AUDIT_FILE in "/etc/audit/audit.rules" "/etc/audit/rules.d/immutable.rules" do echo '' >> $AUDIT_FILE echo '# Set the audit.rules configuration immutable per security requirements' >> $AUDIT_FILE echo '# Reboot is required to change audit rules once this setting is applied' >> $AUDIT_FILE echo '-e 2' >> $AUDIT_FILE chmod o-rwx $AUDIT_FILE chmod g-rwx $AUDIT_FILE done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_immutable' ############################################################################### # BEGIN fix (204 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_mac_modification' ############################################################################### (>&2 echo "Remediating rule 204/252: 'xccdf_org.ssgproject.content_rule_audit_rules_mac_modification'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/selinux/" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/selinux/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/selinux/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/selinux/ -p wa -k MAC-policy" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/MAC-policy.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/selinux/" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/MAC-policy.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/MAC-policy.rules" # If the MAC-policy.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/selinux/" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/selinux/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/selinux/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/selinux/ -p wa -k MAC-policy" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_mac_modification' ############################################################################### # BEGIN fix (205 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_media_export' ############################################################################### (>&2 echo "Remediating rule 205/252: 'xccdf_org.ssgproject.content_rule_audit_rules_media_export'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="mount" KEY="export" SYSCALL_GROUPING="" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_media_export' ############################################################################### # BEGIN fix (206 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_networkconfig_modification' ############################################################################### (>&2 echo "Remediating rule 206/252: 'xccdf_org.ssgproject.content_rule_audit_rules_networkconfig_modification'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="" SYSCALL="sethostname setdomainname" KEY="audit_rules_networkconfig_modification" SYSCALL_GROUPING="sethostname setdomainname" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done # Then perform the remediations for the watch rules # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/issue" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/issue$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/issue -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/issue" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/issue" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/issue$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/issue -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/issue.net" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue.net $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/issue.net$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/issue.net" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/issue.net" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/issue.net $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/issue.net$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/issue.net -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/hosts" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/hosts $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/hosts$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/hosts -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/hosts" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/hosts" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/hosts $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/hosts$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/hosts -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/sysconfig/network" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sysconfig/network $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/sysconfig/network$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sysconfig/network" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/audit_rules_networkconfig_modification.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/audit_rules_networkconfig_modification.rules" # If the audit_rules_networkconfig_modification.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/sysconfig/network" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sysconfig/network $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/sysconfig/network$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/sysconfig/network -p wa -k audit_rules_networkconfig_modification" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_networkconfig_modification' ############################################################################### # BEGIN fix (207 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_session_events_btmp' ############################################################################### (>&2 echo "Remediating rule 207/252: 'xccdf_org.ssgproject.content_rule_audit_rules_session_events_btmp'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/log/btmp" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/btmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/log/btmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/log/btmp -p wa -k session" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/session.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/btmp" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/session.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/session.rules" # If the session.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/log/btmp" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/btmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/log/btmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/log/btmp -p wa -k session" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_session_events_btmp' ############################################################################### # BEGIN fix (208 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_session_events_utmp' ############################################################################### (>&2 echo "Remediating rule 208/252: 'xccdf_org.ssgproject.content_rule_audit_rules_session_events_utmp'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/run/utmp" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/run/utmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/run/utmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/run/utmp -p wa -k session" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/session.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/run/utmp" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/session.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/session.rules" # If the session.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/run/utmp" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/run/utmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/run/utmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/run/utmp -p wa -k session" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_session_events_utmp' ############################################################################### # BEGIN fix (209 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_session_events_wtmp' ############################################################################### (>&2 echo "Remediating rule 209/252: 'xccdf_org.ssgproject.content_rule_audit_rules_session_events_wtmp'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/log/wtmp" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/wtmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/log/wtmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/log/wtmp -p wa -k session" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/session.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/wtmp" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/session.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/session.rules" # If the session.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/log/wtmp" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/wtmp $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/log/wtmp$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/log/wtmp -p wa -k session" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_session_events_wtmp' ############################################################################### # BEGIN fix (210 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_suid_privilege_function' ############################################################################### (>&2 echo "Remediating rule 210/252: 'xccdf_org.ssgproject.content_rule_audit_rules_suid_privilege_function'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="-C uid!=euid -F euid=0" AUID_FILTERS="" SYSCALL="execve" KEY="setuid" SYSCALL_GROUPING="" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="-C gid!=egid -F egid=0" AUID_FILTERS="" SYSCALL="execve" KEY="setgid" SYSCALL_GROUPING="" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_suid_privilege_function' ############################################################################### # BEGIN fix (211 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_sysadmin_actions' ############################################################################### (>&2 echo "Remediating rule 211/252: 'xccdf_org.ssgproject.content_rule_audit_rules_sysadmin_actions'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/actions.rules" # If the actions.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/sudoers$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/sudoers -p wa -k actions" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/actions.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/sudoers.d/" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/actions.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/actions.rules" # If the actions.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/sudoers.d/" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/sudoers.d/ $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/sudoers.d/$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/sudoers.d/ -p wa -k actions" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_sysadmin_actions' ############################################################################### # BEGIN fix (212 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_group' ############################################################################### (>&2 echo "Remediating rule 212/252: 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_group'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/group" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/group" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/group $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/group$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/group -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_group' ############################################################################### # BEGIN fix (213 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_gshadow' ############################################################################### (>&2 echo "Remediating rule 213/252: 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_gshadow'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/gshadow" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/gshadow" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/gshadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/gshadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/gshadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_gshadow' ############################################################################### # BEGIN fix (214 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_opasswd' ############################################################################### (>&2 echo "Remediating rule 214/252: 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_opasswd'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/security/opasswd" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/security/opasswd" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/security/opasswd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/security/opasswd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/security/opasswd -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_opasswd' ############################################################################### # BEGIN fix (215 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_passwd' ############################################################################### (>&2 echo "Remediating rule 215/252: 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_passwd'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification_passwd" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification_passwd.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/passwd" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/audit_rules_usergroup_modification_passwd.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification_passwd.rules" # If the audit_rules_usergroup_modification_passwd.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/passwd" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/passwd $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/passwd$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/passwd -p wa -k audit_rules_usergroup_modification_passwd" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_passwd' ############################################################################### # BEGIN fix (216 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_shadow' ############################################################################### (>&2 echo "Remediating rule 216/252: 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_shadow'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/shadow" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/audit_rules_usergroup_modification.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/audit_rules_usergroup_modification.rules" # If the audit_rules_usergroup_modification.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/shadow" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/shadow $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/shadow$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/shadow -p wa -k audit_rules_usergroup_modification" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification_shadow' ############################################################################### # BEGIN fix (217 / 252) for 'xccdf_org.ssgproject.content_rule_audit_sudo_log_events' ############################################################################### (>&2 echo "Remediating rule 217/252: 'xccdf_org.ssgproject.content_rule_audit_sudo_log_events'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/log/sudo.log" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/sudo.log $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/log/sudo.log$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/log/sudo.log -p wa -k maintenance" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/maintenance.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/sudo.log" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/maintenance.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/maintenance.rules" # If the maintenance.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/log/sudo.log" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/sudo.log $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/log/sudo.log$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/log/sudo.log -p wa -k maintenance" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_sudo_log_events' ############################################################################### # BEGIN fix (218 / 252) for 'xccdf_org.ssgproject.content_rule_directory_access_var_log_audit' ############################################################################### (>&2 echo "Remediating rule 218/252: 'xccdf_org.ssgproject.content_rule_directory_access_var_log_audit'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then OTHER_FILTERS="-F dir=/var/log/audit/ -F perm=r" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="" KEY="access-audit-trail" SYSCALL_GROUPING="" [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_directory_access_var_log_audit' ############################################################################### # BEGIN fix (219 / 252) for 'xccdf_org.ssgproject.content_rule_file_group_ownership_var_log_audit' ############################################################################### (>&2 echo "Remediating rule 219/252: 'xccdf_org.ssgproject.content_rule_file_group_ownership_var_log_audit'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then if LC_ALL=C grep -iw log_file /etc/audit/auditd.conf; then FILE=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') else FILE="/var/log/audit/audit.log" fi if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') if ! [ "${GROUP}" == 'root' ]; then chgrp ${GROUP} $FILE* else chgrp root $FILE* fi else chgrp root $FILE* fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_group_ownership_var_log_audit' ############################################################################### # BEGIN fix (220 / 252) for 'xccdf_org.ssgproject.content_rule_file_ownership_var_log_audit' ############################################################################### (>&2 echo "Remediating rule 220/252: 'xccdf_org.ssgproject.content_rule_file_ownership_var_log_audit'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then if LC_ALL=C grep -m 1 -q ^log_group /etc/audit/auditd.conf; then GROUP=$(awk -F "=" '/log_group/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') if ! [ "${GROUP}" == 'root' ] ; then chown root:${GROUP} /var/log/audit chown root:${GROUP} /var/log/audit/audit.log* else chown root:root /var/log/audit chown root:root /var/log/audit/audit.log* fi else chown root:root /var/log/audit chown root:root /var/log/audit/audit.log* fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_ownership_var_log_audit' ############################################################################### # BEGIN fix (221 / 252) for 'xccdf_org.ssgproject.content_rule_file_permissions_var_log_audit' ############################################################################### (>&2 echo "Remediating rule 221/252: 'xccdf_org.ssgproject.content_rule_file_permissions_var_log_audit'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then if LC_ALL=C grep -iw ^log_file /etc/audit/auditd.conf; then FILE=$(awk -F "=" '/^log_file/ {print $2}' /etc/audit/auditd.conf | tr -d ' ') else FILE="/var/log/audit/audit.log" fi chmod 0600 $FILE else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_var_log_audit' ############################################################################### # BEGIN fix (222 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chmod' ############################################################################### (>&2 echo "Remediating rule 222/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chmod'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="chmod" KEY="perm_mod" SYSCALL_GROUPING="chmod fchmod fchmodat" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chmod' ############################################################################### # BEGIN fix (223 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chown' ############################################################################### (>&2 echo "Remediating rule 223/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chown'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="chown" KEY="perm_mod" SYSCALL_GROUPING="chown fchown fchownat lchown" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chown' ############################################################################### # BEGIN fix (224 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchmod' ############################################################################### (>&2 echo "Remediating rule 224/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchmod'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="fchmod" KEY="perm_mod" SYSCALL_GROUPING="chmod fchmod fchmodat" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchmod' ############################################################################### # BEGIN fix (225 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchmodat' ############################################################################### (>&2 echo "Remediating rule 225/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchmodat'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="fchmodat" KEY="perm_mod" SYSCALL_GROUPING="chmod fchmod fchmodat" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchmodat' ############################################################################### # BEGIN fix (226 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchown' ############################################################################### (>&2 echo "Remediating rule 226/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchown'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="fchown" KEY="perm_mod" SYSCALL_GROUPING="chown fchown fchownat lchown" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchown' ############################################################################### # BEGIN fix (227 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchownat' ############################################################################### (>&2 echo "Remediating rule 227/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchownat'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="fchownat" KEY="perm_mod" SYSCALL_GROUPING="chown fchown fchownat lchown" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fchownat' ############################################################################### # BEGIN fix (228 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fremovexattr' ############################################################################### (>&2 echo "Remediating rule 228/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fremovexattr'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="fremovexattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid=0" SYSCALL="fremovexattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fremovexattr' ############################################################################### # BEGIN fix (229 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fsetxattr' ############################################################################### (>&2 echo "Remediating rule 229/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fsetxattr'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="fsetxattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid=0" SYSCALL="fsetxattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_fsetxattr' ############################################################################### # BEGIN fix (230 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lchown' ############################################################################### (>&2 echo "Remediating rule 230/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lchown'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="lchown" KEY="perm_mod" SYSCALL_GROUPING="chown fchown fchownat lchown" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lchown' ############################################################################### # BEGIN fix (231 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lremovexattr' ############################################################################### (>&2 echo "Remediating rule 231/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lremovexattr'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="lremovexattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid=0" SYSCALL="lremovexattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lremovexattr' ############################################################################### # BEGIN fix (232 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lsetxattr' ############################################################################### (>&2 echo "Remediating rule 232/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lsetxattr'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="lsetxattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid=0" SYSCALL="lsetxattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_lsetxattr' ############################################################################### # BEGIN fix (233 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_removexattr' ############################################################################### (>&2 echo "Remediating rule 233/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_removexattr'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="removexattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid=0" SYSCALL="removexattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_removexattr' ############################################################################### # BEGIN fix (234 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_setxattr' ############################################################################### (>&2 echo "Remediating rule 234/252: 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_setxattr'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="setxattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid=0" SYSCALL="setxattr" KEY="perm_mod" SYSCALL_GROUPING="fremovexattr lremovexattr removexattr fsetxattr lsetxattr setxattr" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_setxattr' ############################################################################### # BEGIN fix (235 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_rename' ############################################################################### (>&2 echo "Remediating rule 235/252: 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_rename'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="rename" KEY="delete" SYSCALL_GROUPING="unlink unlinkat rename renameat renameat2 rmdir" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_rename' ############################################################################### # BEGIN fix (236 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_renameat' ############################################################################### (>&2 echo "Remediating rule 236/252: 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_renameat'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="renameat" KEY="delete" SYSCALL_GROUPING="unlink unlinkat rename renameat renameat2 rmdir" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_renameat' ############################################################################### # BEGIN fix (237 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_rmdir' ############################################################################### (>&2 echo "Remediating rule 237/252: 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_rmdir'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="rmdir" KEY="delete" SYSCALL_GROUPING="unlink unlinkat rename renameat renameat2 rmdir" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_rmdir' ############################################################################### # BEGIN fix (238 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_unlink' ############################################################################### (>&2 echo "Remediating rule 238/252: 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_unlink'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core && { ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ); }; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="unlink" KEY="delete" SYSCALL_GROUPING="unlink unlinkat rename renameat renameat2 rmdir" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_unlink' ############################################################################### # BEGIN fix (239 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_unlinkat' ############################################################################### (>&2 echo "Remediating rule 239/252: 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_unlinkat'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="unlinkat" KEY="delete" SYSCALL_GROUPING="unlink unlinkat rename renameat renameat2 rmdir" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_file_deletion_events_unlinkat' ############################################################################### # BEGIN fix (240 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_faillock' ############################################################################### (>&2 echo "Remediating rule 240/252: 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_faillock'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' var_accounts_passwords_pam_faillock_dir='/var/log/faillock' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+${var_accounts_passwords_pam_faillock_dir}" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+${var_accounts_passwords_pam_faillock_dir} $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+${var_accounts_passwords_pam_faillock_dir}$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w ${var_accounts_passwords_pam_faillock_dir} -p wa -k logins" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+${var_accounts_passwords_pam_faillock_dir}" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/logins.rules" # If the logins.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+${var_accounts_passwords_pam_faillock_dir}" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+${var_accounts_passwords_pam_faillock_dir} $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+${var_accounts_passwords_pam_faillock_dir}$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w ${var_accounts_passwords_pam_faillock_dir} -p wa -k logins" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_faillock' ############################################################################### # BEGIN fix (241 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_lastlog' ############################################################################### (>&2 echo "Remediating rule 241/252: 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_lastlog'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/log/lastlog" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/lastlog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/log/lastlog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/log/lastlog -p wa -k logins" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/lastlog" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/logins.rules" # If the logins.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/log/lastlog" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/lastlog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/log/lastlog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/log/lastlog -p wa -k logins" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_lastlog' ############################################################################### # BEGIN fix (242 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_tallylog' ############################################################################### (>&2 echo "Remediating rule 242/252: 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_tallylog'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/log/tallylog" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/tallylog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/log/tallylog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/log/tallylog -p wa -k logins" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/logins.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/var/log/tallylog" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/logins.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/logins.rules" # If the logins.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/var/log/tallylog" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/var/log/tallylog $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/var/log/tallylog$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /var/log/tallylog -p wa -k logins" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_tallylog' ############################################################################### # BEGIN fix (243 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_time_adjtimex' ############################################################################### (>&2 echo "Remediating rule 243/252: 'xccdf_org.ssgproject.content_rule_audit_rules_time_adjtimex'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do # Create expected audit group and audit rule form for particular system call & architecture if [ ${ARCH} = "b32" ] then ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" # stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output) # so append it to the list of time group system calls to be audited SYSCALL="adjtimex settimeofday stime" SYSCALL_GROUPING="adjtimex settimeofday stime" elif [ ${ARCH} = "b64" ] then ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" # stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output) # therefore don't add it to the list of time group system calls to be audited SYSCALL="adjtimex settimeofday" SYSCALL_GROUPING="adjtimex settimeofday" fi OTHER_FILTERS="" AUID_FILTERS="" KEY="audit_time_rules" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_time_adjtimex' ############################################################################### # BEGIN fix (244 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_time_clock_settime' ############################################################################### (>&2 echo "Remediating rule 244/252: 'xccdf_org.ssgproject.content_rule_audit_rules_time_clock_settime'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="-F a0=0x0" AUID_FILTERS="" SYSCALL="clock_settime" KEY="time-change" SYSCALL_GROUPING="" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_time_clock_settime' ############################################################################### # BEGIN fix (245 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_time_settimeofday' ############################################################################### (>&2 echo "Remediating rule 245/252: 'xccdf_org.ssgproject.content_rule_audit_rules_time_settimeofday'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do # Create expected audit group and audit rule form for particular system call & architecture if [ ${ARCH} = "b32" ] then ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" # stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output) # so append it to the list of time group system calls to be audited SYSCALL="adjtimex settimeofday stime" SYSCALL_GROUPING="adjtimex settimeofday stime" elif [ ${ARCH} = "b64" ] then ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" # stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output) # therefore don't add it to the list of time group system calls to be audited SYSCALL="adjtimex settimeofday" SYSCALL_GROUPING="adjtimex settimeofday" fi OTHER_FILTERS="" AUID_FILTERS="" KEY="audit_time_rules" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_time_settimeofday' ############################################################################### # BEGIN fix (246 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_time_stime' ############################################################################### (>&2 echo "Remediating rule 246/252: 'xccdf_org.ssgproject.content_rule_audit_rules_time_stime'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core && { ( ! ( ( grep -sqE "^.*\.aarch64$" /proc/sys/kernel/osrelease || grep -sqE "^aarch64$" /proc/sys/kernel/arch; ) ) && ! ( ( grep -sqE "^.*\.s390x$" /proc/sys/kernel/osrelease || grep -sqE "^s390x$" /proc/sys/kernel/arch; ) ) ); }; then # Retrieve hardware architecture of the underlying system [ "$(getconf LONG_BIT)" = "32" ] && RULE_ARCHS=("b32") || RULE_ARCHS=("b32" "b64") for ARCH in "${RULE_ARCHS[@]}" do # Create expected audit group and audit rule form for particular system call & architecture if [ ${ARCH} = "b32" ] then ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" # stime system call is known at 32-bit arch (see e.g "$ ausyscall i386 stime" 's output) # so append it to the list of time group system calls to be audited SYSCALL="adjtimex settimeofday stime" SYSCALL_GROUPING="adjtimex settimeofday stime" elif [ ${ARCH} = "b64" ] then ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" # stime system call isn't known at 64-bit arch (see "$ ausyscall x86_64 stime" 's output) # therefore don't add it to the list of time group system calls to be audited SYSCALL="adjtimex settimeofday" SYSCALL_GROUPING="adjtimex settimeofday" fi OTHER_FILTERS="" AUID_FILTERS="" KEY="audit_time_rules" # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'augenrules', then check if the audit rule is defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to the list for inspection # If rule isn't defined yet, add '/etc/audit/rules.d/$key.rules' to the list for inspection default_file="/etc/audit/rules.d/$KEY.rules" # As other_filters may include paths, lets use a different delimiter for it # The "F" script expression tells sed to print the filenames where the expressions matched readarray -t files_to_inspect < <(sed -s -n -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" -e "F" /etc/audit/rules.d/*.rules) # Case when particular rule isn't defined in /etc/audit/rules.d/*.rules yet if [ ${#files_to_inspect[@]} -eq "0" ] then file_to_inspect="/etc/audit/rules.d/$KEY.rules" files_to_inspect=("$file_to_inspect") if [ ! -e "$file_to_inspect" ] then touch "$file_to_inspect" chmod 0600 "$file_to_inspect" fi fi # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi unset syscall_a unset syscall_grouping unset syscall_string unset syscall unset file_to_edit unset rule_to_edit unset rule_syscalls_to_edit unset other_string unset auid_string unset full_rule # Load macro arguments into arrays read -a syscall_a <<< $SYSCALL read -a syscall_grouping <<< $SYSCALL_GROUPING # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- # files_to_inspect=() # If audit tool is 'auditctl', then add '/etc/audit/audit.rules' # file to the list of files to be inspected default_file="/etc/audit/audit.rules" files_to_inspect+=('/etc/audit/audit.rules' ) # After converting to jinja, we cannot return; therefore we skip the rest of the macro if needed instead skip=1 for audit_file in "${files_to_inspect[@]}" do # Filter existing $audit_file rules' definitions to select those that satisfy the rule pattern, # i.e, collect rules that match: # * the action, list and arch, (2-nd argument) # * the other filters, (3-rd argument) # * the auid filters, (4-rd argument) readarray -t similar_rules < <(sed -e "/^$ACTION_ARCH_FILTERS/!d" -e "\#$OTHER_FILTERS#!d" -e "/$AUID_FILTERS/!d" "$audit_file") candidate_rules=() # Filter out rules that have more fields then required. This will remove rules more specific than the required scope for s_rule in "${similar_rules[@]}" do # Strip all the options and fields we know of, # than check if there was any field left over extra_fields=$(sed -E -e "s/^$ACTION_ARCH_FILTERS//" -e "s#$OTHER_FILTERS##" -e "s/$AUID_FILTERS//" -e "s/((:?-S [[:alnum:],]+)+)//g" -e "s/-F key=\w+|-k \w+//"<<< "$s_rule") grep -q -- "-F" <<< "$extra_fields" || candidate_rules+=("$s_rule") done if [[ ${#syscall_a[@]} -ge 1 ]] then # Check if the syscall we want is present in any of the similar existing rules for rule in "${candidate_rules[@]}" do rule_syscalls=$(echo "$rule" | grep -o -P '(-S [\w,]+)+' | xargs) all_syscalls_found=0 for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "$rule_syscalls" || { # A syscall was not found in the candidate rule all_syscalls_found=1 } done if [[ $all_syscalls_found -eq 0 ]] then # We found a rule with all the syscall(s) we want; skip rest of macro skip=0 break fi # Check if this rule can be grouped with our target syscall and keep track of it for syscall_g in "${syscall_grouping[@]}" do if grep -q -- "\b${syscall_g}\b" <<< "$rule_syscalls" then file_to_edit=${audit_file} rule_to_edit=${rule} rule_syscalls_to_edit=${rule_syscalls} fi done done else # If there is any candidate rule, it is compliant; skip rest of macro if [ "${#candidate_rules[@]}" -gt 0 ] then skip=0 fi fi if [ "$skip" -eq 0 ]; then break fi done if [ "$skip" -ne 0 ]; then # We checked all rules that matched the expected resemblance pattern (action, arch & auid) # At this point we know if we need to either append the $full_rule or group # the syscall together with an exsiting rule # Append the full_rule if it cannot be grouped to any other rule if [ -z ${rule_to_edit+x} ] then # Build full_rule while avoid adding double spaces when other_filters is empty if [ "${#syscall_a[@]}" -gt 0 ] then syscall_string="" for syscall in "${syscall_a[@]}" do syscall_string+=" -S $syscall" done fi other_string=$([[ $OTHER_FILTERS ]] && echo " $OTHER_FILTERS") || /bin/true auid_string=$([[ $AUID_FILTERS ]] && echo " $AUID_FILTERS") || /bin/true full_rule="$ACTION_ARCH_FILTERS${syscall_string}${other_string}${auid_string} -F key=$KEY" || /bin/true echo "$full_rule" >> "$default_file" chmod 0600 ${default_file} else # Check if the syscalls are declared as a comma separated list or # as multiple -S parameters if grep -q -- "," <<< "${rule_syscalls_to_edit}" then delimiter="," else delimiter=" -S " fi new_grouped_syscalls="${rule_syscalls_to_edit}" for syscall in "${syscall_a[@]}" do grep -q -- "\b${syscall}\b" <<< "${rule_syscalls_to_edit}" || { # A syscall was not found in the candidate rule new_grouped_syscalls+="${delimiter}${syscall}" } done # Group the syscall in the rule sed -i -e "\#${rule_to_edit}#s#${rule_syscalls_to_edit}#${new_grouped_syscalls}#" "$file_to_edit" fi fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_time_stime' ############################################################################### # BEGIN fix (247 / 252) for 'xccdf_org.ssgproject.content_rule_audit_rules_time_watch_localtime' ############################################################################### (>&2 echo "Remediating rule 247/252: 'xccdf_org.ssgproject.content_rule_audit_rules_time_watch_localtime'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then # Perform the remediation for both possible tools: 'auditctl' and 'augenrules' # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit tool is 'auditctl', then add '/etc/audit/audit.rules' # into the list of files to be inspected files_to_inspect+=('/etc/audit/audit.rules') # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/localtime" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/localtime $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/localtime$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/localtime -p wa -k audit_time_rules" >> "$audit_rules_file" fi done # Create a list of audit *.rules files that should be inspected for presence and correctness # of a particular audit rule. The scheme is as follows: # # ----------------------------------------------------------------------------------------- # Tool used to load audit rules | Rule already defined | Audit rules file to inspect | # ----------------------------------------------------------------------------------------- # auditctl | Doesn't matter | /etc/audit/audit.rules | # ----------------------------------------------------------------------------------------- # augenrules | Yes | /etc/audit/rules.d/*.rules | # augenrules | No | /etc/audit/rules.d/$key.rules | # ----------------------------------------------------------------------------------------- files_to_inspect=() # If the audit is 'augenrules', then check if rule is already defined # If rule is defined, add '/etc/audit/rules.d/*.rules' to list of files for inspection. # If rule isn't defined, add '/etc/audit/rules.d/audit_time_rules.rules' to list of files for inspection. readarray -t matches < <(grep -HP "[\s]*-w[\s]+/etc/localtime" /etc/audit/rules.d/*.rules) # For each of the matched entries for match in "${matches[@]}" do # Extract filepath from the match rulesd_audit_file=$(echo $match | cut -f1 -d ':') # Append that path into list of files for inspection files_to_inspect+=("$rulesd_audit_file") done # Case when particular audit rule isn't defined yet if [ "${#files_to_inspect[@]}" -eq "0" ] then # Append '/etc/audit/rules.d/audit_time_rules.rules' into list of files for inspection key_rule_file="/etc/audit/rules.d/audit_time_rules.rules" # If the audit_time_rules.rules file doesn't exist yet, create it with correct permissions if [ ! -e "$key_rule_file" ] then touch "$key_rule_file" chmod 0600 "$key_rule_file" fi files_to_inspect+=("$key_rule_file") fi # Finally perform the inspection and possible subsequent audit rule # correction for each of the files previously identified for inspection for audit_rules_file in "${files_to_inspect[@]}" do # Check if audit watch file system object rule for given path already present if grep -q -P -- "^[\s]*-w[\s]+/etc/localtime" "$audit_rules_file" then # Rule is found => verify yet if existing rule definition contains # all of the required access type bits # Define BRE whitespace class shortcut sp="[[:space:]]" # Extract current permission access types (e.g. -p [r|w|x|a] values) from audit rule current_access_bits=$(sed -ne "s#$sp*-w$sp\+/etc/localtime $sp\+-p$sp\+\([rxwa]\{1,4\}\).*#\1#p" "$audit_rules_file") # Split required access bits string into characters array # (to check bit's presence for one bit at a time) for access_bit in $(echo "wa" | grep -o .) do # For each from the required access bits (e.g. 'w', 'a') check # if they are already present in current access bits for rule. # If not, append that bit at the end if ! grep -q "$access_bit" <<< "$current_access_bits" then # Concatenate the existing mask with the missing bit current_access_bits="$current_access_bits$access_bit" fi done # Propagate the updated rule's access bits (original + the required # ones) back into the /etc/audit/audit.rules file for that rule sed -i "s#\($sp*-w$sp\+/etc/localtime$sp\+-p$sp\+\)\([rxwa]\{1,4\}\)\(.*\)#\1$current_access_bits\3#" "$audit_rules_file" else # Rule isn't present yet. Append it at the end of $audit_rules_file file # with proper key echo "-w /etc/localtime -p wa -k audit_time_rules" >> "$audit_rules_file" fi done else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_time_watch_localtime' ############################################################################### # BEGIN fix (248 / 252) for 'xccdf_org.ssgproject.content_rule_auditd_audispd_syslog_plugin_activated' ############################################################################### (>&2 echo "Remediating rule 248/252: 'xccdf_org.ssgproject.content_rule_auditd_audispd_syslog_plugin_activated'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then var_syslog_active="yes" AUDISP_SYSLOGCONFIG=/etc/audit/plugins.d/syslog.conf # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^active") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$var_syslog_active" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^active\\>" "$AUDISP_SYSLOGCONFIG"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^active\\>.*/$escaped_formatted_output/gi" "$AUDISP_SYSLOGCONFIG" else if [[ -s "$AUDISP_SYSLOGCONFIG" ]] && [[ -n "$(tail -c 1 -- "$AUDISP_SYSLOGCONFIG" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "$AUDISP_SYSLOGCONFIG" fi cce="CCE-83695-7" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "$AUDISP_SYSLOGCONFIG" >> "$AUDISP_SYSLOGCONFIG" printf '%s\n' "$formatted_output" >> "$AUDISP_SYSLOGCONFIG" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_auditd_audispd_syslog_plugin_activated' ############################################################################### # BEGIN fix (249 / 252) for 'xccdf_org.ssgproject.content_rule_auditd_data_retention_admin_space_left_action' ############################################################################### (>&2 echo "Remediating rule 249/252: 'xccdf_org.ssgproject.content_rule_auditd_data_retention_admin_space_left_action'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then var_auditd_admin_space_left_action='single' var_auditd_admin_space_left_action="$(echo $var_auditd_admin_space_left_action | cut -d \| -f 1)" AUDITCONFIG=/etc/audit/auditd.conf # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^admin_space_left_action") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_admin_space_left_action" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^admin_space_left_action\\>" "$AUDITCONFIG"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^admin_space_left_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" else if [[ -s "$AUDITCONFIG" ]] && [[ -n "$(tail -c 1 -- "$AUDITCONFIG" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "$AUDITCONFIG" fi cce="CCE-83700-5" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "$AUDITCONFIG" >> "$AUDITCONFIG" printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_auditd_data_retention_admin_space_left_action' ############################################################################### # BEGIN fix (250 / 252) for 'xccdf_org.ssgproject.content_rule_auditd_data_retention_space_left' ############################################################################### (>&2 echo "Remediating rule 250/252: 'xccdf_org.ssgproject.content_rule_auditd_data_retention_space_left'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then var_auditd_space_left='100' grep -q "^space_left[[:space:]]*=.*$" /etc/audit/auditd.conf && \ sed -i "s/^space_left[[:space:]]*=.*$/space_left = $var_auditd_space_left/g" /etc/audit/auditd.conf || \ echo "space_left = $var_auditd_space_left" >> /etc/audit/auditd.conf else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_auditd_data_retention_space_left' ############################################################################### # BEGIN fix (251 / 252) for 'xccdf_org.ssgproject.content_rule_auditd_data_retention_space_left_action' ############################################################################### (>&2 echo "Remediating rule 251/252: 'xccdf_org.ssgproject.content_rule_auditd_data_retention_space_left_action'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then var_auditd_space_left_action='email' var_auditd_space_left_action="$(echo $var_auditd_space_left_action | cut -d \| -f 1)" # # If space_left_action present in /etc/audit/auditd.conf, change value # to var_auditd_space_left_action, else # add "space_left_action = $var_auditd_space_left_action" to /etc/audit/auditd.conf # AUDITCONFIG=/etc/audit/auditd.conf # Strip any search characters in the key arg so that the key can be replaced without # adding any search characters to the config file. stripped_key=$(sed 's/[\^=\$,;+]*//g' <<< "^space_left_action") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$var_auditd_space_left_action" # If the key exists, change it. Otherwise, add it to the config_file. # We search for the key string followed by a word boundary (matched by \>), # so if we search for 'setting', 'setting2' won't match. if LC_ALL=C grep -q -m 1 -i -e "^space_left_action\\>" "$AUDITCONFIG"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^space_left_action\\>.*/$escaped_formatted_output/gi" "$AUDITCONFIG" else if [[ -s "$AUDITCONFIG" ]] && [[ -n "$(tail -c 1 -- "$AUDITCONFIG" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "$AUDITCONFIG" fi cce="CCE-83703-9" printf '# Per %s: Set %s in %s\n' "${cce}" "${formatted_output}" "$AUDITCONFIG" >> "$AUDITCONFIG" printf '%s\n' "$formatted_output" >> "$AUDITCONFIG" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_auditd_data_retention_space_left_action' ############################################################################### # BEGIN fix (252 / 252) for 'xccdf_org.ssgproject.content_rule_auditd_name_format' ############################################################################### (>&2 echo "Remediating rule 252/252: 'xccdf_org.ssgproject.content_rule_auditd_name_format'"); ( # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel-core; then var_auditd_name_format='fqd' var_auditd_name_format="$(echo $var_auditd_name_format | cut -d \| -f 1)" if [ -e "/etc/audit/auditd.conf" ] ; then LC_ALL=C sed -i "/^\s*name_format\s*=\s*/Id" "/etc/audit/auditd.conf" else touch "/etc/audit/auditd.conf" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/audit/auditd.conf" cp "/etc/audit/auditd.conf" "/etc/audit/auditd.conf.bak" # Insert at the end of the file printf '%s\n' "name_format = $var_auditd_name_format" >> "/etc/audit/auditd.conf" # Clean up after ourselves. rm "/etc/audit/auditd.conf.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi ) # END fix for 'xccdf_org.ssgproject.content_rule_auditd_name_format'