Server IP : 184.154.167.98 / Your IP : 18.189.157.91 Web Server : Apache System : Linux pink.dnsnetservice.com 4.18.0-553.22.1.lve.1.el8.x86_64 #1 SMP Tue Oct 8 15:52:54 UTC 2024 x86_64 User : puertode ( 1767) PHP Version : 8.2.26 Disable Function : NONE MySQL : OFF | cURL : ON | WGET : ON | Perl : ON | Python : ON | Sudo : ON | Pkexec : ON Directory : /usr/share/scap-security-guide/bash/ |
Upload File : |
#!/usr/bin/env bash ############################################################################### # # Bash Remediation Script for Australian Cyber Security Centre (ACSC) ISM Official # # Profile Description: # This profile contains configuration checks for AlmaLinux 8 # that align to the Australian Cyber Security Centre (ACSC) Information Security Manual (ISM) # with the applicability marking of OFFICIAL. # The ISM uses a risk-based approach to cyber security. This profile provides a guide to aligning # AlmaLinux security controls with the ISM, which can be used to select controls # specific to an organisation's security posture and risk profile. # A copy of the ISM can be found at the ACSC website: # https://www.cyber.gov.au/ism # # Profile ID: xccdf_org.ssgproject.content_profile_ism_o # Benchmark ID: xccdf_org.ssgproject.content_benchmark_ALMALINUX-8 # Benchmark Version: 0.1.75 # XCCDF Version: 1.2 # # This file can be generated by OpenSCAP using: # $ oscap xccdf generate fix --profile xccdf_org.ssgproject.content_profile_ism_o --fix-type bash ssg-almalinux8-ds.xml # # This Bash Remediation Script is generated from an XCCDF 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 / 150) for 'xccdf_org.ssgproject.content_rule_rpm_verify_hashes' ############################################################################### (>&2 echo "Remediating rule 1/150: 'xccdf_org.ssgproject.content_rule_rpm_verify_hashes'") # 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' ' ')" yum reinstall -y $packages_to_reinstall fi # END fix for 'xccdf_org.ssgproject.content_rule_rpm_verify_hashes' ############################################################################### # BEGIN fix (2 / 150) for 'xccdf_org.ssgproject.content_rule_rpm_verify_ownership' ############################################################################### (>&2 echo "Remediating rule 2/150: 'xccdf_org.ssgproject.content_rule_rpm_verify_ownership'") # 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 # END fix for 'xccdf_org.ssgproject.content_rule_rpm_verify_ownership' ############################################################################### # BEGIN fix (3 / 150) for 'xccdf_org.ssgproject.content_rule_rpm_verify_permissions' ############################################################################### (>&2 echo "Remediating rule 3/150: 'xccdf_org.ssgproject.content_rule_rpm_verify_permissions'") # 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,2,1)=="M") print $NF }') for FILE_PATH in "${FILES_WITH_INCORRECT_PERMS[@]}" do # NOTE: some files maybe controlled by more then one package readarray -t RPM_PACKAGES < <(rpm -qf "${FILE_PATH}") for RPM_PACKAGE in "${RPM_PACKAGES[@]}" do # Use an associative array to store packages as it's keys, not having to care about duplicates. SETPERMS_RPM_DICT["$RPM_PACKAGE"]=1 done 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 # END fix for 'xccdf_org.ssgproject.content_rule_rpm_verify_permissions' ############################################################################### # BEGIN fix (4 / 150) for 'xccdf_org.ssgproject.content_rule_package_aide_installed' ############################################################################### (>&2 echo "Remediating rule 4/150: 'xccdf_org.ssgproject.content_rule_package_aide_installed'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if ! rpm -q --quiet "aide" ; then yum 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 (5 / 150) for 'xccdf_org.ssgproject.content_rule_enable_fips_mode' ############################################################################### (>&2 echo "Remediating rule 5/150: 'xccdf_org.ssgproject.content_rule_enable_fips_mode'") # Remediation is applicable only in certain platforms if ( ! ( [ "${container:-}" == "bwrap-osbuild" ] ) && rpm --quiet -q kernel ); then var_system_crypto_policy='FIPS' fips-mode-setup --enable 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 else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_enable_fips_mode' ############################################################################### # BEGIN fix (6 / 150) for 'xccdf_org.ssgproject.content_rule_configure_crypto_policy' ############################################################################### (>&2 echo "Remediating rule 6/150: 'xccdf_org.ssgproject.content_rule_configure_crypto_policy'") var_system_crypto_policy='FIPS' 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 / 150) for 'xccdf_org.ssgproject.content_rule_configure_ssh_crypto_policy' ############################################################################### (>&2 echo "Remediating rule 7/150: 'xccdf_org.ssgproject.content_rule_configure_ssh_crypto_policy'") SSH_CONF="/etc/sysconfig/sshd" sed -i "/^\s*CRYPTO_POLICY.*$/Id" $SSH_CONF # END fix for 'xccdf_org.ssgproject.content_rule_configure_ssh_crypto_policy' ############################################################################### # BEGIN fix (8 / 150) for 'xccdf_org.ssgproject.content_rule_openssl_use_strong_entropy' ############################################################################### (>&2 echo "Remediating rule 8/150: 'xccdf_org.ssgproject.content_rule_openssl_use_strong_entropy'") cat > /etc/profile.d/openssl-rand.sh <<- 'EOM' # provide a default -rand /dev/random option to openssl commands that # support it # written inefficiently for maximum shell compatibility openssl() ( openssl_bin=/usr/bin/openssl case "$*" in # if user specified -rand, honor it *\ -rand\ *|*\ -help*) exec $openssl_bin "$@" ;; esac cmds=`$openssl_bin list -digest-commands -cipher-commands | tr '\n' ' '` for i in `$openssl_bin list -commands`; do if $openssl_bin list -options "$i" | grep -q '^rand '; then cmds=" $i $cmds" fi done case "$cmds" in *\ "$1"\ *) cmd="$1"; shift exec $openssl_bin "$cmd" -rand /dev/random "$@" ;; esac exec $openssl_bin "$@" ) EOM # END fix for 'xccdf_org.ssgproject.content_rule_openssl_use_strong_entropy' ############################################################################### # BEGIN fix (9 / 150) for 'xccdf_org.ssgproject.content_rule_package_sudo_installed' ############################################################################### (>&2 echo "Remediating rule 9/150: 'xccdf_org.ssgproject.content_rule_package_sudo_installed'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if ! rpm -q --quiet "sudo" ; then yum 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 (10 / 150) for 'xccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate' ############################################################################### (>&2 echo "Remediating rule 10/150: 'xccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate'") 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 # END fix for 'xccdf_org.ssgproject.content_rule_sudo_remove_no_authenticate' ############################################################################### # BEGIN fix (11 / 150) for 'xccdf_org.ssgproject.content_rule_sudo_remove_nopasswd' ############################################################################### (>&2 echo "Remediating rule 11/150: 'xccdf_org.ssgproject.content_rule_sudo_remove_nopasswd'") 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 # END fix for 'xccdf_org.ssgproject.content_rule_sudo_remove_nopasswd' ############################################################################### # BEGIN fix (12 / 150) for 'xccdf_org.ssgproject.content_rule_sudo_require_authentication' ############################################################################### (>&2 echo "Remediating rule 12/150: 'xccdf_org.ssgproject.content_rule_sudo_require_authentication'") 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 # END fix for 'xccdf_org.ssgproject.content_rule_sudo_require_authentication' ############################################################################### # BEGIN fix (13 / 150) for 'xccdf_org.ssgproject.content_rule_package_rear_installed' ############################################################################### (>&2 echo "Remediating rule 13/150: 'xccdf_org.ssgproject.content_rule_package_rear_installed'") # Remediation is applicable only in certain platforms if ! ( ( ( grep -q aarch64 /proc/sys/kernel/osrelease && grep -qP "^ID=[\"']?ol[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="9.0"; printf "%s\n%s" "$expected" "$real" | sort -VC; } ) || ( grep -q aarch64 /proc/sys/kernel/osrelease && grep -qP "^ID=[\"']?rhel[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="9.0"; printf "%s\n%s" "$expected" "$real" | sort -VC; } ) || ( grep -qP "^ID=[\"']?rhel[\"']?$" "/etc/os-release" && { real="$(grep -P "^VERSION_ID=[\"']?[\w.]+[\"']?$" /etc/os-release | sed "s/^VERSION_ID=[\"']\?\([^\"']\+\)[\"']\?$/\1/")"; expected="8.4"; printf "%s\n%s" "$real" "$expected" | sort -VC; } && grep -q s390x /proc/sys/kernel/osrelease ) ) ); then if ! rpm -q --quiet "rear" ; then yum install -y "rear" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_package_rear_installed' ############################################################################### # BEGIN fix (14 / 150) for 'xccdf_org.ssgproject.content_rule_dnf-automatic_security_updates_only' ############################################################################### (>&2 echo "Remediating rule 14/150: 'xccdf_org.ssgproject.content_rule_dnf-automatic_security_updates_only'") found=false # set value in all files if they contain section or key for f in $(echo -n "/etc/dnf/automatic.conf"); do if [ ! -e "$f" ]; then continue fi # find key in section and change value if grep -qzosP "[[:space:]]*\[commands\]([^\n\[]*\n+)+?[[:space:]]*upgrade_type" "$f"; then sed -i "s/upgrade_type[^(\n)]*/upgrade_type=security/" "$f" found=true # find section and add key = value to it elif grep -qs "[[:space:]]*\[commands\]" "$f"; then sed -i "/[[:space:]]*\[commands\]/a upgrade_type=security" "$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/dnf/automatic.conf" | cut -f1 -d ' ') mkdir -p "$(dirname "$file")" echo -e "[commands]\nupgrade_type=security" >> "$file" fi # END fix for 'xccdf_org.ssgproject.content_rule_dnf-automatic_security_updates_only' ############################################################################### # BEGIN fix (15 / 150) for 'xccdf_org.ssgproject.content_rule_ensure_almalinux_gpgkey_installed' ############################################################################### (>&2 echo "Remediating rule 15/150: 'xccdf_org.ssgproject.content_rule_ensure_almalinux_gpgkey_installed'") readonly ALMALINUX_FINGERPRINT="5E9B8F5617B5066CE92057C3488FCF7C3ABB34F8" readonly ALMALINUX_AUXILIARY_FINGERPRINT="BC5EDDCADF502C077F1582882AE81E8ACED7258B" # Location of the key we would like to import (once it's integrity verified) readonly ALMALINUX_RELEASE_KEY="/etc/pki/rpm-gpg/RPM-GPG-KEY-AlmaLinux" RPM_GPG_DIR_PERMS=$(stat -c %a "$(dirname "$ALMALINUX_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 # Filter just hexadecimal fingerprints from gpg's output from # processing of a key file echo "${GPG_OUT[*]}" | grep -vE "${ALMALINUX_FINGERPRINT}|${ALMALINUX_AUXILIARY_FINGERPRINT}" || { # If $ALMALINUX_RELEASE_KEY file doesn't contain any keys with unknown fingerprint, import it rpm --import "${ALMALINUX_RELEASE_KEY}" } fi fi # END fix for 'xccdf_org.ssgproject.content_rule_ensure_almalinux_gpgkey_installed' ############################################################################### # BEGIN fix (16 / 150) for 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_globally_activated' ############################################################################### (>&2 echo "Remediating rule 16/150: 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_globally_activated'") # Remediation is applicable only in certain platforms if rpm --quiet -q yum; 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/yum.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/yum.conf" else if [[ -s "/etc/yum.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/yum.conf" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/yum.conf" fi printf '%s\n' "$formatted_output" >> "/etc/yum.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 (17 / 150) for 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_local_packages' ############################################################################### (>&2 echo "Remediating rule 17/150: 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_local_packages'") # Remediation is applicable only in certain platforms if rpm --quiet -q yum; 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' <<< "^localpkg_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 "^localpkg_gpgcheck\\>" "/etc/yum.conf"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^localpkg_gpgcheck\\>.*/$escaped_formatted_output/gi" "/etc/yum.conf" else if [[ -s "/etc/yum.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/yum.conf" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/yum.conf" fi printf '%s\n' "$formatted_output" >> "/etc/yum.conf" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_local_packages' ############################################################################### # BEGIN fix (18 / 150) for 'xccdf_org.ssgproject.content_rule_ensure_gpgcheck_never_disabled' ############################################################################### (>&2 echo "Remediating rule 18/150: '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 (19 / 150) for 'xccdf_org.ssgproject.content_rule_security_patches_up_to_date' ############################################################################### (>&2 echo "Remediating rule 19/150: '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 (20 / 150) for 'xccdf_org.ssgproject.content_rule_enable_authselect' ############################################################################### (>&2 echo "Remediating rule 20/150: 'xccdf_org.ssgproject.content_rule_enable_authselect'") var_authselect_profile='sssd' authselect current if test "$?" -ne 0; then authselect select "$var_authselect_profile" 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 # END fix for 'xccdf_org.ssgproject.content_rule_enable_authselect' ############################################################################### # BEGIN fix (21 / 150) for 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny' ############################################################################### (>&2 echo "Remediating rule 21/150: 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny'") # Remediation is applicable only in certain platforms if rpm --quiet -q pam; then var_accounts_passwords_pam_faillock_deny='3' 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") FAILLOCK_CONF="/etc/security/faillock.conf" if [ -f $FAILLOCK_CONF ]; 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 }') 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 (preauth|authfail).*deny' "$pam_file"; then sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ 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.*silent.*\)\('"deny"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_deny"'\3/' "$pam_file" sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"deny"'=\)[0-9]\+\(.*\)/\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 (22 / 150) for 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny_root' ############################################################################### (>&2 echo "Remediating rule 22/150: 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_deny_root'") # Remediation is applicable only in certain platforms if rpm --quiet -q pam; 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 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") FAILLOCK_CONF="/etc/security/faillock.conf" if [ -f $FAILLOCK_CONF ]; then regex="^\s*even_deny_root" line="even_deny_root" if ! grep -q $regex $FAILLOCK_CONF; then echo $line >> $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 }') 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.*\beven_deny_root\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\beven_deny_root\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 (preauth|authfail).*even_deny_root' "$pam_file"; then sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ even_deny_root/' "$pam_file" sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ even_deny_root/' "$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_root' ############################################################################### # BEGIN fix (23 / 150) for 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_interval' ############################################################################### (>&2 echo "Remediating rule 23/150: 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_interval'") # Remediation is applicable only in certain platforms if rpm --quiet -q pam; then var_accounts_passwords_pam_faillock_fail_interval='900' 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") FAILLOCK_CONF="/etc/security/faillock.conf" if [ -f $FAILLOCK_CONF ]; then regex="^\s*fail_interval\s*=" line="fail_interval = $var_accounts_passwords_pam_faillock_fail_interval" if ! grep -q $regex $FAILLOCK_CONF; then echo $line >> $FAILLOCK_CONF else sed -i --follow-symlinks 's|^\s*\(fail_interval\s*=\s*\)\(\S\+\)|\1'"$var_accounts_passwords_pam_faillock_fail_interval"'|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 }') 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.*\bfail_interval\b" "$PAM_FILE_PATH"; then sed -i -E --follow-symlinks "s/(.*auth.*pam_faillock.so.*)\bfail_interval\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 (preauth|authfail).*fail_interval' "$pam_file"; then sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file" sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*authfail.*/ s/$/ fail_interval='"$var_accounts_passwords_pam_faillock_fail_interval"'/' "$pam_file" else sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*preauth.*silent.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\3/' "$pam_file" sed -i --follow-symlinks 's/\(^auth.*required.*pam_faillock\.so.*authfail.*\)\('"fail_interval"'=\)[0-9]\+\(.*\)/\1\2'"$var_accounts_passwords_pam_faillock_fail_interval"'\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_interval' ############################################################################### # BEGIN fix (24 / 150) for 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_unlock_time' ############################################################################### (>&2 echo "Remediating rule 24/150: 'xccdf_org.ssgproject.content_rule_accounts_passwords_pam_faillock_unlock_time'") # Remediation is applicable only in certain platforms if rpm --quiet -q pam; then var_accounts_passwords_pam_faillock_unlock_time='0' 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") FAILLOCK_CONF="/etc/security/faillock.conf" if [ -f $FAILLOCK_CONF ]; 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 }') 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 (preauth|authfail).*unlock_time' "$pam_file"; then sed -i --follow-symlinks '/^auth.*required.*pam_faillock\.so.*preauth.*silent.*/ 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.*silent.*\)\('"unlock_time"'=\)[0-9]\+\(.*\)/\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"'=\)[0-9]\+\(.*\)/\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 (25 / 150) for 'xccdf_org.ssgproject.content_rule_accounts_password_pam_minlen' ############################################################################### (>&2 echo "Remediating rule 25/150: 'xccdf_org.ssgproject.content_rule_accounts_password_pam_minlen'") # Remediation is applicable only in certain platforms if rpm --quiet -q pam; then var_password_pam_minlen='14' # 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 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 (26 / 150) for 'xccdf_org.ssgproject.content_rule_require_emergency_target_auth' ############################################################################### (>&2 echo "Remediating rule 26/150: 'xccdf_org.ssgproject.content_rule_require_emergency_target_auth'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then service_file="/usr/lib/systemd/system/emergency.service" sulogin='/bin/sh -c "/sbin/sulogin; /usr/bin/systemctl --fail --no-block default"' if grep "^ExecStart=.*" "$service_file" ; then sed -i "s%^ExecStart=.*%ExecStart=-$sulogin%" "$service_file" else echo "ExecStart=-$sulogin" >> "$service_file" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_require_emergency_target_auth' ############################################################################### # BEGIN fix (27 / 150) for 'xccdf_org.ssgproject.content_rule_require_singleuser_auth' ############################################################################### (>&2 echo "Remediating rule 27/150: 'xccdf_org.ssgproject.content_rule_require_singleuser_auth'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then found=false # set value in all files if they contain section or key for f in $(echo -n "/etc/systemd/system/rescue.service.d/10-oscap.conf"); do if [ ! -e "$f" ]; then continue fi # find key in section and change value if grep -qzosP "[[:space:]]*\[Service\]([^\n\[]*\n+)+?[[:space:]]*ExecStart" "$f"; then sed -i "s/ExecStart[^(\n)]*/ExecStart=-/bin/sh -c "/sbin/sulogin; /usr/bin/systemctl --fail --no-block default"/" "$f" found=true # find section and add key = value to it elif grep -qs "[[:space:]]*\[Service\]" "$f"; then sed -i "/[[:space:]]*\[Service\]/a ExecStart=-/bin/sh -c "/sbin/sulogin; /usr/bin/systemctl --fail --no-block default"" "$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/system/rescue.service.d/10-oscap.conf" | cut -f1 -d ' ') mkdir -p "$(dirname "$file")" echo -e "[Service]\nExecStart=-/bin/sh -c "/sbin/sulogin; /usr/bin/systemctl --fail --no-block default"" >> "$file" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_require_singleuser_auth' ############################################################################### # BEGIN fix (28 / 150) for 'xccdf_org.ssgproject.content_rule_accounts_maximum_age_login_defs' ############################################################################### (>&2 echo "Remediating rule 28/150: 'xccdf_org.ssgproject.content_rule_accounts_maximum_age_login_defs'") # Remediation is applicable only in certain platforms if rpm --quiet -q shadow-utils; then var_accounts_maximum_age_login_defs='60' # 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 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 (29 / 150) for 'xccdf_org.ssgproject.content_rule_accounts_minimum_age_login_defs' ############################################################################### (>&2 echo "Remediating rule 29/150: 'xccdf_org.ssgproject.content_rule_accounts_minimum_age_login_defs'") # Remediation is applicable only in certain platforms if rpm --quiet -q shadow-utils; then var_accounts_minimum_age_login_defs='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' <<< "^PASS_MIN_DAYS") # shellcheck disable=SC2059 printf -v formatted_output "%s %s" "$stripped_key" "$var_accounts_minimum_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_MIN_DAYS\\>" "/etc/login.defs"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^PASS_MIN_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 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_minimum_age_login_defs' ############################################################################### # BEGIN fix (30 / 150) for 'xccdf_org.ssgproject.content_rule_accounts_password_warn_age_login_defs' ############################################################################### (>&2 echo "Remediating rule 30/150: 'xccdf_org.ssgproject.content_rule_accounts_password_warn_age_login_defs'") # Remediation is applicable only in certain platforms if 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 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 (31 / 150) for 'xccdf_org.ssgproject.content_rule_accounts_password_all_shadowed' ############################################################################### (>&2 echo "Remediating rule 31/150: '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 (32 / 150) for 'xccdf_org.ssgproject.content_rule_no_empty_passwords' ############################################################################### (>&2 echo "Remediating rule 32/150: 'xccdf_org.ssgproject.content_rule_no_empty_passwords'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; 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.*)\snullok=?[[: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.*)\snullok=?[[: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.*)\snullok=?[[: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.*)\snullok=?[[: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 (33 / 150) for 'xccdf_org.ssgproject.content_rule_accounts_no_uid_except_zero' ############################################################################### (>&2 echo "Remediating rule 33/150: 'xccdf_org.ssgproject.content_rule_accounts_no_uid_except_zero'") awk -F: '$3 == 0 && $1 != "root" { print $1 }' /etc/passwd | xargs --no-run-if-empty --max-lines=1 passwd -l # END fix for 'xccdf_org.ssgproject.content_rule_accounts_no_uid_except_zero' ############################################################################### # BEGIN fix (34 / 150) for 'xccdf_org.ssgproject.content_rule_no_shelllogin_for_systemaccounts' ############################################################################### (>&2 echo "Remediating rule 34/150: 'xccdf_org.ssgproject.content_rule_no_shelllogin_for_systemaccounts'") 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 # END fix for 'xccdf_org.ssgproject.content_rule_no_shelllogin_for_systemaccounts' ############################################################################### # BEGIN fix (35 / 150) for 'xccdf_org.ssgproject.content_rule_package_rsyslog_installed' ############################################################################### (>&2 echo "Remediating rule 35/150: 'xccdf_org.ssgproject.content_rule_package_rsyslog_installed'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if ! rpm -q --quiet "rsyslog" ; then yum install -y "rsyslog" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_package_rsyslog_installed' ############################################################################### # BEGIN fix (36 / 150) for 'xccdf_org.ssgproject.content_rule_service_rsyslog_enabled' ############################################################################### (>&2 echo "Remediating rule 36/150: 'xccdf_org.ssgproject.content_rule_service_rsyslog_enabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" unmask 'rsyslog.service' "$SYSTEMCTL_EXEC" start 'rsyslog.service' "$SYSTEMCTL_EXEC" enable 'rsyslog.service' else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_service_rsyslog_enabled' ############################################################################### # BEGIN fix (37 / 150) for 'xccdf_org.ssgproject.content_rule_rsyslog_cron_logging' ############################################################################### (>&2 echo "Remediating rule 37/150: 'xccdf_org.ssgproject.content_rule_rsyslog_cron_logging'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if ! grep -s "^\s*cron\.\*\s*/var/log/cron$" /etc/rsyslog.conf /etc/rsyslog.d/*.conf; then mkdir -p /etc/rsyslog.d echo "cron.* /var/log/cron" >> /etc/rsyslog.d/cron.conf fi if [[ "$OSCAP_BOOTC_BUILD" != "YES" ]] ; then systemctl restart rsyslog.service fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_rsyslog_cron_logging' ############################################################################### # BEGIN fix (38 / 150) for 'xccdf_org.ssgproject.content_rule_rsyslog_files_groupownership' ############################################################################### (>&2 echo "Remediating rule 38/150: 'xccdf_org.ssgproject.content_rule_rsyslog_files_groupownership'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; 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 (39 / 150) for 'xccdf_org.ssgproject.content_rule_rsyslog_files_ownership' ############################################################################### (>&2 echo "Remediating rule 39/150: 'xccdf_org.ssgproject.content_rule_rsyslog_files_ownership'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; 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 (40 / 150) for 'xccdf_org.ssgproject.content_rule_rsyslog_files_permissions' ############################################################################### (>&2 echo "Remediating rule 40/150: 'xccdf_org.ssgproject.content_rule_rsyslog_files_permissions'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; 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 (41 / 150) for 'xccdf_org.ssgproject.content_rule_rsyslog_nolisten' ############################################################################### (>&2 echo "Remediating rule 41/150: 'xccdf_org.ssgproject.content_rule_rsyslog_nolisten'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then legacy_regex='^\s*\$(((Input(TCP|RELP)|UDP)ServerRun)|ModLoad\s+(imtcp|imudp|imrelp))' rainer_regex='^\s*(module|input)\((load|type)="(imtcp|imudp)".*$' readarray -t legacy_targets < <(grep -l -E -r "${legacy_regex[@]}" /etc/rsyslog.conf /etc/rsyslog.d/) readarray -t rainer_targets < <(grep -l -E -r "${rainer_regex[@]}" /etc/rsyslog.conf /etc/rsyslog.d/) config_changed=false if [ ${#legacy_targets[@]} -gt 0 ]; then for target in "${legacy_targets[@]}"; do sed -E -i "/$legacy_regex/ s/^/# /" "$target" done config_changed=true fi if [ ${#rainer_targets[@]} -gt 0 ]; then for target in "${rainer_targets[@]}"; do sed -E -i "/$rainer_regex/ s/^/# /" "$target" done config_changed=true fi if $config_changed; then systemctl restart rsyslog.service fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_rsyslog_nolisten' ############################################################################### # BEGIN fix (42 / 150) for 'xccdf_org.ssgproject.content_rule_rsyslog_remote_loghost' ############################################################################### (>&2 echo "Remediating rule 42/150: 'xccdf_org.ssgproject.content_rule_rsyslog_remote_loghost'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then rsyslog_remote_loghost_address='logcollector' # 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' <<< "^\*\.\*") # shellcheck disable=SC2059 printf -v formatted_output "%s %s" "$stripped_key" "@@$rsyslog_remote_loghost_address" # 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 "^\*\.\*\\>" "/etc/rsyslog.conf"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^\*\.\*\\>.*/$escaped_formatted_output/gi" "/etc/rsyslog.conf" else if [[ -s "/etc/rsyslog.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/rsyslog.conf" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/rsyslog.conf" fi printf '%s\n' "$formatted_output" >> "/etc/rsyslog.conf" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_rsyslog_remote_loghost' ############################################################################### # BEGIN fix (43 / 150) for 'xccdf_org.ssgproject.content_rule_rsyslog_remote_tls' ############################################################################### (>&2 echo "Remediating rule 43/150: 'xccdf_org.ssgproject.content_rule_rsyslog_remote_tls'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then rsyslog_remote_loghost_address='logcollector' params_to_add_if_missing=("protocol" "target" "port" "StreamDriver" "StreamDriverMode" "StreamDriverAuthMode" "streamdriver.CheckExtendedKeyPurpose") values_to_add_if_missing=("tcp" "$rsyslog_remote_loghost_address" "6514" "gtls" "1" "x509/name" "on") params_to_replace_if_wrong_value=("protocol" "StreamDriver" "StreamDriverMode" "StreamDriverAuthMode" "streamdriver.CheckExtendedKeyPurpose") values_to_replace_if_wrong_value=("tcp" "gtls" "1" "x509/name" "on") files_containing_omfwd=("$(grep -ilE '^[^#]*\s*action\s*\(\s*type\s*=\s*"omfwd".*' /etc/rsyslog.conf /etc/rsyslog.d/*.conf)") if [ -n "${files_containing_omfwd[*]}" ]; then for file in "${files_containing_omfwd[@]}"; do for ((i=0; i<${#params_to_replace_if_wrong_value[@]}; i++)); do sed -i -E -e 'H;$!d;x;s/^\n//' -e "s|(\s*action\s*\(\s*type\s*=\s*[\"]omfwd[\"].*?)${params_to_replace_if_wrong_value[$i]}\s*=\s*[\"]\S*[\"](.*\))|\1${params_to_replace_if_wrong_value[$i]}=\"${values_to_replace_if_wrong_value[$i]}\"\2|gI" "$file" done for ((i=0; i<${#params_to_add_if_missing[@]}; i++)); do if ! grep -qPzi "(?s)\s*action\s*\(\s*type\s*=\s*[\"]omfwd[\"].*?${params_to_add_if_missing[$i]}.*?\).*" "$file"; then sed -i -E -e 'H;$!d;x;s/^\n//' -e "s|(\s*action\s*\(\s*type\s*=\s*[\"]omfwd[\"])|\1\n${params_to_add_if_missing[$i]}=\"${values_to_add_if_missing[$i]}\"|gI" "$file" fi done done else echo "action(type=\"omfwd\" protocol=\"tcp\" Target=\"$rsyslog_remote_loghost_address\" port=\"6514\" StreamDriver=\"gtls\" StreamDriverMode=\"1\" StreamDriverAuthMode=\"x509/name\" streamdriver.CheckExtendedKeyPurpose=\"on\")" >> /etc/rsyslog.conf fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_rsyslog_remote_tls' ############################################################################### # BEGIN fix (44 / 150) for 'xccdf_org.ssgproject.content_rule_rsyslog_remote_tls_cacert' ############################################################################### (>&2 echo "Remediating rule 44/150: 'xccdf_org.ssgproject.content_rule_rsyslog_remote_tls_cacert'") (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_rsyslog_remote_tls_cacert' IS MISSING!") # END fix for 'xccdf_org.ssgproject.content_rule_rsyslog_remote_tls_cacert' ############################################################################### # BEGIN fix (45 / 150) for 'xccdf_org.ssgproject.content_rule_network_nmcli_permissions' ############################################################################### (>&2 echo "Remediating rule 45/150: 'xccdf_org.ssgproject.content_rule_network_nmcli_permissions'") # Remediation is applicable only in certain platforms if rpm --quiet -q polkit; then 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 (46 / 150) for 'xccdf_org.ssgproject.content_rule_network_sniffer_disabled' ############################################################################### (>&2 echo "Remediating rule 46/150: '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 (47 / 150) for 'xccdf_org.ssgproject.content_rule_package_firewalld_installed' ############################################################################### (>&2 echo "Remediating rule 47/150: 'xccdf_org.ssgproject.content_rule_package_firewalld_installed'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if ! rpm -q --quiet "firewalld" ; then yum 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 (48 / 150) for 'xccdf_org.ssgproject.content_rule_service_firewalld_enabled' ############################################################################### (>&2 echo "Remediating rule 48/150: 'xccdf_org.ssgproject.content_rule_service_firewalld_enabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel && { rpm --quiet -q firewalld; }; then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" unmask 'firewalld.service' "$SYSTEMCTL_EXEC" start 'firewalld.service' "$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 (49 / 150) for 'xccdf_org.ssgproject.content_rule_configure_firewalld_ports' ############################################################################### (>&2 echo "Remediating rule 49/150: '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 (50 / 150) for 'xccdf_org.ssgproject.content_rule_set_firewalld_default_zone' ############################################################################### (>&2 echo "Remediating rule 50/150: '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 (51 / 150) for 'xccdf_org.ssgproject.content_rule_wireless_disable_interfaces' ############################################################################### (>&2 echo "Remediating rule 51/150: 'xccdf_org.ssgproject.content_rule_wireless_disable_interfaces'") if ! rpm -q --quiet "NetworkManager" ; then yum 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 # END fix for 'xccdf_org.ssgproject.content_rule_wireless_disable_interfaces' ############################################################################### # BEGIN fix (52 / 150) for 'xccdf_org.ssgproject.content_rule_dir_perms_world_writable_sticky_bits' ############################################################################### (>&2 echo "Remediating rule 52/150: '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 (53 / 150) for 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_sgid' ############################################################################### (>&2 echo "Remediating rule 53/150: 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_sgid'") (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_sgid' IS MISSING!") # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_sgid' ############################################################################### # BEGIN fix (54 / 150) for 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_suid' ############################################################################### (>&2 echo "Remediating rule 54/150: 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_suid'") (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_suid' IS MISSING!") # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_suid' ############################################################################### # BEGIN fix (55 / 150) for 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_world_writable' ############################################################################### (>&2 echo "Remediating rule 55/150: 'xccdf_org.ssgproject.content_rule_file_permissions_unauthorized_world_writable'") FILTER_NODEV=$(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) PARTITIONS=$(findmnt -n -l -k -it $FILTER_NODEV | awk '{ print $1 }') 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 whem 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 (56 / 150) for 'xccdf_org.ssgproject.content_rule_file_ownership_binary_dirs' ############################################################################### (>&2 echo "Remediating rule 56/150: 'xccdf_org.ssgproject.content_rule_file_ownership_binary_dirs'") find /bin/ \ /usr/bin/ \ /usr/local/bin/ \ /sbin/ \ /usr/sbin/ \ /usr/local/sbin/ \ /usr/libexec \ \! -user root -execdir chown root {} \; # END fix for 'xccdf_org.ssgproject.content_rule_file_ownership_binary_dirs' ############################################################################### # BEGIN fix (57 / 150) for 'xccdf_org.ssgproject.content_rule_file_ownership_library_dirs' ############################################################################### (>&2 echo "Remediating rule 57/150: 'xccdf_org.ssgproject.content_rule_file_ownership_library_dirs'") find /lib/ -type f ! -uid 0 -regextype posix-extended -regex '^.*$' -exec chown -L 0 {} \; find /lib64/ -type f ! -uid 0 -regextype posix-extended -regex '^.*$' -exec chown -L 0 {} \; find /usr/lib/ -type f ! -uid 0 -regextype posix-extended -regex '^.*$' -exec chown -L 0 {} \; find /usr/lib64/ -type f ! -uid 0 -regextype posix-extended -regex '^.*$' -exec chown -L 0 {} \; # END fix for 'xccdf_org.ssgproject.content_rule_file_ownership_library_dirs' ############################################################################### # BEGIN fix (58 / 150) for 'xccdf_org.ssgproject.content_rule_file_permissions_binary_dirs' ############################################################################### (>&2 echo "Remediating rule 58/150: 'xccdf_org.ssgproject.content_rule_file_permissions_binary_dirs'") DIRS="/bin /usr/bin /usr/local/bin /sbin /usr/sbin /usr/local/sbin /usr/libexec" for dirPath in $DIRS; do find "$dirPath" -perm /022 -exec chmod go-w '{}' \; done # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_binary_dirs' ############################################################################### # BEGIN fix (59 / 150) for 'xccdf_org.ssgproject.content_rule_file_permissions_library_dirs' ############################################################################### (>&2 echo "Remediating rule 59/150: 'xccdf_org.ssgproject.content_rule_file_permissions_library_dirs'") find /lib/ -perm /g+w,o+w -type f -regextype posix-extended -regex '^.*$' -exec chmod g-w,o-w {} \; find /lib64/ -perm /g+w,o+w -type f -regextype posix-extended -regex '^.*$' -exec chmod g-w,o-w {} \; find /usr/lib/ -perm /g+w,o+w -type f -regextype posix-extended -regex '^.*$' -exec chmod g-w,o-w {} \; find /usr/lib64/ -perm /g+w,o+w -type f -regextype posix-extended -regex '^.*$' -exec chmod g-w,o-w {} \; # END fix for 'xccdf_org.ssgproject.content_rule_file_permissions_library_dirs' ############################################################################### # BEGIN fix (60 / 150) for 'xccdf_org.ssgproject.content_rule_mount_option_dev_shm_nodev' ############################################################################### (>&2 echo "Remediating rule 60/150: 'xccdf_org.ssgproject.content_rule_mount_option_dev_shm_nodev'") # Remediation is applicable only in certain platforms if ( ! ( { rpm --quiet -q kernel ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} ) && ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ); then function perform_remediation { mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" /dev/shm)" # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab if ! grep -q "$mount_point_match_regexp" /etc/fstab; then # runtime opts without some automatic kernel/userspace-added defaults previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ | sed -E "s/(rw|defaults|seclabel|nodev)(,|$)//g;s/,$//") [ "$previous_mount_opts" ] && previous_mount_opts+="," # In iso9660 filesystems mtab could describe a "blocksize" value, this should be reflected in # fstab as "block". The next variable is to satisfy shellcheck SC2050. fs_type="tmpfs" if [ "$fs_type" == "iso9660" ] ; then previous_mount_opts=$(sed 's/blocksize=/block=/' <<< "$previous_mount_opts") fi echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}nodev 0 0" >> /etc/fstab # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it elif ! grep "$mount_point_match_regexp" /etc/fstab | grep -q "nodev"; then previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nodev|" /etc/fstab fi if mkdir -p "/dev/shm"; then if mountpoint -q "/dev/shm"; then mount -o remount --target "/dev/shm" fi fi } perform_remediation else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_mount_option_dev_shm_nodev' ############################################################################### # BEGIN fix (61 / 150) for 'xccdf_org.ssgproject.content_rule_mount_option_dev_shm_noexec' ############################################################################### (>&2 echo "Remediating rule 61/150: 'xccdf_org.ssgproject.content_rule_mount_option_dev_shm_noexec'") # Remediation is applicable only in certain platforms if ( ! ( { rpm --quiet -q kernel ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} ) && ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ); then function perform_remediation { mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" /dev/shm)" # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab if ! grep -q "$mount_point_match_regexp" /etc/fstab; then # runtime opts without some automatic kernel/userspace-added defaults previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ | sed -E "s/(rw|defaults|seclabel|noexec)(,|$)//g;s/,$//") [ "$previous_mount_opts" ] && previous_mount_opts+="," # In iso9660 filesystems mtab could describe a "blocksize" value, this should be reflected in # fstab as "block". The next variable is to satisfy shellcheck SC2050. fs_type="tmpfs" if [ "$fs_type" == "iso9660" ] ; then previous_mount_opts=$(sed 's/blocksize=/block=/' <<< "$previous_mount_opts") fi echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}noexec 0 0" >> /etc/fstab # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it elif ! grep "$mount_point_match_regexp" /etc/fstab | grep -q "noexec"; then previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,noexec|" /etc/fstab fi if mkdir -p "/dev/shm"; then if mountpoint -q "/dev/shm"; then mount -o remount --target "/dev/shm" fi fi } perform_remediation else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_mount_option_dev_shm_noexec' ############################################################################### # BEGIN fix (62 / 150) for 'xccdf_org.ssgproject.content_rule_mount_option_dev_shm_nosuid' ############################################################################### (>&2 echo "Remediating rule 62/150: 'xccdf_org.ssgproject.content_rule_mount_option_dev_shm_nosuid'") # Remediation is applicable only in certain platforms if ( ! ( { rpm --quiet -q kernel ;} && { rpm --quiet -q rpm-ostree ;} && { rpm --quiet -q bootc ;} ) && ! ( [ -f /.dockerenv ] || [ -f /run/.containerenv ] ) ); then function perform_remediation { mount_point_match_regexp="$(printf "^[[:space:]]*[^#].*[[:space:]]%s[[:space:]]" /dev/shm)" # If the mount point is not in /etc/fstab, get previous mount options from /etc/mtab if ! grep -q "$mount_point_match_regexp" /etc/fstab; then # runtime opts without some automatic kernel/userspace-added defaults previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/mtab | head -1 | awk '{print $4}' \ | sed -E "s/(rw|defaults|seclabel|nosuid)(,|$)//g;s/,$//") [ "$previous_mount_opts" ] && previous_mount_opts+="," # In iso9660 filesystems mtab could describe a "blocksize" value, this should be reflected in # fstab as "block". The next variable is to satisfy shellcheck SC2050. fs_type="tmpfs" if [ "$fs_type" == "iso9660" ] ; then previous_mount_opts=$(sed 's/blocksize=/block=/' <<< "$previous_mount_opts") fi echo "tmpfs /dev/shm tmpfs defaults,${previous_mount_opts}nosuid 0 0" >> /etc/fstab # If the mount_opt option is not already in the mount point's /etc/fstab entry, add it elif ! grep "$mount_point_match_regexp" /etc/fstab | grep -q "nosuid"; then previous_mount_opts=$(grep "$mount_point_match_regexp" /etc/fstab | awk '{print $4}') sed -i "s|\(${mount_point_match_regexp}.*${previous_mount_opts}\)|\1,nosuid|" /etc/fstab fi if mkdir -p "/dev/shm"; then if mountpoint -q "/dev/shm"; then mount -o remount --target "/dev/shm" fi fi } perform_remediation else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_mount_option_dev_shm_nosuid' ############################################################################### # BEGIN fix (63 / 150) for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_dmesg_restrict' ############################################################################### (>&2 echo "Remediating rule 63/150: 'xccdf_org.ssgproject.content_rule_sysctl_kernel_dmesg_restrict'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then # Comment out any occurrences of kernel.dmesg_restrict from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/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.dmesg_restrict.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "kernel.dmesg_restrict" 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.dmesg_restrict # /sbin/sysctl -q -n -w kernel.dmesg_restrict="1" # # If kernel.dmesg_restrict present in /etc/sysctl.conf, change value to "1" # else, add "kernel.dmesg_restrict = 1" 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.dmesg_restrict") # 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 "^kernel.dmesg_restrict\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^kernel.dmesg_restrict\\>.*/$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 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_dmesg_restrict' ############################################################################### # BEGIN fix (64 / 150) for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_kexec_load_disabled' ############################################################################### (>&2 echo "Remediating rule 64/150: 'xccdf_org.ssgproject.content_rule_sysctl_kernel_kexec_load_disabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then # Comment out any occurrences of kernel.kexec_load_disabled from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/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.kexec_load_disabled.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "kernel.kexec_load_disabled" 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.kexec_load_disabled # /sbin/sysctl -q -n -w kernel.kexec_load_disabled="1" # # If kernel.kexec_load_disabled present in /etc/sysctl.conf, change value to "1" # else, add "kernel.kexec_load_disabled = 1" 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.kexec_load_disabled") # 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 "^kernel.kexec_load_disabled\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^kernel.kexec_load_disabled\\>.*/$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 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_kexec_load_disabled' ############################################################################### # BEGIN fix (65 / 150) for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_unprivileged_bpf_disabled' ############################################################################### (>&2 echo "Remediating rule 65/150: 'xccdf_org.ssgproject.content_rule_sysctl_kernel_unprivileged_bpf_disabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then # Comment out any occurrences of kernel.unprivileged_bpf_disabled from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/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.unprivileged_bpf_disabled.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "kernel.unprivileged_bpf_disabled" 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.unprivileged_bpf_disabled # /sbin/sysctl -q -n -w kernel.unprivileged_bpf_disabled="1" # # If kernel.unprivileged_bpf_disabled present in /etc/sysctl.conf, change value to "1" # else, add "kernel.unprivileged_bpf_disabled = 1" 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.unprivileged_bpf_disabled") # 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 "^kernel.unprivileged_bpf_disabled\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^kernel.unprivileged_bpf_disabled\\>.*/$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 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_unprivileged_bpf_disabled' ############################################################################### # BEGIN fix (66 / 150) for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_yama_ptrace_scope' ############################################################################### (>&2 echo "Remediating rule 66/150: 'xccdf_org.ssgproject.content_rule_sysctl_kernel_yama_ptrace_scope'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then # Comment out any occurrences of kernel.yama.ptrace_scope from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/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.yama.ptrace_scope.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "kernel.yama.ptrace_scope" 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.yama.ptrace_scope # /sbin/sysctl -q -n -w kernel.yama.ptrace_scope="1" # # If kernel.yama.ptrace_scope present in /etc/sysctl.conf, change value to "1" # else, add "kernel.yama.ptrace_scope = 1" 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.yama.ptrace_scope") # 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 "^kernel.yama.ptrace_scope\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^kernel.yama.ptrace_scope\\>.*/$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 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_yama_ptrace_scope' ############################################################################### # BEGIN fix (67 / 150) for 'xccdf_org.ssgproject.content_rule_sysctl_net_core_bpf_jit_harden' ############################################################################### (>&2 echo "Remediating rule 67/150: 'xccdf_org.ssgproject.content_rule_sysctl_net_core_bpf_jit_harden'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then # Comment out any occurrences of net.core.bpf_jit_harden from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/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.core.bpf_jit_harden.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "net.core.bpf_jit_harden" 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.core.bpf_jit_harden # /sbin/sysctl -q -n -w net.core.bpf_jit_harden="2" # # If net.core.bpf_jit_harden present in /etc/sysctl.conf, change value to "2" # else, add "net.core.bpf_jit_harden = 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' <<< "^net.core.bpf_jit_harden") # 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 "^net.core.bpf_jit_harden\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^net.core.bpf_jit_harden\\>.*/$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 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_core_bpf_jit_harden' ############################################################################### # BEGIN fix (68 / 150) for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_exec_shield' ############################################################################### (>&2 echo "Remediating rule 68/150: 'xccdf_org.ssgproject.content_rule_sysctl_kernel_exec_shield'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ "$(getconf LONG_BIT)" = "32" ] ; then # # Set runtime for kernel.exec-shield # sysctl -q -n -w kernel.exec-shield=1 # # If kernel.exec-shield present in /etc/sysctl.conf, change value to "1" # else, add "kernel.exec-shield = 1" 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.exec-shield") # 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 "^kernel.exec-shield\\>" "/etc/sysctl.conf"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^kernel.exec-shield\\>.*/$escaped_formatted_output/gi" "/etc/sysctl.conf" else if [[ -s "/etc/sysctl.conf" ]] && [[ -n "$(tail -c 1 -- "/etc/sysctl.conf" || true)" ]]; then LC_ALL=C sed -i --follow-symlinks '$a'\\ "/etc/sysctl.conf" fi printf '%s\n' "$formatted_output" >> "/etc/sysctl.conf" fi fi if [ "$(getconf LONG_BIT)" = "64" ] ; then grubby --update-kernel=ALL --remove-args=noexec --env=/boot/grub2/grubenv fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_exec_shield' ############################################################################### # BEGIN fix (69 / 150) for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_kptr_restrict' ############################################################################### (>&2 echo "Remediating rule 69/150: 'xccdf_org.ssgproject.content_rule_sysctl_kernel_kptr_restrict'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then # Comment out any occurrences of kernel.kptr_restrict from /etc/sysctl.d/*.conf files for f in /etc/sysctl.d/*.conf /run/sysctl.d/*.conf /usr/local/lib/sysctl.d/*.conf /usr/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.kptr_restrict.*$' $f | uniq ) if ! test -z "$matching_list"; then while IFS= read -r entry; do escaped_entry=$(sed -e 's|/|\\/|g' <<< "$entry") # comment out "kernel.kptr_restrict" 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_kernel_kptr_restrict_value='1' # # Set runtime for kernel.kptr_restrict # /sbin/sysctl -q -n -w kernel.kptr_restrict="$sysctl_kernel_kptr_restrict_value" # # If kernel.kptr_restrict present in /etc/sysctl.conf, change value to appropriate value # else, add "kernel.kptr_restrict = 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' <<< "^kernel.kptr_restrict") # shellcheck disable=SC2059 printf -v formatted_output "%s = %s" "$stripped_key" "$sysctl_kernel_kptr_restrict_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 "^kernel.kptr_restrict\\>" "${SYSCONFIG_FILE}"; then escaped_formatted_output=$(sed -e 's|/|\\/|g' <<< "$formatted_output") LC_ALL=C sed -i --follow-symlinks "s/^kernel.kptr_restrict\\>.*/$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 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_kptr_restrict' ############################################################################### # BEGIN fix (70 / 150) for 'xccdf_org.ssgproject.content_rule_sysctl_kernel_randomize_va_space' ############################################################################### (>&2 echo "Remediating rule 70/150: 'xccdf_org.ssgproject.content_rule_sysctl_kernel_randomize_va_space'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; 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 /usr/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 # /sbin/sysctl -q -n -w kernel.randomize_va_space="2" # # 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 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 (71 / 150) for 'xccdf_org.ssgproject.content_rule_selinux_policytype' ############################################################################### (>&2 echo "Remediating rule 71/150: 'xccdf_org.ssgproject.content_rule_selinux_policytype'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; 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 (72 / 150) for 'xccdf_org.ssgproject.content_rule_selinux_state' ############################################################################### (>&2 echo "Remediating rule 72/150: 'xccdf_org.ssgproject.content_rule_selinux_state'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; 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 fixfiles -f relabel else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_selinux_state' ############################################################################### # BEGIN fix (73 / 150) for 'xccdf_org.ssgproject.content_rule_sebool_auditadm_exec_content' ############################################################################### (>&2 echo "Remediating rule 73/150: 'xccdf_org.ssgproject.content_rule_sebool_auditadm_exec_content'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if ! rpm -q --quiet "libsemanage-python" ; then yum install -y "libsemanage-python" fi if selinuxenabled || [[ "$OSCAP_BOOTC_BUILD" == "YES" ]] ; then var_auditadm_exec_content='true' setsebool -P auditadm_exec_content $var_auditadm_exec_content else echo "Skipping remediation, SELinux is disabled"; false fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_sebool_auditadm_exec_content' ############################################################################### # BEGIN fix (74 / 150) for 'xccdf_org.ssgproject.content_rule_service_avahi-daemon_disabled' ############################################################################### (>&2 echo "Remediating rule 74/150: '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 ); then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" stop 'avahi-daemon.service' "$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 "$SYSTEMCTL_EXEC" stop 'avahi-daemon.socket' "$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 (75 / 150) for 'xccdf_org.ssgproject.content_rule_package_fapolicyd_installed' ############################################################################### (>&2 echo "Remediating rule 75/150: 'xccdf_org.ssgproject.content_rule_package_fapolicyd_installed'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if ! rpm -q --quiet "fapolicyd" ; then yum install -y "fapolicyd" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_package_fapolicyd_installed' ############################################################################### # BEGIN fix (76 / 150) for 'xccdf_org.ssgproject.content_rule_service_fapolicyd_enabled' ############################################################################### (>&2 echo "Remediating rule 76/150: 'xccdf_org.ssgproject.content_rule_service_fapolicyd_enabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" unmask 'fapolicyd.service' "$SYSTEMCTL_EXEC" start 'fapolicyd.service' "$SYSTEMCTL_EXEC" enable 'fapolicyd.service' else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_service_fapolicyd_enabled' ############################################################################### # BEGIN fix (77 / 150) for 'xccdf_org.ssgproject.content_rule_package_chrony_installed' ############################################################################### (>&2 echo "Remediating rule 77/150: 'xccdf_org.ssgproject.content_rule_package_chrony_installed'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if ! rpm -q --quiet "chrony" ; then yum 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 (78 / 150) for 'xccdf_org.ssgproject.content_rule_service_chronyd_enabled' ############################################################################### (>&2 echo "Remediating rule 78/150: 'xccdf_org.ssgproject.content_rule_service_chronyd_enabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel && { rpm --quiet -q chrony; }; then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" unmask 'chronyd.service' "$SYSTEMCTL_EXEC" start 'chronyd.service' "$SYSTEMCTL_EXEC" enable 'chronyd.service' else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_service_chronyd_enabled' ############################################################################### # BEGIN fix (79 / 150) for 'xccdf_org.ssgproject.content_rule_service_chronyd_or_ntpd_enabled' ############################################################################### (>&2 echo "Remediating rule 79/150: 'xccdf_org.ssgproject.content_rule_service_chronyd_or_ntpd_enabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if rpm --quiet -q "chrony" ; then if ! /usr/sbin/pidof ntpd ; then /usr/bin/systemctl enable "chronyd" /usr/bin/systemctl start "chronyd" # 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. if /usr/bin/systemctl --failed | grep -q "chronyd"; then /usr/bin/systemctl reset-failed "chronyd" fi fi elif rpm --quiet -q "ntp" ; then /usr/bin/systemctl enable "ntpd" /usr/bin/systemctl start "ntpd" # 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. if /usr/bin/systemctl --failed | grep -q "ntpd"; then /usr/bin/systemctl reset-failed "ntpd" fi else if ! rpm -q --quiet "chrony" ; then yum install -y "chrony" fi /usr/bin/systemctl enable "chronyd" /usr/bin/systemctl start "chronyd" # 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. if /usr/bin/systemctl --failed | grep -q "chronyd"; then /usr/bin/systemctl reset-failed "chronyd" fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_service_chronyd_or_ntpd_enabled' ############################################################################### # BEGIN fix (80 / 150) for 'xccdf_org.ssgproject.content_rule_chronyd_specify_remote_server' ############################################################################### (>&2 echo "Remediating rule 80/150: 'xccdf_org.ssgproject.content_rule_chronyd_specify_remote_server'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel && { 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 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 (81 / 150) for 'xccdf_org.ssgproject.content_rule_chronyd_or_ntpd_specify_multiple_servers' ############################################################################### (>&2 echo "Remediating rule 81/150: 'xccdf_org.ssgproject.content_rule_chronyd_or_ntpd_specify_multiple_servers'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then var_multiple_time_servers='0.pool.ntp.org,1.pool.ntp.org,2.pool.ntp.org,3.pool.ntp.org' config_file="/etc/ntp.conf" /usr/sbin/pidof ntpd || config_file="/etc/chrony.conf" if ! [ "$(grep -c '^server' "$config_file")" -gt 1 ] ; 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 fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_chronyd_or_ntpd_specify_multiple_servers' ############################################################################### # BEGIN fix (82 / 150) for 'xccdf_org.ssgproject.content_rule_package_xinetd_removed' ############################################################################### (>&2 echo "Remediating rule 82/150: 'xccdf_org.ssgproject.content_rule_package_xinetd_removed'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then # CAUTION: This remediation script will remove xinetd # from the system, and may remove any packages # that depend on xinetd. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "xinetd" ; then yum remove -y "xinetd" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_package_xinetd_removed' ############################################################################### # BEGIN fix (83 / 150) for 'xccdf_org.ssgproject.content_rule_service_xinetd_disabled' ############################################################################### (>&2 echo "Remediating rule 83/150: 'xccdf_org.ssgproject.content_rule_service_xinetd_disabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" stop 'xinetd.service' "$SYSTEMCTL_EXEC" disable 'xinetd.service' "$SYSTEMCTL_EXEC" mask 'xinetd.service' # Disable socket activation if we have a unit file for it if "$SYSTEMCTL_EXEC" -q list-unit-files xinetd.socket; then "$SYSTEMCTL_EXEC" stop 'xinetd.socket' "$SYSTEMCTL_EXEC" mask 'xinetd.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 'xinetd.service' || true else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_service_xinetd_disabled' ############################################################################### # BEGIN fix (84 / 150) for 'xccdf_org.ssgproject.content_rule_package_ypbind_removed' ############################################################################### (>&2 echo "Remediating rule 84/150: 'xccdf_org.ssgproject.content_rule_package_ypbind_removed'") # CAUTION: This remediation script will remove ypbind # from the system, and may remove any packages # that depend on ypbind. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "ypbind" ; then yum remove -y "ypbind" fi # END fix for 'xccdf_org.ssgproject.content_rule_package_ypbind_removed' ############################################################################### # BEGIN fix (85 / 150) for 'xccdf_org.ssgproject.content_rule_package_rsh-server_removed' ############################################################################### (>&2 echo "Remediating rule 85/150: 'xccdf_org.ssgproject.content_rule_package_rsh-server_removed'") # CAUTION: This remediation script will remove rsh-server # from the system, and may remove any packages # that depend on rsh-server. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "rsh-server" ; then yum remove -y "rsh-server" fi # END fix for 'xccdf_org.ssgproject.content_rule_package_rsh-server_removed' ############################################################################### # BEGIN fix (86 / 150) for 'xccdf_org.ssgproject.content_rule_package_rsh_removed' ############################################################################### (>&2 echo "Remediating rule 86/150: 'xccdf_org.ssgproject.content_rule_package_rsh_removed'") # CAUTION: This remediation script will remove rsh # from the system, and may remove any packages # that depend on rsh. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "rsh" ; then yum remove -y "rsh" fi # END fix for 'xccdf_org.ssgproject.content_rule_package_rsh_removed' ############################################################################### # BEGIN fix (87 / 150) for 'xccdf_org.ssgproject.content_rule_package_talk-server_removed' ############################################################################### (>&2 echo "Remediating rule 87/150: 'xccdf_org.ssgproject.content_rule_package_talk-server_removed'") # CAUTION: This remediation script will remove talk-server # from the system, and may remove any packages # that depend on talk-server. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "talk-server" ; then yum remove -y "talk-server" fi # END fix for 'xccdf_org.ssgproject.content_rule_package_talk-server_removed' ############################################################################### # BEGIN fix (88 / 150) for 'xccdf_org.ssgproject.content_rule_package_talk_removed' ############################################################################### (>&2 echo "Remediating rule 88/150: 'xccdf_org.ssgproject.content_rule_package_talk_removed'") # CAUTION: This remediation script will remove talk # from the system, and may remove any packages # that depend on talk. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "talk" ; then yum remove -y "talk" fi # END fix for 'xccdf_org.ssgproject.content_rule_package_talk_removed' ############################################################################### # BEGIN fix (89 / 150) for 'xccdf_org.ssgproject.content_rule_package_telnet-server_removed' ############################################################################### (>&2 echo "Remediating rule 89/150: '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 yum remove -y "telnet-server" fi # END fix for 'xccdf_org.ssgproject.content_rule_package_telnet-server_removed' ############################################################################### # BEGIN fix (90 / 150) for 'xccdf_org.ssgproject.content_rule_package_telnet_removed' ############################################################################### (>&2 echo "Remediating rule 90/150: '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 yum remove -y "telnet" fi # END fix for 'xccdf_org.ssgproject.content_rule_package_telnet_removed' ############################################################################### # BEGIN fix (91 / 150) for 'xccdf_org.ssgproject.content_rule_service_telnet_disabled' ############################################################################### (>&2 echo "Remediating rule 91/150: 'xccdf_org.ssgproject.content_rule_service_telnet_disabled'") # Remediation is applicable only in certain platforms if ( rpm --quiet -q telnet-server && rpm --quiet -q kernel ); then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" stop 'telnet.service' "$SYSTEMCTL_EXEC" disable 'telnet.service' "$SYSTEMCTL_EXEC" mask 'telnet.service' # Disable socket activation if we have a unit file for it if "$SYSTEMCTL_EXEC" -q list-unit-files telnet.socket; then "$SYSTEMCTL_EXEC" stop 'telnet.socket' "$SYSTEMCTL_EXEC" mask 'telnet.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 'telnet.service' || true else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_service_telnet_disabled' ############################################################################### # BEGIN fix (92 / 150) for 'xccdf_org.ssgproject.content_rule_package_squid_removed' ############################################################################### (>&2 echo "Remediating rule 92/150: 'xccdf_org.ssgproject.content_rule_package_squid_removed'") # CAUTION: This remediation script will remove squid # from the system, and may remove any packages # that depend on squid. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "squid" ; then yum remove -y "squid" fi # END fix for 'xccdf_org.ssgproject.content_rule_package_squid_removed' ############################################################################### # BEGIN fix (93 / 150) for 'xccdf_org.ssgproject.content_rule_service_squid_disabled' ############################################################################### (>&2 echo "Remediating rule 93/150: 'xccdf_org.ssgproject.content_rule_service_squid_disabled'") # Remediation is applicable only in certain platforms if ( rpm --quiet -q squid && rpm --quiet -q kernel ); then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" stop 'squid.service' "$SYSTEMCTL_EXEC" disable 'squid.service' "$SYSTEMCTL_EXEC" mask 'squid.service' # Disable socket activation if we have a unit file for it if "$SYSTEMCTL_EXEC" -q list-unit-files squid.socket; then "$SYSTEMCTL_EXEC" stop 'squid.socket' "$SYSTEMCTL_EXEC" mask 'squid.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 'squid.service' || true else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_service_squid_disabled' ############################################################################### # BEGIN fix (94 / 150) for 'xccdf_org.ssgproject.content_rule_package_quagga_removed' ############################################################################### (>&2 echo "Remediating rule 94/150: 'xccdf_org.ssgproject.content_rule_package_quagga_removed'") # CAUTION: This remediation script will remove quagga # from the system, and may remove any packages # that depend on quagga. Execute this # remediation AFTER testing on a non-production # system! if rpm -q --quiet "quagga" ; then yum remove -y "quagga" fi # END fix for 'xccdf_org.ssgproject.content_rule_package_quagga_removed' ############################################################################### # BEGIN fix (95 / 150) for 'xccdf_org.ssgproject.content_rule_service_zebra_disabled' ############################################################################### (>&2 echo "Remediating rule 95/150: 'xccdf_org.ssgproject.content_rule_service_zebra_disabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" stop 'zebra.service' "$SYSTEMCTL_EXEC" disable 'zebra.service' "$SYSTEMCTL_EXEC" mask 'zebra.service' # Disable socket activation if we have a unit file for it if "$SYSTEMCTL_EXEC" -q list-unit-files zebra.socket; then "$SYSTEMCTL_EXEC" stop 'zebra.socket' "$SYSTEMCTL_EXEC" mask 'zebra.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 'zebra.service' || true else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_service_zebra_disabled' ############################################################################### # BEGIN fix (96 / 150) for 'xccdf_org.ssgproject.content_rule_service_snmpd_disabled' ############################################################################### (>&2 echo "Remediating rule 96/150: 'xccdf_org.ssgproject.content_rule_service_snmpd_disabled'") # Remediation is applicable only in certain platforms if ( rpm --quiet -q net-snmp && rpm --quiet -q kernel ); then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" stop 'snmpd.service' "$SYSTEMCTL_EXEC" disable 'snmpd.service' "$SYSTEMCTL_EXEC" mask 'snmpd.service' # Disable socket activation if we have a unit file for it if "$SYSTEMCTL_EXEC" -q list-unit-files snmpd.socket; then "$SYSTEMCTL_EXEC" stop 'snmpd.socket' "$SYSTEMCTL_EXEC" mask 'snmpd.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 'snmpd.service' || true else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_service_snmpd_disabled' ############################################################################### # BEGIN fix (97 / 150) for 'xccdf_org.ssgproject.content_rule_snmpd_use_newer_protocol' ############################################################################### (>&2 echo "Remediating rule 97/150: 'xccdf_org.ssgproject.content_rule_snmpd_use_newer_protocol'") (>&2 echo "FIX FOR THIS RULE 'xccdf_org.ssgproject.content_rule_snmpd_use_newer_protocol' IS MISSING!") # END fix for 'xccdf_org.ssgproject.content_rule_snmpd_use_newer_protocol' ############################################################################### # BEGIN fix (98 / 150) for 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_private_key' ############################################################################### (>&2 echo "Remediating rule 98/150: 'xccdf_org.ssgproject.content_rule_file_permissions_sshd_private_key'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; 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 (99 / 150) for 'xccdf_org.ssgproject.content_rule_disable_host_auth' ############################################################################### (>&2 echo "Remediating rule 99/150: 'xccdf_org.ssgproject.content_rule_disable_host_auth'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*HostbasedAuthentication\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "HostbasedAuthentication no" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.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 (100 / 150) for 'xccdf_org.ssgproject.content_rule_firewalld_sshd_port_enabled' ############################################################################### (>&2 echo "Remediating rule 100/150: 'xccdf_org.ssgproject.content_rule_firewalld_sshd_port_enabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if ! rpm -q --quiet "firewalld" ; then yum install -y "firewalld" fi if ! rpm -q --quiet "NetworkManager" ; then yum install -y "NetworkManager" fi firewalld_sshd_zone='public' if test "$(stat -c %d:%i /)" != "$(stat -c %d:%i /proc/1/root/.)"; then # TODO: NM (nmcli) now has --offline mode support, and it could operate without NM service. # See: https://gitlab.freedesktop.org/NetworkManager/NetworkManager/-/merge_requests/1183 # The feature is not quite straighforward (and probably incomplete), though. echo "Not applicable in offline mode. Remediation aborted!" else if systemctl is-active NetworkManager && systemctl is-active firewalld; then # First make sure the SSH service is enabled in run-time for the proper zone. # This is to avoid connection issues when new interfaces are addeded to this zone. firewall-cmd --zone="$firewalld_sshd_zone" --add-service=ssh # This will collect all NetworkManager connections names readarray -t nm_connections < <(nmcli -g UUID,TYPE con | grep -v loopback | awk -F ':' '{ print $1 }') # If the connection is not yet assigned to a firewalld zone, assign it to the proper zone. # This will not change connections which are already assigned to any firewalld zone. for connection in "${nm_connections[@]}"; do current_zone=$(nmcli -f connection.zone connection show "$connection" | awk '{ print $2}') if [ $current_zone = "--" ]; then nmcli connection modify "$connection" connection.zone $firewalld_sshd_zone fi done systemctl restart NetworkManager # Active zones are zones with at least one interface assigned to it. # It is possible that traffic is coming by any active interface and consequently any # active zone. So, this make sure all active zones are permanently allowing SSH service. readarray -t firewalld_active_zones < <(firewall-cmd --get-active-zones | grep -v "^ " | cut -d " " -f 1) for zone in "${firewalld_active_zones[@]}"; do firewall-cmd --permanent --zone="$zone" --add-service=ssh done firewall-cmd --reload else echo "The firewalld or NetworkManager service is not active. Remediation aborted!" fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_firewalld_sshd_port_enabled' ############################################################################### # BEGIN fix (101 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_allow_only_protocol2' ############################################################################### (>&2 echo "Remediating rule 101/150: 'xccdf_org.ssgproject.content_rule_sshd_allow_only_protocol2'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*Protocol\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "Protocol 2" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_sshd_allow_only_protocol2' ############################################################################### # BEGIN fix (102 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_disable_empty_passwords' ############################################################################### (>&2 echo "Remediating rule 102/150: 'xccdf_org.ssgproject.content_rule_sshd_disable_empty_passwords'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*PermitEmptyPasswords\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "PermitEmptyPasswords no" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.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 (103 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_disable_gssapi_auth' ############################################################################### (>&2 echo "Remediating rule 103/150: 'xccdf_org.ssgproject.content_rule_sshd_disable_gssapi_auth'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*GSSAPIAuthentication\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "GSSAPIAuthentication no" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_sshd_disable_gssapi_auth' ############################################################################### # BEGIN fix (104 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_disable_kerb_auth' ############################################################################### (>&2 echo "Remediating rule 104/150: 'xccdf_org.ssgproject.content_rule_sshd_disable_kerb_auth'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*KerberosAuthentication\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "KerberosAuthentication no" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_sshd_disable_kerb_auth' ############################################################################### # BEGIN fix (105 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_disable_rhosts' ############################################################################### (>&2 echo "Remediating rule 105/150: 'xccdf_org.ssgproject.content_rule_sshd_disable_rhosts'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*IgnoreRhosts\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "IgnoreRhosts yes" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.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 (106 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_disable_root_login' ############################################################################### (>&2 echo "Remediating rule 106/150: 'xccdf_org.ssgproject.content_rule_sshd_disable_root_login'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*PermitRootLogin\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "PermitRootLogin no" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.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 (107 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_disable_user_known_hosts' ############################################################################### (>&2 echo "Remediating rule 107/150: 'xccdf_org.ssgproject.content_rule_sshd_disable_user_known_hosts'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*IgnoreUserKnownHosts\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "IgnoreUserKnownHosts yes" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_sshd_disable_user_known_hosts' ############################################################################### # BEGIN fix (108 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_disable_x11_forwarding' ############################################################################### (>&2 echo "Remediating rule 108/150: 'xccdf_org.ssgproject.content_rule_sshd_disable_x11_forwarding'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*X11Forwarding\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "X11Forwarding no" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.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 (109 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_do_not_permit_user_env' ############################################################################### (>&2 echo "Remediating rule 109/150: 'xccdf_org.ssgproject.content_rule_sshd_do_not_permit_user_env'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*PermitUserEnvironment\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "PermitUserEnvironment no" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.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 (110 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_enable_strictmodes' ############################################################################### (>&2 echo "Remediating rule 110/150: 'xccdf_org.ssgproject.content_rule_sshd_enable_strictmodes'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*StrictModes\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "StrictModes yes" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_sshd_enable_strictmodes' ############################################################################### # BEGIN fix (111 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_enable_warning_banner' ############################################################################### (>&2 echo "Remediating rule 111/150: 'xccdf_org.ssgproject.content_rule_sshd_enable_warning_banner'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*Banner\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "Banner /etc/issue" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_sshd_enable_warning_banner' ############################################################################### # BEGIN fix (112 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_print_last_log' ############################################################################### (>&2 echo "Remediating rule 112/150: 'xccdf_org.ssgproject.content_rule_sshd_print_last_log'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*PrintLastLog\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "PrintLastLog yes" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_sshd_print_last_log' ############################################################################### # BEGIN fix (113 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_set_loglevel_info' ############################################################################### (>&2 echo "Remediating rule 113/150: 'xccdf_org.ssgproject.content_rule_sshd_set_loglevel_info'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*LogLevel\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "LogLevel INFO" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.bak" else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_sshd_set_loglevel_info' ############################################################################### # BEGIN fix (114 / 150) for 'xccdf_org.ssgproject.content_rule_sshd_set_max_auth_tries' ############################################################################### (>&2 echo "Remediating rule 114/150: 'xccdf_org.ssgproject.content_rule_sshd_set_max_auth_tries'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then sshd_max_auth_tries_value='5' if [ -e "/etc/ssh/sshd_config" ] ; then LC_ALL=C sed -i "/^\s*MaxAuthTries\s\+/Id" "/etc/ssh/sshd_config" else touch "/etc/ssh/sshd_config" fi # make sure file has newline at the end sed -i -e '$a\' "/etc/ssh/sshd_config" cp "/etc/ssh/sshd_config" "/etc/ssh/sshd_config.bak" # Insert at the beginning of the file printf '%s\n' "MaxAuthTries $sshd_max_auth_tries_value" > "/etc/ssh/sshd_config" cat "/etc/ssh/sshd_config.bak" >> "/etc/ssh/sshd_config" # Clean up after ourselves. rm "/etc/ssh/sshd_config.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 (115 / 150) for 'xccdf_org.ssgproject.content_rule_package_usbguard_installed' ############################################################################### (>&2 echo "Remediating rule 115/150: 'xccdf_org.ssgproject.content_rule_package_usbguard_installed'") # Remediation is applicable only in certain platforms if ( ! grep -q s390x /proc/sys/kernel/osrelease && rpm --quiet -q kernel ); then if ! rpm -q --quiet "usbguard" ; then yum install -y "usbguard" fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_package_usbguard_installed' ############################################################################### # BEGIN fix (116 / 150) for 'xccdf_org.ssgproject.content_rule_service_usbguard_enabled' ############################################################################### (>&2 echo "Remediating rule 116/150: 'xccdf_org.ssgproject.content_rule_service_usbguard_enabled'") # Remediation is applicable only in certain platforms if ( ! grep -q s390x /proc/sys/kernel/osrelease && rpm --quiet -q kernel ); then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" unmask 'usbguard.service' "$SYSTEMCTL_EXEC" start 'usbguard.service' "$SYSTEMCTL_EXEC" enable 'usbguard.service' else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_service_usbguard_enabled' ############################################################################### # BEGIN fix (117 / 150) for 'xccdf_org.ssgproject.content_rule_usbguard_allow_hid_and_hub' ############################################################################### (>&2 echo "Remediating rule 117/150: 'xccdf_org.ssgproject.content_rule_usbguard_allow_hid_and_hub'") # Remediation is applicable only in certain platforms if ( ! grep -q s390x /proc/sys/kernel/osrelease && rpm --quiet -q kernel ); then echo "allow with-interface match-all { 03:*:* 09:00:* }" >> /etc/usbguard/rules.conf else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_usbguard_allow_hid_and_hub' ############################################################################### # BEGIN fix (118 / 150) for 'xccdf_org.ssgproject.content_rule_service_auditd_enabled' ############################################################################### (>&2 echo "Remediating rule 118/150: 'xccdf_org.ssgproject.content_rule_service_auditd_enabled'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel && { rpm --quiet -q audit; }; then SYSTEMCTL_EXEC='/usr/bin/systemctl' "$SYSTEMCTL_EXEC" unmask 'auditd.service' "$SYSTEMCTL_EXEC" start 'auditd.service' "$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 (119 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_networkconfig_modification' ############################################################################### (>&2 echo "Remediating rule 119/150: '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; 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 0640 "$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 o-rwx ${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 o-rwx ${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 0640 "$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 0640 "$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 0640 "$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 0640 "$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 (120 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_session_events' ############################################################################### (>&2 echo "Remediating rule 120/150: 'xccdf_org.ssgproject.content_rule_audit_rules_session_events'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; 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 0640 "$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 # 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 0640 "$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 # 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 0640 "$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' ############################################################################### # BEGIN fix (121 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_sysadmin_actions' ############################################################################### (>&2 echo "Remediating rule 121/150: '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; 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 0640 "$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 0640 "$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 (122 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification' ############################################################################### (>&2 echo "Remediating rule 122/150: 'xccdf_org.ssgproject.content_rule_audit_rules_usergroup_modification'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; 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 0640 "$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 # 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" >> "$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/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.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 0640 "$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" >> "$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/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 0640 "$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 # 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 0640 "$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 # 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 0640 "$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' ############################################################################### # BEGIN fix (123 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chmod' ############################################################################### (>&2 echo "Remediating rule 123/150: '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 && { ! ( grep -q aarch64 /proc/sys/kernel/osrelease ); }; 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 0640 "$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 o-rwx ${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 o-rwx ${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 (124 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_dac_modification_chown' ############################################################################### (>&2 echo "Remediating rule 124/150: '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 && { ! ( grep -q aarch64 /proc/sys/kernel/osrelease ); }; 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 0640 "$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 o-rwx ${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 o-rwx ${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 (125 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_chcon' ############################################################################### (>&2 echo "Remediating rule 125/150: 'xccdf_org.ssgproject.content_rule_audit_rules_execution_chcon'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then ACTION_ARCH_FILTERS="-a always,exit" OTHER_FILTERS="-F path=/usr/bin/chcon" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="" KEY="privileged" 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 0640 "$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 o-rwx ${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 o-rwx ${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 else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_chcon' ############################################################################### # BEGIN fix (126 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_restorecon' ############################################################################### (>&2 echo "Remediating rule 126/150: 'xccdf_org.ssgproject.content_rule_audit_rules_execution_restorecon'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then ACTION_ARCH_FILTERS="-a always,exit" OTHER_FILTERS="-F path=/usr/sbin/restorecon" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="" KEY="privileged" 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 0640 "$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 o-rwx ${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 o-rwx ${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 else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_restorecon' ############################################################################### # BEGIN fix (127 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_semanage' ############################################################################### (>&2 echo "Remediating rule 127/150: 'xccdf_org.ssgproject.content_rule_audit_rules_execution_semanage'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then ACTION_ARCH_FILTERS="-a always,exit" OTHER_FILTERS="-F path=/usr/sbin/semanage" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="" KEY="privileged" 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 0640 "$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 o-rwx ${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 o-rwx ${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 else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_semanage' ############################################################################### # BEGIN fix (128 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_setfiles' ############################################################################### (>&2 echo "Remediating rule 128/150: 'xccdf_org.ssgproject.content_rule_audit_rules_execution_setfiles'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then ACTION_ARCH_FILTERS="-a always,exit" OTHER_FILTERS="-F path=/usr/sbin/setfiles" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="" KEY="privileged" 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 0640 "$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 o-rwx ${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 o-rwx ${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 else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_setfiles' ############################################################################### # BEGIN fix (129 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_setsebool' ############################################################################### (>&2 echo "Remediating rule 129/150: 'xccdf_org.ssgproject.content_rule_audit_rules_execution_setsebool'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then ACTION_ARCH_FILTERS="-a always,exit" OTHER_FILTERS="-F path=/usr/sbin/setsebool" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="" KEY="privileged" 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 0640 "$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 o-rwx ${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 o-rwx ${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 else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_setsebool' ############################################################################### # BEGIN fix (130 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_seunshare' ############################################################################### (>&2 echo "Remediating rule 130/150: 'xccdf_org.ssgproject.content_rule_audit_rules_execution_seunshare'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then ACTION_ARCH_FILTERS="-a always,exit" OTHER_FILTERS="-F path=/usr/sbin/seunshare" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="" KEY="privileged" 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 0640 "$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 o-rwx ${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 o-rwx ${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 else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_execution_seunshare' ############################################################################### # BEGIN fix (131 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification' ############################################################################### (>&2 echo "Remediating rule 131/150: 'xccdf_org.ssgproject.content_rule_audit_rules_unsuccessful_file_modification'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel && { ! ( grep -q aarch64 /proc/sys/kernel/osrelease ); }; then # 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 # First fix the -EACCES requirement ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="-F exit=-EACCES" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="creat open openat open_by_handle_at truncate ftruncate" KEY="access" SYSCALL_GROUPING="creat open openat open_by_handle_at truncate ftruncate" # 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 0640 "$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 o-rwx ${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 o-rwx ${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 # Then fix the -EPERM requirement # No need to change content of $GROUP variable - it's the same as for -EACCES case above ACTION_ARCH_FILTERS="-a always,exit -F arch=$ARCH" OTHER_FILTERS="-F exit=-EPERM" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="creat open openat open_by_handle_at truncate ftruncate" KEY="access" SYSCALL_GROUPING="creat open openat open_by_handle_at truncate ftruncate" # 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 0640 "$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 o-rwx ${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 o-rwx ${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_unsuccessful_file_modification' ############################################################################### # BEGIN fix (132 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_kernel_module_loading' ############################################################################### (>&2 echo "Remediating rule 132/150: 'xccdf_org.ssgproject.content_rule_audit_rules_kernel_module_loading'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then # First perform the remediation of the syscall rule # Retrieve hardware architecture of the underlying system # Note: 32-bit and 64-bit kernel syscall numbers not always line up => # it's required on a 64-bit system to check also for the presence # of 32-bit's equivalent of the corresponding rule. # (See `man 7 audit.rules` for details ) [ "$(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="init_module finit_module delete_module" KEY="modules" SYSCALL_GROUPING="init_module finit_module delete_module" # 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 0640 "$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 o-rwx ${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 o-rwx ${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_kernel_module_loading' ############################################################################### # BEGIN fix (133 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_login_events' ############################################################################### (>&2 echo "Remediating rule 133/150: 'xccdf_org.ssgproject.content_rule_audit_rules_login_events'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; 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/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 0640 "$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 # 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 0640 "$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 # 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 0640 "$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' ############################################################################### # BEGIN fix (134 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_faillock' ############################################################################### (>&2 echo "Remediating rule 134/150: '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; 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 0640 "$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 (135 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_lastlog' ############################################################################### (>&2 echo "Remediating rule 135/150: '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; 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 0640 "$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 (136 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_login_events_tallylog' ############################################################################### (>&2 echo "Remediating rule 136/150: '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; 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 0640 "$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 (137 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_privileged_commands' ############################################################################### (>&2 echo "Remediating rule 137/150: 'xccdf_org.ssgproject.content_rule_audit_rules_privileged_commands'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then ACTION_ARCH_FILTERS="-a always,exit" AUID_FILTERS="-F auid>=1000 -F auid!=unset" SYSCALL="" KEY="privileged" SYSCALL_GROUPING="" FILTER_NODEV=$(awk '/nodev/ { print $2 }' /proc/filesystems | paste -sd,) PARTITIONS=$(findmnt -n -l -k -it $FILTER_NODEV | grep -Pv "noexec|nosuid|/proc($|/.*$)" | awk '{ print $1 }') for PARTITION in $PARTITIONS; do PRIV_CMDS=$(find "${PARTITION}" -xdev -perm /6000 -type f 2>/dev/null) for PRIV_CMD in $PRIV_CMDS; do OTHER_FILTERS="-F path=$PRIV_CMD -F perm=x" # 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 0640 "$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 o-rwx ${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 o-rwx ${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 done else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_audit_rules_privileged_commands' ############################################################################### # BEGIN fix (138 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_time_adjtimex' ############################################################################### (>&2 echo "Remediating rule 138/150: '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; 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 0640 "$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 o-rwx ${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 o-rwx ${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 (139 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_time_clock_settime' ############################################################################### (>&2 echo "Remediating rule 139/150: '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; 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 0640 "$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 o-rwx ${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 o-rwx ${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 (140 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_time_settimeofday' ############################################################################### (>&2 echo "Remediating rule 140/150: '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; 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 0640 "$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 o-rwx ${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 o-rwx ${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 (141 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_time_stime' ############################################################################### (>&2 echo "Remediating rule 141/150: '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 && { ( ! ( grep -q aarch64 /proc/sys/kernel/osrelease ) && ! ( grep -q s390x /proc/sys/kernel/osrelease ) ); }; 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 0640 "$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 o-rwx ${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 o-rwx ${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 (142 / 150) for 'xccdf_org.ssgproject.content_rule_audit_rules_time_watch_localtime' ############################################################################### (>&2 echo "Remediating rule 142/150: '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; 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 0640 "$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 (143 / 150) for 'xccdf_org.ssgproject.content_rule_auditd_data_retention_flush' ############################################################################### (>&2 echo "Remediating rule 143/150: 'xccdf_org.ssgproject.content_rule_auditd_data_retention_flush'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then var_auditd_flush='incremental_async' AUDITCONFIG=/etc/audit/auditd.conf # if flush is present, flush param edited to var_auditd_flush # else flush param is defined by var_auditd_flush # # the freq param is only used for values 'incremental' and 'incremental_async' and will be # commented out if flush != incremental or flush != incremental_async # # if flush == incremental or flush == incremental_async && freq param is not defined, it # will be defined as the package-default value of 20 grep -q ^flush $AUDITCONFIG && \ sed -i 's/^flush.*/flush = '"$var_auditd_flush"'/g' $AUDITCONFIG if ! [ $? -eq 0 ]; then echo "flush = $var_auditd_flush" >> $AUDITCONFIG fi if ! [ "$var_auditd_flush" == "incremental" ] && ! [ "$var_auditd_flush" == "incremental_async" ]; then sed -i 's/^freq/##freq/g' $AUDITCONFIG elif [ "$var_auditd_flush" == "incremental" ] || [ "$var_auditd_flush" == "incremental_async" ]; then grep -q freq $AUDITCONFIG && \ sed -i 's/^#\+freq/freq/g' $AUDITCONFIG if ! [ $? -eq 0 ]; then echo "freq = 20" >> $AUDITCONFIG fi fi else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_auditd_data_retention_flush' ############################################################################### # BEGIN fix (144 / 150) for 'xccdf_org.ssgproject.content_rule_auditd_freq' ############################################################################### (>&2 echo "Remediating rule 144/150: 'xccdf_org.ssgproject.content_rule_auditd_freq'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then if [ -e "/etc/audit/auditd.conf" ] ; then LC_ALL=C sed -i "/^\s*freq\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' "freq = 50" >> "/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_freq' ############################################################################### # BEGIN fix (145 / 150) for 'xccdf_org.ssgproject.content_rule_auditd_local_events' ############################################################################### (>&2 echo "Remediating rule 145/150: 'xccdf_org.ssgproject.content_rule_auditd_local_events'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then if [ -e "/etc/audit/auditd.conf" ] ; then LC_ALL=C sed -i "/^\s*local_events\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' "local_events = yes" >> "/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_local_events' ############################################################################### # BEGIN fix (146 / 150) for 'xccdf_org.ssgproject.content_rule_auditd_log_format' ############################################################################### (>&2 echo "Remediating rule 146/150: 'xccdf_org.ssgproject.content_rule_auditd_log_format'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then if [ -e "/etc/audit/auditd.conf" ] ; then LC_ALL=C sed -i "/^\s*log_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' "log_format = ENRICHED" >> "/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_log_format' ############################################################################### # BEGIN fix (147 / 150) for 'xccdf_org.ssgproject.content_rule_auditd_name_format' ############################################################################### (>&2 echo "Remediating rule 147/150: 'xccdf_org.ssgproject.content_rule_auditd_name_format'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then var_auditd_name_format='hostname' 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' ############################################################################### # BEGIN fix (148 / 150) for 'xccdf_org.ssgproject.content_rule_auditd_write_logs' ############################################################################### (>&2 echo "Remediating rule 148/150: 'xccdf_org.ssgproject.content_rule_auditd_write_logs'") # Remediation is applicable only in certain platforms if rpm --quiet -q audit && rpm --quiet -q kernel; then if [ -e "/etc/audit/auditd.conf" ] ; then LC_ALL=C sed -i "/^\s*write_logs\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' "write_logs = yes" >> "/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_write_logs' ############################################################################### # BEGIN fix (149 / 150) for 'xccdf_org.ssgproject.content_rule_audit_access_failed' ############################################################################### (>&2 echo "Remediating rule 149/150: 'xccdf_org.ssgproject.content_rule_audit_access_failed'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules ## Unsuccessful file access (any other opens) This has to go last. -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EACCES -F auid>=1000 -F auid!=unset -F key=unsuccessful-access -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F exit=-EPERM -F auid>=1000 -F auid!=unset -F key=unsuccessful-access EOF chmod o-rwx /etc/audit/rules.d/30-ospp-v42-3-access-failed.rules augenrules --load else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_audit_access_failed' ############################################################################### # BEGIN fix (150 / 150) for 'xccdf_org.ssgproject.content_rule_audit_access_success' ############################################################################### (>&2 echo "Remediating rule 150/150: 'xccdf_org.ssgproject.content_rule_audit_access_success'") # Remediation is applicable only in certain platforms if rpm --quiet -q kernel; then cat << 'EOF' > /etc/audit/rules.d/30-ospp-v42-3-access-success.rules ## Successful file access (any other opens) This has to go last. ## These next two are likely to result in a whole lot of events -a always,exit -F arch=b32 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access -a always,exit -F arch=b64 -S open,openat,openat2,open_by_handle_at -F success=1 -F auid>=1000 -F auid!=unset -F key=successful-access EOF chmod o-rwx /etc/audit/rules.d/30-ospp-v42-3-access-success.rules augenrules --load else >&2 echo 'Remediation is not applicable, nothing was done' fi # END fix for 'xccdf_org.ssgproject.content_rule_audit_access_success'